]> granicus.if.org Git - taglib/commitdiff
Add FileStream as a copy of File's methods
authorLukáš Lalinský <lalinsky@gmail.com>
Mon, 11 Apr 2011 21:12:58 +0000 (23:12 +0200)
committerLukáš Lalinský <lalinsky@gmail.com>
Mon, 11 Apr 2011 21:12:58 +0000 (23:12 +0200)
taglib/CMakeLists.txt
taglib/toolkit/tfile.cpp
taglib/toolkit/tfilestream.cpp [new file with mode: 0644]
taglib/toolkit/tfilestream.h [new file with mode: 0644]
taglib/toolkit/tiostream.cpp
taglib/toolkit/tiostream.h

index 53022c0aede532b40bce8278662a92a581d393f6..3647caed1df5a9684d3bde1e2d279b36444c85ed 100644 (file)
@@ -161,7 +161,9 @@ set(toolkit_SRCS
   toolkit/tstringlist.cpp
   toolkit/tbytevector.cpp
   toolkit/tbytevectorlist.cpp
+  toolkit/tiostream.cpp
   toolkit/tfile.cpp
+  toolkit/tfilestream.cpp
   toolkit/tdebug.cpp
   toolkit/unicode.cpp
 )
index 63c28e5a5cc1a936834cad5f4d30d54e2a8c38be..c47892217b119304bbc61d658a0be25a99aa8a2a 100644 (file)
@@ -24,6 +24,7 @@
  ***************************************************************************/
 
 #include "tfile.h"
+#include "tfilestream.h"
 #include "tstring.h"
 #include "tdebug.h"
 
@@ -70,55 +71,16 @@ class File::FilePrivate
 public:
   FilePrivate(FileName fileName);
 
-  FILE *file;
   IOStream *stream;
-
-  FileNameHandle name;
-
-  bool readOnly;
   bool valid;
-  ulong size;
   static const uint bufferSize = 1024;
 };
 
 File::FilePrivate::FilePrivate(FileName fileName) :
-  file(0),
-  name(fileName),
-  readOnly(true),
-  valid(true),
-  size(0)
+  stream(0),
+  valid(true)
 {
-  // First try with read / write mode, if that fails, fall back to read only.
-
-#ifdef _WIN32
-
-  if(wcslen((const wchar_t *) fileName) > 0) {
-
-    file = _wfopen(name, L"rb+");
-
-    if(file)
-      readOnly = false;
-    else
-      file = _wfopen(name, L"rb");
-
-    if(file)
-      return;
-
-  }
-
-#endif
-
-  file = fopen(name, "rb+");
-
-  if(file)
-    readOnly = false;
-  else
-    file = fopen(name, "rb");
-
-  if(!file)
-  {
-    debug("Could not open file " + String((const char *) name));
-  }
+  stream = new FileStream(fileName);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -138,54 +100,29 @@ File::File(IOStream *stream)
 
 File::~File()
 {
-  if(d->file)
-    fclose(d->file);
+  if(d->stream)
+    delete d->stream;
   delete d;
 }
 
 FileName File::name() const
 {
-  return d->name;
+  return d->stream->name();
 }
 
 ByteVector File::readBlock(ulong length)
 {
-  if(!d->file) {
-    debug("File::readBlock() -- Invalid File");
-    return ByteVector::null;
-  }
-
-  if(length == 0)
-    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;
+  return d->stream->readBlock(length);
 }
 
 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);
+  d->stream->writeBlock(data);
 }
 
 long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before)
 {
-  if(!d->file || pattern.size() > d->bufferSize)
+  if(!d->stream || pattern.size() > d->bufferSize)
       return -1;
 
   // The position in the file that the current buffer starts at.
@@ -281,7 +218,7 @@ long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &be
 
 long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before)
 {
-  if(!d->file || pattern.size() > d->bufferSize)
+  if(!d->stream || pattern.size() > d->bufferSize)
       return -1;
 
   // The position in the file that the current buffer starts at.
@@ -349,147 +286,22 @@ long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &b
 
 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;
-
-  // In case we've already reached the end of file...
-
-  buffer.resize(bytesRead);
-
-  // 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(!buffer.isEmpty()) {
-
-    // 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), buffer.size(), d->file);
-    writePosition += buffer.size();
-
-    // 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;
-  }
+  d->stream->insert(data, start, replace);
 }
 
 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 = 1;
