diff options
author | Lukáš Lalinský <lalinsky@gmail.com> | 2011-06-09 18:58:05 +0200 |
---|---|---|
committer | Lukáš Lalinský <lalinsky@gmail.com> | 2011-06-09 18:58:05 +0200 |
commit | 8eb32577bdcdc6f565f3a3807b188d4cee11d5f7 (patch) | |
tree | 2aa7a8f3c186c2661d524d643a6a6077760a5f19 | |
parent | b3646a07348ffa276ea41a9dae03ddc63ea6c532 (diff) | |
parent | 0ed2477dfadc80ea64b9d760e74ba3c9f8bace89 (diff) |
Merge branch 'abstract-io'
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); |