[xiph-commits] r16372 - trunk/ezstream/win32
moritz at svn.xiph.org
moritz at svn.xiph.org
Sat Aug 1 03:54:00 PDT 2009
Author: moritz
Date: 2009-08-01 03:54:00 -0700 (Sat, 01 Aug 2009)
New Revision: 16372
Added:
trunk/ezstream/win32/tfile.cpp
Modified:
trunk/ezstream/win32/Makefile.am
Log:
Add and redistribute tfile.cpp, which I needed to modify in order to build Taglib 1.4 on Windows. This should also satisfy the LGPL.
Modified: trunk/ezstream/win32/Makefile.am
===================================================================
--- trunk/ezstream/win32/Makefile.am 2009-08-01 10:51:48 UTC (rev 16371)
+++ trunk/ezstream/win32/Makefile.am 2009-08-01 10:54:00 UTC (rev 16372)
@@ -7,7 +7,8 @@
ezstream.sln ezstream.vcproj \
libflac.vcproj libogg.vcproj libshout.vcproj libspeex.vcproj \
libtheora.vcproj libvorbis.vcproj libvorbisfile.vcproj libz.vcproj \
- taglib.vcproj taglib_c.vcproj
+ taglib.vcproj taglib_c.vcproj \
+ tfile.cpp
CLEANFILES = core *.core *~ .*~ ezstream.1.pdf
Added: trunk/ezstream/win32/tfile.cpp
===================================================================
--- trunk/ezstream/win32/tfile.cpp (rev 0)
+++ trunk/ezstream/win32/tfile.cpp 2009-08-01 10:54:00 UTC (rev 16372)
@@ -0,0 +1,511 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler at 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 "tfile.h"
+#include "tstring.h"
+#include "tdebug.h"
+
+#ifdef WIN32
+# include <io.h>
+# define ftruncate _chsize
+#endif /* WIN32 */
+#include <stdio.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#if !defined(R_OK) || !defined(W_OK)
+# undef R_OK
+# undef W_OK
+# define R_OK 4
+# define W_OK 2
+#endif /* !R_OK || !W_OK */
+
+using namespace TagLib;
+
+class File::FilePrivate
+{
+public:
+ FilePrivate(const char *fileName) :
+ file(0),
+ name(fileName),
+ readOnly(true),
+ valid(true),
+ size(0)
+ {}
+
+ ~FilePrivate()
+ {
+ free((void *)name);
+ }
+
+ FILE *file;
+ const char *name;
+ bool readOnly;
+ bool valid;
+ ulong size;
+ static const uint bufferSize = 1024;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+File::File(const char *file)
+{
+ d = new FilePrivate(::strdup(file));
+
+ d->readOnly = !isWritable(file);
+ d->file = fopen(file, d->readOnly ? "r" : "r+");
+
+ if(!d->file)
+ debug("Could not open file " + String(file));
+}
+
+File::~File()
+{
+ if(d->file)
+ fclose(d->file);
+ delete d;
+}
+
+const char *File::name() const
+{
+ return d->name;
+}
+
+ByteVector File::readBlock(ulong length)
+{
+ if(!d->file) {
+ debug("File::readBlock() -- Invalid File");
+ return ByteVector::null;
+ }
+
+ if(length > FilePrivate::bufferSize &&
+ length > ulong(File::length()))
+ {
+ length = File::length();
+ }
+
+ ByteVector v(static_cast<uint>(length));
+ const int count = fread(v.data(), sizeof(char), length, d->file);
+ v.resize(count);
+ return v;
+}
+
+void File::writeBlock(const ByteVector &data)
+{
+ if(!d->file)
+ return;
+
+ if(d->readOnly) {
+ debug("File::writeBlock() -- attempted to write to a file that is not writable");
+ return;
+ }
+
+ fwrite(data.data(), sizeof(char), data.size(), d->file);
+}
+
+long File::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 File::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 File::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<uint>(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 = 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 = 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);
+ 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 File::removeBlock(ulong start, ulong length)
+{
+ if(!d->file)
+ return;
+
+ ulong bufferLength = bufferSize();
+
+ long readPosition = start + length;
+ long writePosition = start;
+
+ ByteVector buffer(static_cast<uint>(bufferLength));
+
+ ulong bytesRead = true;
+
+ while(bytesRead != 0) {
+ seek(readPosition);
+ bytesRead = 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);
+ fwrite(buffer.data(), sizeof(char), bytesRead, d->file);
+ writePosition += bytesRead;
+ }
+ truncate(writePosition);
+}
+
+bool File::readOnly() const
+{
+ return d->readOnly;
+}
+
+bool File::isReadable(const char *file)
+{
+ return access(file, R_OK) == 0;
+}
+
+bool File::isOpen() const
+{
+ return d->file;
+}
+
+bool File::isValid() const
+{
+ return d->file && d->valid;
+}
+
+void File::seek(long offset, Position p)
+{
+ if(!d->file) {
+ debug("File::seek() -- trying to seek in a file that isn't opened.");
+ return;
+ }
+
+ switch(p) {
+ case Beginning:
+ fseek(d->file, offset, SEEK_SET);
+ break;
+ case Current:
+ fseek(d->file, offset, SEEK_CUR);
+ break;
+ case End:
+ fseek(d->file, offset, SEEK_END);
+ break;
+ }
+}
+
+void File::clear()
+{
+ clearerr(d->file);
+}
+
+long File::tell() const
+{
+ return ftell(d->file);
+}
+
+long File::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 File::isWritable(const char *file)
+{
+ return access(file, W_OK) == 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void File::setValid(bool valid)
+{
+ d->valid = valid;
+}
+
+void File::truncate(long length)
+{
+ ftruncate(fileno(d->file), length);
+}
+
+TagLib::uint File::bufferSize()
+{
+ return FilePrivate::bufferSize;
+}
Property changes on: trunk/ezstream/win32/tfile.cpp
___________________________________________________________________
Added: svn:eol-style
+ native
More information about the commits
mailing list