-
-  while(bytesRead != 0) {
-    seek(readPosition);
-    bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file);
-    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);
+  d->stream->removeBlock(start, length);
 }
 
 bool File::readOnly() const
 {
-  return d->readOnly;
-}
-
-bool File::isReadable(const char *file)
-{
-  return access(file, R_OK) == 0;
+  return d->stream->readOnly();
 }
 
 bool File::isOpen() const
 {
-  return (d->file != NULL);
+  return d->stream->isOpen();
 }
 
 bool File::isValid() const
@@ -499,53 +311,32 @@ bool File::isValid() const
 
 void File::seek(long offset, Position p)
 {
-  if(!d->file) {
-    debug("File::seek() -- trying to seek in a file that isn't opened.");
-    return;
-  }
+  d->stream->seek(offset, IOStream::Position(p));
+}
 
-  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::truncate(long length)
+{
+  d->stream->truncate(length);
 }
 
 void File::clear()
 {
-  clearerr(d->file);
+  d->stream->clear();
 }
 
 long File::tell() const
 {
-  return ftell(d->file);
+  return d->stream->tell();
 }
 
 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);
+  return d->stream->length();
+}
 
-  d->size = endpos;
-  return endpos;
+bool File::isReadable(const char *file)
+{
+  return access(file, R_OK) == 0;
 }
 
 bool File::isWritable(const char *file)
@@ -557,17 +348,12 @@ bool File::isWritable(const char *file)
 // protected members
 ////////////////////////////////////////////////////////////////////////////////
 
-void File::setValid(bool valid)
-{
-  d->valid = valid;
-}
-
-void File::truncate(long length)
+TagLib::uint File::bufferSize()
 {
-  ftruncate(fileno(d->file), length);
+  return FilePrivate::bufferSize;
 }
 
-TagLib::uint File::bufferSize()
+void File::setValid(bool valid)
 {
-  return FilePrivate::bufferSize;
+  d->valid = valid;
 }
diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp
new file mode 100644 (file)
index 0000000..ce2bf11
--- /dev/null
@@ -0,0 +1,380 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA         *
+ *   02110-1301  USA                                                       *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#include "tfilestream.h"
+#include "tstring.h"
+#include "tdebug.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# include <wchar.h>
+# include <windows.h>
+# include <io.h>
+# define ftruncate _chsize
+#else
+# include <unistd.h>
+#endif
+
+#include <stdlib.h>
+
+#ifndef R_OK
+# define R_OK 4
+#endif
+#ifndef W_OK
+# define W_OK 2
+#endif
+
+using namespace TagLib;
+
+#ifdef _WIN32
+
+typedef FileName FileNameHandle;
+
+#else
+
+struct FileNameHandle : public std::string
+{
+  FileNameHandle(FileName name) : std::string(name) {}
+  operator FileName () const { return c_str(); }
+};
+
+#endif
+
+class FileStream::FileStreamPrivate
+{
+public:
+  FileStreamPrivate(FileName fileName);
+
+  FILE *file;
+
+  FileNameHandle name;
+
+  bool readOnly;
+  ulong size;
+  static const uint bufferSize = 1024;
+};
+
+FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName) :
+  file(0),
+  name(fileName),
+  readOnly(true),
+  size(0)
+{
+  // First try with read / write mode, if that fails, fall back to read only.
+
+#ifdef _WIN32
+
+  if(wcslen((const wchar_t *) fileName) > 0) {
+
+    file = _wfopen(name, L"rb+");
+
+    if(file)
+      readOnly = false;
+    else
+      file = _wfopen(name, L"rb");
+
+    if(file)
+      return;
+
+  }
+
+#endif
+
+  file = fopen(name, "rb+");
+
+  if(file)
+    readOnly = false;
+  else
+    file = fopen(name, "rb");
+
+  if(!file)
+  {
+    debug("Could not open file " + String((const char *) name));
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FileStream::FileStream(FileName file)
+{
+  d = new FileStreamPrivate(file);
+}
+
+FileStream::~FileStream()
+{
+  if(d->file)
+    fclose(d->file);
+  delete d;
+}
+
+FileName FileStream::name() const
+{
+  return d->name;
+}
+
+ByteVector FileStream::readBlock(ulong length)
+{
+  if(!d->file) {
+    debug("FileStream::readBlock() -- Invalid File");
+    return ByteVector::null;
+  }
+
+  if(length == 0)
+    return ByteVector::null;
+
+  if(length > FileStreamPrivate::bufferSize &&
+     length > ulong(FileStream::length()))
+  {
+    length = FileStream::length();
+  }
+
+  ByteVector v(static_cast<uint>(length));
+  const int count = fread(v.data(), sizeof(char), length, d->file);
+  v.resize(count);
+  return v;
+}
+
+void FileStream::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);
+}
+
+void FileStream::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;
+
+  // In case we've already reached the end of file...
+
+  buffer.resize(bytesRead);
+
+  // 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(!buffer.isEmpty()) {
+
+    // 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), buffer.size(), d->file);
+    writePosition += buffer.size();
+
+    // 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 FileStream::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 = 1;
+
+  while(bytesRead != 0) {
+    seek(readPosition);
+    bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file);
+    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 FileStream::readOnly() const
+{
+  return d->readOnly;
+}
+
+bool FileStream::isOpen() const
+{
+  return (d->file != NULL);
+}
+
+void FileStream::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 FileStream::clear()
+{
+  clearerr(d->file);
+}
+
+long FileStream::tell() const
+{
+  return ftell(d->file);
+}
+
+long FileStream::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;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void FileStream::truncate(long length)
+{
+  ftruncate(fileno(d->file), length);
+}
+
+TagLib::uint FileStream::bufferSize()
+{
+  return FileStreamPrivate::bufferSize;
+}
diff --git a/taglib/toolkit/tfilestream.h b/taglib/toolkit/tfilestream.h
new file mode 100644 (file)
index 0000000..ac17ef0
--- /dev/null
@@ -0,0 +1,186 @@
+/***************************************************************************
+    copyright            : (C) 2002 - 2008 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., 51 Franklin Street, Fifth Floor, Boston, MA         *
+ *   02110-1301  USA                                                       *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_FILESTREAM_H
+#define TAGLIB_FILESTREAM_H
+
+#include "taglib_export.h"
+#include "taglib.h"
+#include "tbytevector.h"
+#include "tiostream.h"
+
+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 TAGLIB_EXPORT FileStream : public IOStream
+  {
+  public:
+    /*!
+     * Construct a File object and opens the \a file.  \a file should be a
+     * be a C-string in the local file system encoding.
+     */
+    FileStream(FileName file);
+
+    /*!
+     * Destroys this FileStream instance.
+     */
+    virtual ~FileStream();
+
+    /*!
+     * Returns the file name in the local file system encoding.
+     */
+    FileName name() const;
+
+    /*!
+     * 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;
+
+    /*!
+     * 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 within the file.
+     */
+    long tell() const;
+
+    /*!
+     * Returns the length of the file.
+     */
+    long length();
+
+    /*!
+     * Truncates the file to a \a length.
+     */
+    void truncate(long length);
+
+  protected:
+
+    /*!
+     * Returns the buffer size that is used for internal buffering.
+     */
+    static uint bufferSize();
+
+  private:
+    class FileStreamPrivate;
+    FileStreamPrivate *d;
+  };
+
+}
+
+#endif
index 12c4b6fea5f10afea4323a5a3b9e290a5a09d282..f76fe095040fcc4a0ecf212bc95b2f6857a1b6f8 100644 (file)
@@ -31,7 +31,10 @@ using namespace TagLib;
 // public members
 ////////////////////////////////////////////////////////////////////////////////
 
+IOStream::IOStream()
+{
+}
+
 IOStream::~IOStream()
 {
-  delete d;
 }
index fee02e8f906e73e00c8e3f586dfd26ae37cc4792..8d7ba1059e4fe0fe315a3195544cf1aa2428a976 100644 (file)
@@ -65,6 +65,8 @@ namespace TagLib {
       End
     };
 
+    IOStream();
+
     /*!
      * Destroys this IOStream instance.
      */
@@ -120,11 +122,6 @@ namespace TagLib {
      */
     virtual bool isOpen() const = 0;
 
-    /*!
-     * Returns true if the file is open and readble.
-     */
-    virtual bool isValid() const = 0;
-
     /*!
      * Move the I/O pointer to \a offset in the stream from position \a p.  This
      * defaults to seeking from the beginning of the stream.
@@ -148,6 +145,11 @@ namespace TagLib {
      */
     virtual long length() = 0;
 
+    /*!
+     * Truncates the stream to a \a length.
+     */
+    virtual void truncate(long length) = 0;
+
   private:
     IOStream(const IOStream &);
     IOStream &operator=(const IOStream &);