# HG changeset patch # User nenolod # Date 1165479775 28800 # Node ID 598564ddc4e9cda4718a8598fbebe71141146896 # Parent 230e36118438ce572914f4ad974e70e9394c7334 [svn] - no, this is not going to work diff -r 230e36118438 -r 598564ddc4e9 ChangeLog --- a/ChangeLog Thu Dec 07 00:12:31 2006 -0800 +++ b/ChangeLog Thu Dec 07 00:22:55 2006 -0800 @@ -1,3 +1,13 @@ +2006-12-07 08:12:31 +0000 William Pitcock + revision [3131] + - add work in progress TagLib::TagVFSFile class. + + trunk/taglib-vfs/Makefile | 32 ++ + trunk/taglib-vfs/tvfsfile.cxx | 501 ++++++++++++++++++++++++++++++++++++++++++ + trunk/taglib-vfs/tvfsfile.h | 247 ++++++++++++++++++++ + 3 files changed, 780 insertions(+) + + 2006-12-06 11:41:04 +0000 William Pitcock revision [3129] - playlist_scan_thread_is_going should not be TRUE if the playlist is diff -r 230e36118438 -r 598564ddc4e9 taglib-vfs/Makefile --- a/taglib-vfs/Makefile Thu Dec 07 00:12:31 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -include ../mk/rules.mk -include ../mk/init.mk - -beepincludedir = $(includedir)/audacious - -OBJECTIVE_LIBS = libtagvfs.so - -LDFLAGS += $(AUDLDFLAGS) - -CXXFLAGS += $(LIBLDFLAGS) \ - $(GTK_CFLAGS) \ - $(LIBGLADE_CFLAGS) \ - $(BEEP_DEFINES) \ - $(ARCH_DEFINES) \ - -D_AUDACIOUS_CORE \ - -I.. \ - -I../intl \ - -I/usr/include/taglib - -HEADERS = \ - tvfsfile.h - -SOURCES = tvfsfile.cxx - -OBJECTS = ${SOURCES:.cxx=.o} - -beepinclude_HEADERS = plugin.h output.h input.h - -desktop_DATA = audacious.desktop -desktopdir = $(datadir)/applications - -include ../mk/objective.mk diff -r 230e36118438 -r 598564ddc4e9 taglib-vfs/tvfsfile.cxx --- a/taglib-vfs/tvfsfile.cxx Thu Dec 07 00:12:31 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,501 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002, 2003 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * - * USA * - ***************************************************************************/ - -#include "tvfsfile.h" -#include "tstring.h" - -#include -#include -#include - -#include "libaudacious/vfs.h" - -using namespace TagLib; - -class TagVFSFile::TagVFSFilePrivate -{ -public: - TagVFSFilePrivate(const char *fileName) : - file(0), - name(fileName), - readOnly(true), - valid(true), - size(0) - {} - - ~TagVFSFilePrivate() - { - free((void *)name); - } - - VFSFile *file; - const char *name; - bool readOnly; - bool valid; - ulong size; - static const uint bufferSize = 1024; -}; - -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - -TagVFSFile::TagVFSFile(const char *file) -{ - d = new TagVFSFilePrivate(::strdup(file)); - - d->readOnly = !isWritable(file); - d->file = vfs_fopen(file, d->readOnly ? "r" : "r+"); - -#if 0 - if(!d->file) - puts("Could not open file " + String(file)); -#endif -} - -TagVFSFile::~TagVFSFile() -{ - if(d->file) - vfs_fclose(d->file); - delete d; -} - -const char *TagVFSFile::name() const -{ - return d->name; -} - -ByteVector TagVFSFile::readBlock(ulong length) -{ - if(!d->file) { - puts("File::readBlock() -- Invalid File"); - return ByteVector::null; - } - - if(length > TagVFSFilePrivate::bufferSize && - length > ulong(TagVFSFile::length())) - { - length = TagVFSFile::length(); - } - - ByteVector v(static_cast(length)); - const int count = vfs_fread(v.data(), sizeof(char), length, d->file); - v.resize(count); - return v; -} - -void TagVFSFile::writeBlock(const ByteVector &data) -{ - if(!d->file) - return; - - if(d->readOnly) { - puts("File::writeBlock() -- attempted to write to a file that is not writable"); - return; - } - - vfs_fwrite(data.data(), sizeof(char), data.size(), d->file); -} - -long TagVFSFile::find(const ByteVector &pattern, long fromOffset, const ByteVector &before) -{ - if(!d->file || pattern.size() > d->bufferSize) - return -1; - - // The position in the file that the current buffer starts at. - - long bufferOffset = fromOffset; - ByteVector buffer; - - // These variables are used to keep track of a partial match that happens at - // the end of a buffer. - - int previousPartialMatch = -1; - int beforePreviousPartialMatch = -1; - - // Save the location of the current read pointer. We will restore the - // position using seek() before all returns. - - long originalPosition = tell(); - - // Start the search at the offset. - - seek(fromOffset); - - // This loop is the crux of the find method. There are three cases that we - // want to account for: - // - // (1) The previously searched buffer contained a partial match of the search - // pattern and we want to see if the next one starts with the remainder of - // that pattern. - // - // (2) The search pattern is wholly contained within the current buffer. - // - // (3) The current buffer ends with a partial match of the pattern. We will - // note this for use in the next itteration, where we will check for the rest - // of the pattern. - // - // All three of these are done in two steps. First we check for the pattern - // and do things appropriately if a match (or partial match) is found. We - // then check for "before". The order is important because it gives priority - // to "real" matches. - - for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) { - - // (1) previous partial match - - if(previousPartialMatch >= 0 && int(d->bufferSize) > previousPartialMatch) { - const int patternOffset = (d->bufferSize - previousPartialMatch); - if(buffer.containsAt(pattern, 0, patternOffset)) { - seek(originalPosition); - return bufferOffset - d->bufferSize + previousPartialMatch; - } - } - - if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(d->bufferSize) > beforePreviousPartialMatch) { - const int beforeOffset = (d->bufferSize - beforePreviousPartialMatch); - if(buffer.containsAt(before, 0, beforeOffset)) { - seek(originalPosition); - return -1; - } - } - - // (2) pattern contained in current buffer - - long location = buffer.find(pattern); - if(location >= 0) { - seek(originalPosition); - return bufferOffset + location; - } - - if(!before.isNull() && buffer.find(before) >= 0) { - seek(originalPosition); - return -1; - } - - // (3) partial match - - previousPartialMatch = buffer.endsWithPartialMatch(pattern); - - if(!before.isNull()) - beforePreviousPartialMatch = buffer.endsWithPartialMatch(before); - - bufferOffset += d->bufferSize; - } - - // Since we hit the end of the file, reset the status before continuing. - - clear(); - - seek(originalPosition); - - return -1; -} - - -long TagVFSFile::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before) -{ - if(!d->file || pattern.size() > d->bufferSize) - return -1; - - // The position in the file that the current buffer starts at. - - ByteVector buffer; - - // These variables are used to keep track of a partial match that happens at - // the end of a buffer. - - /* - int previousPartialMatch = -1; - int beforePreviousPartialMatch = -1; - */ - - // Save the location of the current read pointer. We will restore the - // position using seek() before all returns. - - long originalPosition = tell(); - - // Start the search at the offset. - - long bufferOffset; - if(fromOffset == 0) { - seek(-1 * int(d->bufferSize), End); - bufferOffset = tell(); - } - else { - seek(fromOffset + -1 * int(d->bufferSize), Beginning); - bufferOffset = tell(); - } - - // See the notes in find() for an explanation of this algorithm. - - for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) { - - // TODO: (1) previous partial match - - // (2) pattern contained in current buffer - - long location = buffer.rfind(pattern); - if(location >= 0) { - seek(originalPosition); - return bufferOffset + location; - } - - if(!before.isNull() && buffer.find(before) >= 0) { - seek(originalPosition); - return -1; - } - - // TODO: (3) partial match - - bufferOffset -= d->bufferSize; - seek(bufferOffset); - } - - // Since we hit the end of the file, reset the status before continuing. - - clear(); - - seek(originalPosition); - - return -1; -} - -void TagVFSFile::insert(const ByteVector &data, ulong start, ulong replace) -{ - if(!d->file) - return; - - if(data.size() == replace) { - seek(start); - writeBlock(data); - return; - } - else if(data.size() < replace) { - seek(start); - writeBlock(data); - removeBlock(start + data.size(), replace - data.size()); - return; - } - - // Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore - // and avoid TagLib's high level API for rendering just copying parts of - // the file that don't contain tag data. - // - // Now I'll explain the steps in this ugliness: - - // First, make sure that we're working with a buffer that is longer than - // the *differnce* in the tag sizes. We want to avoid overwriting parts - // that aren't yet in memory, so this is necessary. - - ulong bufferLength = bufferSize(); - while(data.size() - replace > bufferLength) - bufferLength += bufferSize(); - - // Set where to start the reading and writing. - - long readPosition = start + replace; - long writePosition = start; - - ByteVector buffer; - ByteVector aboutToOverwrite(static_cast(bufferLength)); - - // This is basically a special case of the loop below. Here we're just - // doing the same steps as below, but since we aren't using the same buffer - // size -- instead we're using the tag size -- this has to be handled as a - // special case. We're also using File::writeBlock() just for the tag. - // That's a bit slower than using char *'s so, we're only doing it here. - - seek(readPosition); - int bytesRead = vfs_fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file); - readPosition += bufferLength; - - seek(writePosition); - writeBlock(data); - writePosition += data.size(); - - buffer = aboutToOverwrite; - - // Ok, here's the main loop. We want to loop until the read fails, which - // means that we hit the end of the file. - - while(bytesRead != 0) { - - // Seek to the current read position and read the data that we're about - // to overwrite. Appropriately increment the readPosition. - - seek(readPosition); - bytesRead = vfs_fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file); - aboutToOverwrite.resize(bytesRead); - readPosition += bufferLength; - - // Check to see if we just read the last block. We need to call clear() - // if we did so that the last write succeeds. - - if(ulong(bytesRead) < bufferLength) - clear(); - - // Seek to the write position and write our buffer. Increment the - // writePosition. - - seek(writePosition); - vfs_fwrite(buffer.data(), sizeof(char), bufferLength, d->file); - writePosition += bufferLength; - - // Make the current buffer the data that we read in the beginning. - - buffer = aboutToOverwrite; - - // Again, we need this for the last write. We don't want to write garbage - // at the end of our file, so we need to set the buffer size to the amount - // that we actually read. - - bufferLength = bytesRead; - } -} - -void TagVFSFile::removeBlock(ulong start, ulong length) -{ - if(!d->file) - return; - - ulong bufferLength = bufferSize(); - - long readPosition = start + length; - long writePosition = start; - - ByteVector buffer(static_cast(bufferLength)); - - ulong bytesRead = true; - - while(bytesRead != 0) { - seek(readPosition); - bytesRead = vfs_fread(buffer.data(), sizeof(char), bufferLength, d->file); - buffer.resize(bytesRead); - readPosition += bytesRead; - - // Check to see if we just read the last block. We need to call clear() - // if we did so that the last write succeeds. - - if(bytesRead < bufferLength) - clear(); - - seek(writePosition); - vfs_fwrite(buffer.data(), sizeof(char), bytesRead, d->file); - writePosition += bytesRead; - } - truncate(writePosition); -} - -bool TagVFSFile::readOnly() const -{ - return d->readOnly; -} - -bool TagVFSFile::isReadable(const char *file) -{ - return access(file, R_OK) == 0; -} - -bool TagVFSFile::isOpen() const -{ - return d->file; -} - -bool TagVFSFile::isValid() const -{ - return d->file && d->valid; -} - -void TagVFSFile::seek(long offset, Position p) -{ - if(!d->file) { - puts("File::seek() -- trying to seek in a file that isn't opened."); - return; - } - - switch(p) { - case Beginning: - vfs_fseek(d->file, offset, SEEK_SET); - break; - case Current: - vfs_fseek(d->file, offset, SEEK_CUR); - break; - case End: - vfs_fseek(d->file, offset, SEEK_END); - break; - } -} - -void TagVFSFile::clear() -{ - // don't do anything here at the moment -} - -long TagVFSFile::tell() const -{ - return vfs_ftell(d->file); -} - -long TagVFSFile::length() -{ - // Do some caching in case we do multiple calls. - - if(d->size > 0) - return d->size; - - if(!d->file) - return 0; - - long curpos = tell(); - - seek(0, End); - long endpos = tell(); - - seek(curpos, Beginning); - - d->size = endpos; - return endpos; -} - -bool TagVFSFile::isWritable(const char *file) -{ - return access(file, W_OK) == 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -void TagVFSFile::setValid(bool valid) -{ - d->valid = valid; -} - -void TagVFSFile::truncate(long length) -{ - vfs_truncate(d->file, length); -} - -TagLib::uint TagVFSFile::bufferSize() -{ - return TagVFSFilePrivate::bufferSize; -} diff -r 230e36118438 -r 598564ddc4e9 taglib-vfs/tvfsfile.h --- a/taglib-vfs/tvfsfile.h Thu Dec 07 00:12:31 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,247 +0,0 @@ -/*************************************************************************** - copyright : (C) 2002, 2003 by Scott Wheeler - email : wheeler@kde.org - ***************************************************************************/ - -/*************************************************************************** - * This library is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License version * - * 2.1 as published by the Free Software Foundation. * - * * - * This library is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this library; if not, write to the Free Software * - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * - * USA * - ***************************************************************************/ - -#ifndef TAGLIB_VFSFILE_H -#define TAGLIB_VFSFILE_H - -#include "taglib.h" -#include "tbytevector.h" -#include "tfile.h" - -#ifndef _AUDACIOUS_CORE -# include -#else -# include "libaudacious/vfs.h" -#endif - -namespace TagLib { - - class String; - class Tag; - class AudioProperties; - - //! A file class with some useful methods for tag manipulation - - /*! - * This class is a basic file class with some methods that are particularly - * useful for tag editors. It has methods to take advantage of - * ByteVector and a binary search method for finding patterns in a file. - */ - - class TagVFSFile - { - public: - /*! - * Position in the file used for seeking. - */ - enum Position { - //! Seek from the beginning of the file. - Beginning, - //! Seek from the current position in the file. - Current, - //! Seek from the end of the file. - End - }; - - /*! - * Destroys this File instance. - */ - virtual ~TagVFSFile(); - - /*! - * Returns the file name in the local file system encoding. - */ - const char *name() const; - - /*! - * Returns a pointer to this file's tag. This should be reimplemented in - * the concrete subclasses. - */ - virtual Tag *tag() const = 0; - - /*! - * Returns a pointer to this file's audio properties. This should be - * reimplemented in the concrete subclasses. If no audio properties were - * read then this will return a null pointer. - */ - virtual AudioProperties *audioProperties() const = 0; - - /*! - * Save the file and its associated tags. This should be reimplemented in - * the concrete subclasses. Returns true if the save succeeds. - */ - virtual bool save() = 0; - - /*! - * Reads a block of size \a length at the current get pointer. - */ - ByteVector readBlock(ulong length); - - /*! - * Attempts to write the block \a data at the current get pointer. If the - * file is currently only opened read only -- i.e. readOnly() returns true -- - * this attempts to reopen the file in read/write mode. - * - * \note This should be used instead of using the streaming output operator - * for a ByteVector. And even this function is significantly slower than - * doing output with a char[]. - */ - void writeBlock(const ByteVector &data); - - /*! - * Returns the offset in the file that \a pattern occurs at or -1 if it can - * not be found. If \a before is set, the search will only continue until the - * pattern \a before is found. This is useful for tagging purposes to search - * for a tag before the synch frame. - * - * Searching starts at \a fromOffset, which defaults to the beginning of the - * file. - * - * \note This has the practial limitation that \a pattern can not be longer - * than the buffer size used by readBlock(). Currently this is 1024 bytes. - */ - long find(const ByteVector &pattern, - long fromOffset = 0, - const ByteVector &before = ByteVector::null); - - /*! - * Returns the offset in the file that \a pattern occurs at or -1 if it can - * not be found. If \a before is set, the search will only continue until the - * pattern \a before is found. This is useful for tagging purposes to search - * for a tag before the synch frame. - * - * Searching starts at \a fromOffset and proceeds from the that point to the - * beginning of the file and defaults to the end of the file. - * - * \note This has the practial limitation that \a pattern can not be longer - * than the buffer size used by readBlock(). Currently this is 1024 bytes. - */ - long rfind(const ByteVector &pattern, - long fromOffset = 0, - const ByteVector &before = ByteVector::null); - - /*! - * Insert \a data at position \a start in the file overwriting \a replace - * bytes of the original content. - * - * \note This method is slow since it requires rewriting all of the file - * after the insertion point. - */ - void insert(const ByteVector &data, ulong start = 0, ulong replace = 0); - - /*! - * Removes a block of the file starting a \a start and continuing for - * \a length bytes. - * - * \note This method is slow since it involves rewriting all of the file - * after the removed portion. - */ - void removeBlock(ulong start = 0, ulong length = 0); - - /*! - * Returns true if the file is read only (or if the file can not be opened). - */ - bool readOnly() const; - - /*! - * Since the file can currently only be opened as an argument to the - * constructor (sort-of by design), this returns if that open succeeded. - */ - bool isOpen() const; - - /*! - * Returns true if the file is open and readble and valid information for - * the Tag and / or AudioProperties was found. - */ - bool isValid() const; - - /*! - * Move the I/O pointer to \a offset in the file from position \a p. This - * defaults to seeking from the beginning of the file. - * - * \see Position - */ - void seek(long offset, Position p = Beginning); - - /*! - * Reset the end-of-file and error flags on the file. - */ - void clear(); - - /*! - * Returns the current offset withing the file. - */ - long tell() const; - - /*! - * Returns the length of the file. - */ - long length(); - - /*! - * Returns true if \a file can be opened for reading. If the file does not - * exist, this will return false. - */ - static bool isReadable(const char *file); - - /*! - * Returns true if \a file can be opened for writing. - */ - static bool isWritable(const char *name); - - protected: - /*! - * Construct a File object and opens the \a file. \a file should be a - * be a C-string in the local file system encoding. - * - * \note Constructor is protected since this class should only be - * instantiated through subclasses. - */ - TagVFSFile(const char *file); - - /*! - * Marks the file as valid or invalid. - * - * \see isValid() - */ - void setValid(bool valid); - - /*! - * Truncates the file to a \a length. - */ - void truncate(long length); - - /*! - * Returns the buffer size that is used for internal buffering. - */ - static uint bufferSize(); - - private: - TagVFSFile(const TagVFSFile &); - TagVFSFile &operator=(TagVFSFile &); - - class TagVFSFilePrivate; - TagVFSFilePrivate *d; - }; - -} - -#endif