]> granicus.if.org Git - taglib/commitdiff
Add a ByteVector-backed stream class
authorLukáš Lalinský <lalinsky@gmail.com>
Tue, 12 Apr 2011 12:28:02 +0000 (14:28 +0200)
committerLukáš Lalinský <lalinsky@gmail.com>
Tue, 12 Apr 2011 12:28:02 +0000 (14:28 +0200)
taglib/CMakeLists.txt
taglib/toolkit/tbytevectorstream.cpp [new file with mode: 0644]
taglib/toolkit/tbytevectorstream.h [new file with mode: 0644]
taglib/toolkit/tfilestream.h
tests/CMakeLists.txt
tests/test_bytevectorstream.cpp [new file with mode: 0644]

index 3647caed1df5a9684d3bde1e2d279b36444c85ed..645d554698cea51ebe27ae5135de364440ca2cd2 100644 (file)
@@ -161,6 +161,7 @@ set(toolkit_SRCS
   toolkit/tstringlist.cpp
   toolkit/tbytevector.cpp
   toolkit/tbytevectorlist.cpp
+  toolkit/tbytevectorstream.cpp
   toolkit/tiostream.cpp
   toolkit/tfile.cpp
   toolkit/tfilestream.cpp
diff --git a/taglib/toolkit/tbytevectorstream.cpp b/taglib/toolkit/tbytevectorstream.cpp
new file mode 100644 (file)
index 0000000..a01da55
--- /dev/null
@@ -0,0 +1,167 @@
+/***************************************************************************
+    copyright            : (C) 2011 by Lukas Lalinsky
+    email                : lalinsky@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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 "tbytevectorstream.h"
+#include "tstring.h"
+#include "tdebug.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <stdlib.h>
+
+using namespace TagLib;
+
+class ByteVectorStream::ByteVectorStreamPrivate
+{
+public:
+  ByteVectorStreamPrivate(const ByteVector &data);
+
+  ByteVector data;
+  long position;
+};
+
+ByteVectorStream::ByteVectorStreamPrivate::ByteVectorStreamPrivate(const ByteVector &data) :
+  data(data),
+  position(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ByteVectorStream::ByteVectorStream(const ByteVector &data)
+{
+  d = new ByteVectorStreamPrivate(data);
+}
+
+ByteVectorStream::~ByteVectorStream()
+{
+  delete d;
+}
+
+FileName ByteVectorStream::name() const
+{
+  return FileName(""); // XXX do we need a name?
+}
+
+ByteVector ByteVectorStream::readBlock(ulong length)
+{
+  if(length == 0)
+    return ByteVector::null;
+
+  ByteVector v = d->data.mid(d->position, length);
+  d->position += v.size();
+  return v;
+}
+
+void ByteVectorStream::writeBlock(const ByteVector &data)
+{
+  uint size = data.size();
+  if(d->position + size > length()) {
+    truncate(d->position + size);
+  }
+  memcpy(d->data.data() + d->position, data.data(), size);
+  d->position += size;
+}
+
+void ByteVectorStream::insert(const ByteVector &data, ulong start, ulong replace)
+{
+  long sizeDiff = data.size() - replace;
+  if(sizeDiff < 0) {
+    removeBlock(start + data.size(), -sizeDiff);
+  }
+  else if(sizeDiff > 0) {
+    truncate(length() + sizeDiff);
+    ulong readPosition = start + replace;
+    ulong writePosition = start + data.size();
+    memmove(d->data.data() + writePosition, d->data.data() + readPosition, length() - sizeDiff - readPosition);
+  }
+  seek(start);
+  writeBlock(data);
+}
+
+void ByteVectorStream::removeBlock(ulong start, ulong length)
+{
+  ulong readPosition = start + length;
+  ulong writePosition = start;
+  if(readPosition < ulong(ByteVectorStream::length())) {
+    ulong bytesToMove = ByteVectorStream::length() - readPosition;
+    memmove(d->data.data() + writePosition, d->data.data() + readPosition, bytesToMove);
+    writePosition += bytesToMove;
+  }
+  d->position = writePosition;
+  truncate(writePosition);
+}
+
+bool ByteVectorStream::readOnly() const
+{
+  return false;
+}
+
+bool ByteVectorStream::isOpen() const
+{
+  return true;
+}
+
+void ByteVectorStream::seek(long offset, Position p)
+{
+  switch(p) {
+  case Beginning:
+    d->position = offset;
+    break;
+  case Current:
+    d->position += offset;
+    break;
+  case End:
+    d->position = length() - offset;
+    break;
+  }
+}
+
+void ByteVectorStream::clear()
+{
+}
+
+long ByteVectorStream::tell() const
+{
+  return d->position;
+}
+
+long ByteVectorStream::length()
+{
+  return d->data.size();
+}
+
+void ByteVectorStream::truncate(long length)
+{
+  d->data.resize(length);
+}
+
+ByteVector *ByteVectorStream::data()
+{
+  return &d->data;
+}
diff --git a/taglib/toolkit/tbytevectorstream.h b/taglib/toolkit/tbytevectorstream.h
new file mode 100644 (file)
index 0000000..456b854
--- /dev/null
@@ -0,0 +1,145 @@
+/***************************************************************************
+    copyright            : (C) 2011 by Lukas Lalinsky
+    email                : lalinsky@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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_BYTEVECTORSTREAM_H
+#define TAGLIB_BYTEVECTORSTREAM_H
+
+#include "taglib_export.h"
+#include "taglib.h"
+#include "tbytevector.h"
+#include "tiostream.h"
+
+namespace TagLib {
+
+  class String;
+  class Tag;
+  class AudioProperties;
+
+  //! In-memory Stream class using ByteVector for its storage.
+
+  class TAGLIB_EXPORT ByteVectorStream : 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.
+     */
+    ByteVectorStream(const ByteVector &data);
+
+    /*!
+     * Destroys this ByteVectorStream instance.
+     */
+    virtual ~ByteVectorStream();
+
+    /*!
+     * 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);
+
+    /*!
+     * 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);
+
+    ByteVector *data();
+
+  protected:
+
+  private:
+    class ByteVectorStreamPrivate;
+    ByteVectorStreamPrivate *d;
+  };
+
+}
+
+#endif
index ac17ef068f29f6020e855fe8500119ac56910a5a..65ed5fb5b8c9728018eedebb990150fd4aa1851a 100644 (file)
@@ -80,38 +80,6 @@ namespace TagLib {
      */
     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.
index 86408af0b09c86d64e5f1db3bc27f35c65b7a537..d169a626d6f25d1a96e96b7f74214c16ff847809 100644 (file)
@@ -30,6 +30,7 @@ SET(test_runner_SRCS
   test_trueaudio.cpp
   test_bytevector.cpp
   test_bytevectorlist.cpp
+  test_bytevectorstream.cpp
   test_string.cpp
   test_fileref.cpp
   test_id3v1.cpp
diff --git a/tests/test_bytevectorstream.cpp b/tests/test_bytevectorstream.cpp
new file mode 100644 (file)
index 0000000..b511467
--- /dev/null
@@ -0,0 +1,92 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <tbytevectorstream.h>
+
+using namespace std;
+using namespace TagLib;
+
+class TestByteVectorStream : public CppUnit::TestFixture
+{
+  CPPUNIT_TEST_SUITE(TestByteVectorStream);
+  CPPUNIT_TEST(testInitialData);
+  CPPUNIT_TEST(testWriteBlock);
+  CPPUNIT_TEST(testWriteBlockResize);
+  CPPUNIT_TEST(testReadBlock);
+  CPPUNIT_TEST(testRemoveBlock);
+  CPPUNIT_TEST(testInsert);
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+
+  void testInitialData()
+  {
+    ByteVector v("abcd");
+    ByteVectorStream stream(v);
+
+    CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), *stream.data());
+  }
+
+  void testWriteBlock()
+  {
+    ByteVector v("abcd");
+    ByteVectorStream stream(v);
+
+    stream.seek(1);
+    stream.writeBlock(ByteVector("xx"));
+    CPPUNIT_ASSERT_EQUAL(ByteVector("axxd"), *stream.data());
+  }
+
+  void testWriteBlockResize()
+  {
+    ByteVector v("abcd");
+    ByteVectorStream stream(v);
+
+    stream.seek(3);
+    stream.writeBlock(ByteVector("xx"));
+    CPPUNIT_ASSERT_EQUAL(ByteVector("abcxx"), *stream.data());
+    stream.seek(5);
+    stream.writeBlock(ByteVector("yy"));
+    CPPUNIT_ASSERT_EQUAL(ByteVector("abcxxyy"), *stream.data());
+  }
+
+  void testReadBlock()
+  {
+    ByteVector v("abcd");
+    ByteVectorStream stream(v);
+
+    CPPUNIT_ASSERT_EQUAL(ByteVector("a"), stream.readBlock(1));
+    CPPUNIT_ASSERT_EQUAL(ByteVector("bc"), stream.readBlock(2));
+    CPPUNIT_ASSERT_EQUAL(ByteVector("d"), stream.readBlock(3));
+    CPPUNIT_ASSERT_EQUAL(ByteVector::null, stream.readBlock(3));
+  }
+
+  void testRemoveBlock()
+  {
+    ByteVector v("abcd");
+    ByteVectorStream stream(v);
+
+    stream.removeBlock(1, 1);
+    CPPUNIT_ASSERT_EQUAL(ByteVector("acd"), *stream.data());
+    stream.removeBlock(0, 2);
+    CPPUNIT_ASSERT_EQUAL(ByteVector("d"), *stream.data());
+    stream.removeBlock(0, 2);
+    CPPUNIT_ASSERT_EQUAL(ByteVector(""), *stream.data());
+  }
+
+  void testInsert()
+  {
+    ByteVector v("abcd");
+    ByteVectorStream stream(v);
+
+    stream.insert(ByteVector("xx"), 1, 1);
+    CPPUNIT_ASSERT_EQUAL(ByteVector("axxcd"), *stream.data());
+    stream.insert(ByteVector("yy"), 0, 2);
+    CPPUNIT_ASSERT_EQUAL(ByteVector("yyxcd"), *stream.data());
+    stream.insert(ByteVector("foa"), 3, 2);
+    CPPUNIT_ASSERT_EQUAL(ByteVector("yyxfoa"), *stream.data());
+    stream.insert(ByteVector("123"), 3, 0);
+    CPPUNIT_ASSERT_EQUAL(ByteVector("yyx123foa"), *stream.data());
+  }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVectorStream);