aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukáš Lalinský <lalinsky@gmail.com>2011-06-09 18:58:05 +0200
committerLukáš Lalinský <lalinsky@gmail.com>2011-06-09 18:58:05 +0200
commit8eb32577bdcdc6f565f3a3807b188d4cee11d5f7 (patch)
tree2aa7a8f3c186c2661d524d643a6a6077760a5f19
parentb3646a07348ffa276ea41a9dae03ddc63ea6c532 (diff)
parent0ed2477dfadc80ea64b9d760e74ba3c9f8bace89 (diff)
Merge branch 'abstract-io'
-rw-r--r--taglib/CMakeLists.txt3
-rw-r--r--taglib/ape/apefile.cpp7
-rw-r--r--taglib/ape/apefile.h8
-rw-r--r--taglib/asf/asffile.cpp7
-rw-r--r--taglib/asf/asffile.h10
-rw-r--r--taglib/flac/flacfile.cpp9
-rw-r--r--taglib/flac/flacfile.h13
-rw-r--r--taglib/mp4/mp4file.cpp7
-rw-r--r--taglib/mp4/mp4file.h10
-rw-r--r--taglib/mpc/mpcfile.cpp7
-rw-r--r--taglib/mpc/mpcfile.h8
-rw-r--r--taglib/mpeg/mpegfile.cpp10
-rw-r--r--taglib/mpeg/mpegfile.h15
-rw-r--r--taglib/ogg/flac/oggflacfile.cpp7
-rw-r--r--taglib/ogg/flac/oggflacfile.h8
-rw-r--r--taglib/ogg/oggfile.cpp5
-rw-r--r--taglib/ogg/oggfile.h11
-rw-r--r--taglib/ogg/speex/speexfile.cpp7
-rw-r--r--taglib/ogg/speex/speexfile.h8
-rw-r--r--taglib/ogg/vorbis/vorbisfile.cpp7
-rw-r--r--taglib/ogg/vorbis/vorbisfile.h8
-rw-r--r--taglib/riff/aiff/aifffile.cpp8
-rw-r--r--taglib/riff/aiff/aifffile.h8
-rw-r--r--taglib/riff/rifffile.cpp9
-rw-r--r--taglib/riff/rifffile.h1
-rw-r--r--taglib/riff/wav/wavfile.cpp8
-rw-r--r--taglib/riff/wav/wavfile.h8
-rw-r--r--taglib/toolkit/tbytevectorstream.cpp167
-rw-r--r--taglib/toolkit/tbytevectorstream.h145
-rw-r--r--taglib/toolkit/tfile.cpp323
-rw-r--r--taglib/toolkit/tfile.h25
-rw-r--r--taglib/toolkit/tfilestream.cpp380
-rw-r--r--taglib/toolkit/tfilestream.h154
-rw-r--r--taglib/toolkit/tiostream.cpp45
-rw-r--r--taglib/toolkit/tiostream.h160
-rw-r--r--taglib/trueaudio/trueaudiofile.cpp17
-rw-r--r--taglib/trueaudio/trueaudiofile.h18
-rw-r--r--taglib/wavpack/wavpackfile.cpp7
-rw-r--r--taglib/wavpack/wavpackfile.h8
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/test_bytevectorstream.cpp92
41 files changed, 1460 insertions, 299 deletions
diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt
index d7274a89..3e7ae76a 100644
--- a/taglib/CMakeLists.txt
+++ b/taglib/CMakeLists.txt
@@ -239,7 +239,10 @@ set(toolkit_SRCS
toolkit/tstringlist.cpp
toolkit/tbytevector.cpp
toolkit/tbytevectorlist.cpp
+ toolkit/tbytevectorstream.cpp
+ toolkit/tiostream.cpp
toolkit/tfile.cpp
+ toolkit/tfilestream.cpp
toolkit/tdebug.cpp
toolkit/unicode.cpp
)
diff --git a/taglib/ape/apefile.cpp b/taglib/ape/apefile.cpp
index 5d914756..2973a476 100644
--- a/taglib/ape/apefile.cpp
+++ b/taglib/ape/apefile.cpp
@@ -92,6 +92,13 @@ APE::File::File(FileName file, bool readProperties,
read(readProperties, propertiesStyle);
}
+APE::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
APE::File::~File()
{
delete d;
diff --git a/taglib/ape/apefile.h b/taglib/ape/apefile.h
index a4bc80d9..2f22fdde 100644
--- a/taglib/ape/apefile.h
+++ b/taglib/ape/apefile.h
@@ -92,6 +92,14 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an WavPack file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp
index 734898d4..cb71b580 100644
--- a/taglib/asf/asffile.cpp
+++ b/taglib/asf/asffile.cpp
@@ -369,6 +369,13 @@ ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle proper
read(readProperties, propertiesStyle);
}
+ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
+ : TagLib::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
ASF::File::~File()
{
for(unsigned int i = 0; i < d->objects.size(); i++) {
diff --git a/taglib/asf/asffile.h b/taglib/asf/asffile.h
index 45e603dc..f0acd728 100644
--- a/taglib/asf/asffile.h
+++ b/taglib/asf/asffile.h
@@ -58,6 +58,16 @@ namespace TagLib {
File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an ASF file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ *
+ * \note In the current implementation, both \a readProperties and
+ * \a propertiesStyle are ignored.
+ */
+ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp
index 4e3d2b36..8c43a8c1 100644
--- a/taglib/flac/flacfile.cpp
+++ b/taglib/flac/flacfile.cpp
@@ -119,6 +119,15 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
read(readProperties, propertiesStyle);
}
+FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
+ bool readProperties, Properties::ReadStyle propertiesStyle) :
+ TagLib::File(stream)
+{
+ d = new FilePrivate;
+ d->ID3v2FrameFactory = frameFactory;
+ read(readProperties, propertiesStyle);
+}
+
FLAC::File::~File()
{
delete d;
diff --git a/taglib/flac/flacfile.h b/taglib/flac/flacfile.h
index 64e67bc0..e9251fed 100644
--- a/taglib/flac/flacfile.h
+++ b/taglib/flac/flacfile.h
@@ -91,6 +91,19 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs a FLAC file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ *
+ * If this file contains and ID3v2 tag the frames will be created using
+ * \a frameFactory.
+ */
+ // BIC: merge with the above constructor
+ File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
+ bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp
index 2d59a8e5..d6ef4ebd 100644
--- a/taglib/mp4/mp4file.cpp
+++ b/taglib/mp4/mp4file.cpp
@@ -72,6 +72,13 @@ MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle a
read(readProperties, audioPropertiesStyle);
}
+MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle)
+ : TagLib::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, audioPropertiesStyle);
+}
+
MP4::File::~File()
{
delete d;
diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h
index 5c28d774..c3613f76 100644
--- a/taglib/mp4/mp4file.h
+++ b/taglib/mp4/mp4file.h
@@ -59,6 +59,16 @@ namespace TagLib {
File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
+ * Contructs a MP4 file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ *
+ * \note In the current implementation, both \a readProperties and
+ * \a propertiesStyle are ignored.
+ */
+ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/mpc/mpcfile.cpp b/taglib/mpc/mpcfile.cpp
index 9e9d6b88..216c1b3b 100644
--- a/taglib/mpc/mpcfile.cpp
+++ b/taglib/mpc/mpcfile.cpp
@@ -96,6 +96,13 @@ MPC::File::File(FileName file, bool readProperties,
read(readProperties, propertiesStyle);
}
+MPC::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
MPC::File::~File()
{
delete d;
diff --git a/taglib/mpc/mpcfile.h b/taglib/mpc/mpcfile.h
index 6adc0ffb..93471cf1 100644
--- a/taglib/mpc/mpcfile.h
+++ b/taglib/mpc/mpcfile.h
@@ -89,6 +89,14 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an MPC file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp
index 3b3513ae..9786c99a 100644
--- a/taglib/mpeg/mpegfile.cpp
+++ b/taglib/mpeg/mpegfile.cpp
@@ -113,6 +113,16 @@ MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
read(readProperties, propertiesStyle);
}
+MPEG::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
+ bool readProperties, Properties::ReadStyle propertiesStyle) :
+ TagLib::File(stream)
+{
+ d = new FilePrivate(frameFactory);
+
+ if(isOpen())
+ read(readProperties, propertiesStyle);
+}
+
MPEG::File::~File()
{
delete d;
diff --git a/taglib/mpeg/mpegfile.h b/taglib/mpeg/mpegfile.h
index 7b41328a..cff5469d 100644
--- a/taglib/mpeg/mpegfile.h
+++ b/taglib/mpeg/mpegfile.h
@@ -85,13 +85,26 @@ namespace TagLib {
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored. The frames will be created using
* \a frameFactory.
+ *
+ * \deprecated This constructor will be dropped in favor of the one below
+ * in a future version.
*/
- // BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an MPEG file from \a stream. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored. The frames will be created using
+ * \a frameFactory.
+ */
+ // BIC: merge with the above constructor
+ File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
+ bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp
index 22d7b81f..3addbffa 100644
--- a/taglib/ogg/flac/oggflacfile.cpp
+++ b/taglib/ogg/flac/oggflacfile.cpp
@@ -75,6 +75,13 @@ Ogg::FLAC::File::File(FileName file, bool readProperties,
read(readProperties, propertiesStyle);
}
+Ogg::FLAC::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
Ogg::FLAC::File::~File()
{
delete d;
diff --git a/taglib/ogg/flac/oggflacfile.h b/taglib/ogg/flac/oggflacfile.h
index 5882a696..d4373795 100644
--- a/taglib/ogg/flac/oggflacfile.h
+++ b/taglib/ogg/flac/oggflacfile.h
@@ -72,6 +72,14 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an Ogg/FLAC file from \a file. If \a readProperties is true
+ * the file's audio properties will also be read using \a propertiesStyle.
+ * If false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/ogg/oggfile.cpp b/taglib/ogg/oggfile.cpp
index f29ba14a..01bb5c9d 100644
--- a/taglib/ogg/oggfile.cpp
+++ b/taglib/ogg/oggfile.cpp
@@ -213,6 +213,11 @@ Ogg::File::File(FileName file) : TagLib::File(file)
d = new FilePrivate;
}
+Ogg::File::File(IOStream *stream) : TagLib::File(stream)
+{
+ d = new FilePrivate;
+}
+
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/oggfile.h b/taglib/ogg/oggfile.h
index da1fcb69..1ecf2b9b 100644
--- a/taglib/ogg/oggfile.h
+++ b/taglib/ogg/oggfile.h
@@ -92,6 +92,17 @@ namespace TagLib {
*/
File(FileName file);
+ /*!
+ * Contructs an Ogg file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ *
+ * \note This constructor is protected since Ogg::File shouldn't be
+ * instantiated directly but rather should be used through the codec
+ * specific subclasses.
+ */
+ File(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/ogg/speex/speexfile.cpp b/taglib/ogg/speex/speexfile.cpp
index 604ac7c6..3a4940a2 100644
--- a/taglib/ogg/speex/speexfile.cpp
+++ b/taglib/ogg/speex/speexfile.cpp
@@ -65,6 +65,13 @@ Speex::File::File(FileName file, bool readProperties,
read(readProperties, propertiesStyle);
}
+Speex::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
Speex::File::~File()
{
delete d;
diff --git a/taglib/ogg/speex/speexfile.h b/taglib/ogg/speex/speexfile.h
index 508b7aaa..c14cf2aa 100644
--- a/taglib/ogg/speex/speexfile.h
+++ b/taglib/ogg/speex/speexfile.h
@@ -64,6 +64,14 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs a Speex file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/ogg/vorbis/vorbisfile.cpp b/taglib/ogg/vorbis/vorbisfile.cpp
index 1098ec38..60056f83 100644
--- a/taglib/ogg/vorbis/vorbisfile.cpp
+++ b/taglib/ogg/vorbis/vorbisfile.cpp
@@ -68,6 +68,13 @@ Vorbis::File::File(FileName file, bool readProperties,
read(readProperties, propertiesStyle);
}
+Vorbis::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
Vorbis::File::~File()
{
delete d;
diff --git a/taglib/ogg/vorbis/vorbisfile.h b/taglib/ogg/vorbis/vorbisfile.h
index 3e33c113..299d9c2d 100644
--- a/taglib/ogg/vorbis/vorbisfile.h
+++ b/taglib/ogg/vorbis/vorbisfile.h
@@ -71,6 +71,14 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs a Vorbis file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/riff/aiff/aifffile.cpp b/taglib/riff/aiff/aifffile.cpp
index 425bfa02..9f6be7ff 100644
--- a/taglib/riff/aiff/aifffile.cpp
+++ b/taglib/riff/aiff/aifffile.cpp
@@ -65,6 +65,14 @@ RIFF::AIFF::File::File(FileName file, bool readProperties,
read(readProperties, propertiesStyle);
}
+RIFF::AIFF::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : RIFF::File(stream, BigEndian)
+{
+ d = new FilePrivate;
+ if(isOpen())
+ read(readProperties, propertiesStyle);
+}
+
RIFF::AIFF::File::~File()
{
delete d;
diff --git a/taglib/riff/aiff/aifffile.h b/taglib/riff/aiff/aifffile.h
index b9b0809f..cac42934 100644
--- a/taglib/riff/aiff/aifffile.h
+++ b/taglib/riff/aiff/aifffile.h
@@ -66,6 +66,14 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an AIFF file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/riff/rifffile.cpp b/taglib/riff/rifffile.cpp
index 8d23bcd6..984568d1 100644
--- a/taglib/riff/rifffile.cpp
+++ b/taglib/riff/rifffile.cpp
@@ -79,6 +79,15 @@ RIFF::File::File(FileName file, Endianness endianness) : TagLib::File(file)
read();
}
+RIFF::File::File(IOStream *stream, Endianness endianness) : TagLib::File(stream)
+{
+ d = new FilePrivate;
+ d->endianness = endianness;
+
+ if(isOpen())
+ read();
+}
+
TagLib::uint RIFF::File::riffSize() const
{
return d->size;
diff --git a/taglib/riff/rifffile.h b/taglib/riff/rifffile.h
index 99282564..e418dbb6 100644
--- a/taglib/riff/rifffile.h
+++ b/taglib/riff/rifffile.h
@@ -56,6 +56,7 @@ namespace TagLib {
enum Endianness { BigEndian, LittleEndian };
File(FileName file, Endianness endianness);
+ File(IOStream *stream, Endianness endianness);
/*!
* \return The size of the main RIFF chunk.
diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp
index 9ec3b510..70d9a35c 100644
--- a/taglib/riff/wav/wavfile.cpp
+++ b/taglib/riff/wav/wavfile.cpp
@@ -65,6 +65,14 @@ RIFF::WAV::File::File(FileName file, bool readProperties,
read(readProperties, propertiesStyle);
}
+RIFF::WAV::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : RIFF::File(stream, LittleEndian)
+{
+ d = new FilePrivate;
+ if(isOpen())
+ read(readProperties, propertiesStyle);
+}
+
RIFF::WAV::File::~File()
{
delete d;
diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h
index b44668c3..341932b9 100644
--- a/taglib/riff/wav/wavfile.h
+++ b/taglib/riff/wav/wavfile.h
@@ -66,6 +66,14 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an WAV file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/toolkit/tbytevectorstream.cpp b/taglib/toolkit/tbytevectorstream.cpp
new file mode 100644
index 00000000..a01da552
--- /dev/null
+++ b/taglib/toolkit/tbytevectorstream.cpp
@@ -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
index 00000000..456b854e
--- /dev/null
+++ b/taglib/toolkit/tbytevectorstream.h
@@ -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
diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp
index 93879f10..bb0261ea 100644
--- a/taglib/toolkit/tfile.cpp
+++ b/taglib/toolkit/tfile.cpp
@@ -24,161 +24,71 @@
***************************************************************************/
#include "tfile.h"
+#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 File::FilePrivate
{
public:
- FilePrivate(FileName fileName);
+ FilePrivate(IOStream *stream);
- FILE *file;
-
- FileNameHandle name;
-
- bool readOnly;
+ IOStream *stream;
bool valid;
- ulong size;
static const uint bufferSize = 1024;
};
-File::FilePrivate::FilePrivate(FileName fileName) :
- file(0),
- name(fileName),
- readOnly(true),
- valid(true),
- size(0)
+File::FilePrivate::FilePrivate(IOStream *stream) :
+ stream(stream),
+ 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));
- }
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
-File::File(FileName file)
+File::File(FileName fileName)
+{
+ IOStream *stream = new FileStream(fileName);
+ d = new FilePrivate(stream);
+}
+
+File::File(IOStream *stream)
{
- d = new FilePrivate(file);
+ d = new FilePrivate(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.
@@ -274,7 +184,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.
@@ -342,147 +252,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
@@ -492,53 +277,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)
@@ -550,18 +314,13 @@ 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/tfile.h b/taglib/toolkit/tfile.h
index da322809..ee6f0488 100644
--- a/taglib/toolkit/tfile.h
+++ b/taglib/toolkit/tfile.h
@@ -29,6 +29,7 @@
#include "taglib_export.h"
#include "taglib.h"
#include "tbytevector.h"
+#include "tiostream.h"
namespace TagLib {
@@ -36,22 +37,6 @@ namespace TagLib {
class Tag;
class AudioProperties;
-#ifdef _WIN32
- class TAGLIB_EXPORT FileName
- {
- public:
- FileName(const wchar_t *name) : m_wname(name) {}
- FileName(const char *name) : m_name(name) {}
- operator const wchar_t *() const { return m_wname.c_str(); }
- operator const char *() const { return m_name.c_str(); }
- private:
- std::string m_name;
- std::wstring m_wname;
- };
-#else
- typedef const char *FileName;
-#endif
-
//! A file class with some useful methods for tag manipulation
/*!
@@ -241,6 +226,14 @@ namespace TagLib {
File(FileName file);
/*!
+ * Construct a File object and use the \a stream instance.
+ *
+ * \note Constructor is protected since this class should only be
+ * instantiated through subclasses.
+ */
+ File(IOStream *stream);
+
+ /*!
* Marks the file as valid or invalid.
*
* \see isValid()
diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp
new file mode 100644
index 00000000..ce2bf11f
--- /dev/null
+++ b/taglib/toolkit/tfilestream.cpp
@@ -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
index 00000000..65ed5fb5
--- /dev/null
+++ b/taglib/toolkit/tfilestream.h
@@ -0,0 +1,154 @@
+/***************************************************************************
+ 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);
+
+ /*!
+ * 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
diff --git a/taglib/toolkit/tiostream.cpp b/taglib/toolkit/tiostream.cpp
new file mode 100644
index 00000000..e7b60dd7
--- /dev/null
+++ b/taglib/toolkit/tiostream.cpp
@@ -0,0 +1,45 @@
+/***************************************************************************
+ 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 "tiostream.h"
+
+using namespace TagLib;
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+IOStream::IOStream()
+{
+}
+
+IOStream::~IOStream()
+{
+}
+
+void IOStream::clear()
+{
+}
+
diff --git a/taglib/toolkit/tiostream.h b/taglib/toolkit/tiostream.h
new file mode 100644
index 00000000..3e7de22a
--- /dev/null
+++ b/taglib/toolkit/tiostream.h
@@ -0,0 +1,160 @@
+/***************************************************************************
+ 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_IOSTREAM_H
+#define TAGLIB_IOSTREAM_H
+
+#include "taglib_export.h"
+#include "taglib.h"
+#include "tbytevector.h"
+
+namespace TagLib {
+
+#ifdef _WIN32
+ class TAGLIB_EXPORT FileName
+ {
+ public:
+ FileName(const wchar_t *name) : m_wname(name) {}
+ FileName(const char *name) : m_name(name) {}
+ operator const wchar_t *() const { return m_wname.c_str(); }
+ operator const char *() const { return m_name.c_str(); }
+ private:
+ std::string m_name;
+ std::wstring m_wname;
+ };
+#else
+ typedef const char *FileName;
+#endif
+
+ //! An abstract class that provides operations on a sequence of bytes
+
+ class TAGLIB_EXPORT IOStream
+ {
+ 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
+ };
+
+ IOStream();
+
+ /*!
+ * Destroys this IOStream instance.
+ */
+ virtual ~IOStream();
+
+ /*!
+ * Returns the stream name in the local file system encoding.
+ */
+ virtual FileName name() const = 0;
+
+ /*!
+ * Reads a block of size \a length at the current get pointer.
+ */
+ virtual ByteVector readBlock(ulong length) = 0;
+
+ /*!
+ * 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[].
+ */
+ virtual void writeBlock(const ByteVector &data) = 0;
+
+ /*!
+ * 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.
+ */
+ virtual void insert(const ByteVector &data, ulong start = 0, ulong replace = 0) = 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.
+ */
+ virtual void removeBlock(ulong start = 0, ulong length = 0) = 0;
+
+ /*!
+ * Returns true if the file is read only (or if the file can not be opened).
+ */
+ virtual bool readOnly() const = 0;
+
+ /*!
+ * Since the file can currently only be opened as an argument to the
+ * constructor (sort-of by design), this returns if that open succeeded.
+ */
+ virtual bool isOpen() 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.
+ *
+ * \see Position
+ */
+ virtual void seek(long offset, Position p = Beginning) = 0;
+
+ /*!
+ * Reset the end-of-stream and error flags on the stream.
+ */
+ virtual void clear();
+
+ /*!
+ * Returns the current offset within the stream.
+ */
+ virtual long tell() const = 0;
+
+ /*!
+ * Returns the length of the stream.
+ */
+ 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 &);
+ };
+
+}
+
+#endif
diff --git a/taglib/trueaudio/trueaudiofile.cpp b/taglib/trueaudio/trueaudiofile.cpp
index 0f362da9..b584b7fd 100644
--- a/taglib/trueaudio/trueaudiofile.cpp
+++ b/taglib/trueaudio/trueaudiofile.cpp
@@ -99,6 +99,23 @@ TrueAudio::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
read(readProperties, propertiesStyle);
}
+TrueAudio::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
+{
+ d = new FilePrivate;
+ if(isOpen())
+ read(readProperties, propertiesStyle);
+}
+
+TrueAudio::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
+ bool readProperties, Properties::ReadStyle propertiesStyle) :
+ TagLib::File(stream)
+{
+ d = new FilePrivate(frameFactory);
+ if(isOpen())
+ read(readProperties, propertiesStyle);
+}
+
TrueAudio::File::~File()
{
delete d;
diff --git a/taglib/trueaudio/trueaudiofile.h b/taglib/trueaudio/trueaudiofile.h
index 32cbf4b1..9c866233 100644
--- a/taglib/trueaudio/trueaudiofile.h
+++ b/taglib/trueaudio/trueaudiofile.h
@@ -97,6 +97,24 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an TrueAudio file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
+ * Contructs an TrueAudio file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored. The frames will be created using
+ * \a frameFactory.
+ */
+ File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
+ bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/taglib/wavpack/wavpackfile.cpp b/taglib/wavpack/wavpackfile.cpp
index 999a5445..19e4c77d 100644
--- a/taglib/wavpack/wavpackfile.cpp
+++ b/taglib/wavpack/wavpackfile.cpp
@@ -88,6 +88,13 @@ WavPack::File::File(FileName file, bool readProperties,
read(readProperties, propertiesStyle);
}
+WavPack::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
WavPack::File::~File()
{
delete d;
diff --git a/taglib/wavpack/wavpackfile.h b/taglib/wavpack/wavpackfile.h
index 3415a329..5173c136 100644
--- a/taglib/wavpack/wavpackfile.h
+++ b/taglib/wavpack/wavpackfile.h
@@ -88,6 +88,14 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
+ * Contructs an WavPack file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
* Destroys this instance of the File.
*/
virtual ~File();
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 86408af0..d169a626 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -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
index 00000000..b5114679
--- /dev/null
+++ b/tests/test_bytevectorstream.cpp
@@ -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);