This commit was manufactured by cvs2svn to accommodate

a server-side copy/move.


git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@288617 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
This commit is contained in:
Scott Wheeler
2004-02-17 02:11:05 +00:00
commit 7fe6647435
97 changed files with 17567 additions and 0 deletions

16
toolkit/Makefile.am Normal file
View File

@ -0,0 +1,16 @@
INCLUDES = $(all_includes)
noinst_LTLIBRARIES = libtoolkit.la
libtoolkit_la_SOURCES = \
tstring.cpp tstringlist.cpp tbytevector.cpp \
tbytevectorlist.cpp tfile.cpp tdebug.cpp unicode.cpp
taglib_include_HEADERS = \
taglib.h tstring.h tlist.h tlist.tcc tstringlist.h \
tbytevector.h tbytevectorlist.h tfile.h tdebug.h \
tmap.h tmap.tcc
taglib_includedir = $(includedir)/taglib
EXTRA_DIST = $(libtoolkit_la_SOURCES) $(taglib_include_HEADERS)

148
toolkit/taglib.h Normal file
View File

@ -0,0 +1,148 @@
/***************************************************************************
copyright : (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_H
#define TAGLIB_H
#define TAGLIB_MAJOR_VERSION 0
#define TAGLIB_MINOR_VERSION 96
#include <string>
//! A namespace for all TagLib related classes and functions
/*!
* This namespace contains everything in TagLib. For projects working with
* TagLib extensively it may be conveniten to add a
* \code
* using namespace TagLib;
* \endcode
*/
namespace TagLib {
class String;
typedef wchar_t wchar;
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
/*!
* Unfortunately std::wstring isn't defined on some systems, (i.e. GCC < 3)
* so I'm providing something here that should be constant.
*/
typedef std::basic_string<wchar> wstring;
#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class.
/*!
* \internal
* This is just used as a base class for shared classes in TagLib.
*
* \warning This <b>is not</b> part of the TagLib public API!
*/
class RefCounter
{
public:
RefCounter() : refCount(1) {}
void ref() { refCount++; }
bool deref() { return ! --refCount ; }
int count() { return refCount; }
private:
uint refCount;
};
/*!
* A simple strdup implementation since the standard one creates some wierdness
* with delete.
*/
static inline char *strdup(const char *s)
{
const int l = ::strlen(s);
char *buffer = new char[l];
::memcpy(buffer, s, l);
return buffer;
}
#endif // DO_NOT_DOCUMENT
}
/*!
* \mainpage TagLib
* \section intro Introduction
* TagLib, is well, a library for reading and editing audio meta data, commonly know as \e tags.
*
* Some goals of TagLib:
* - A clean, high level, C++ API to handling audio meta data.
* - Support for at least ID3v1, ID3v2 and Ogg Vorbis \e comments.
* - A generic, \e simple API for the most common tagging related functions.
* - Binary compatibility between minor releases using the standard KDE/Qt techniques for C++ binary compatibility.
* - Make the tagging framework extensible by library users; i.e. it will be possible for libarary users to implement
* additional ID3v2 frames, without modifying the TagLib source (through the use of <i>Abstract Factories</i> and
* such.
*
* Because TagLib desires to be toolkit agnostic, in hope of being widely adopted and the most flexible in licensing
* TagLib provides many of its own toolkit classes; in fact the only external dependancy that TagLib has, it a
* semi-sane STL implementation.
*
* \section why Why TagLib?
*
* TagLib was written to fill a gap in the Open Source/Free Software community. Currently there is a lack in the
* OSS/FS for a homogenous API to the most common music types. In fact the only semi-portable implementation of the
* ID3v2 standard available on Linux is id3lib, which unfortunately is poorly written, poorly documented and which
* cycles through maintainers at least once a year (I took my turn some time ago.).
*
* As TagLib will be initially injected into the KDE community, while I am not linking to any of the KDE or Qt libraries
* I have tried to follow the coding style of those libraries. Again, this is in sharp contrast to id3lib, which
* basically provides a hybrid C/C++ API and uses a dubious object model.
*
* I get asked rather frequently why I am replacing id3lib (mostly by people that have never worked with id3lib), if
* you are concerned about this please email me; I can provide my lengthy standard rant. :-)
*
* \section examples Examples:
*
* I've talked a lot about the \e homogenous API to common music formats. Here's an example of how things (will) work:
*
* \code
*
* TagLib::FileRef f("Latex Solar Beef.mp3");
* TagLib::String artist = f.tag()->artist(); // artist == "Frank Zappa"
*
* f.tag()->setAlbum("Fillmore East");
* f.save();
*
* TagLib::FileRef g("Free City Rhymes.ogg");
* TagLib::String album = g.tag()->album(); // album == "NYC Ghosts & Flowers"
*
* g.tag()->setTrack(1);
* g.save();
*
* \endcode
*
* Notice that these high level functions work for both Ogg \e and MP3. For this high level API, which is suitable for
* most applications, the differences between ID3v2, ID3v1, MPEG and Ogg Vorbis can all be ignored.
*
* \author Scott Wheeler <wheeler@kde.org>
*/
#endif

604
toolkit/tbytevector.cpp Normal file
View File

@ -0,0 +1,604 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#include <iostream>
#include <tstring.h>
#include <tdebug.h>
#include "tbytevector.h"
namespace TagLib {
static const uint crcTable[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
/*!
* A templatized find that works both with a ByteVector and a ByteVectorMirror.
*/
template <class Vector>
int vectorFind(const Vector &v, const Vector &pattern, uint offset, int byteAlign)
{
if(pattern.size() > v.size() || offset >= v.size() - 1)
return -1;
// if an offset was specified, just do a recursive call on the substring
if(offset > 0) {
// start at the next byte aligned block
Vector section = v.mid(offset + byteAlign - 1 - offset % byteAlign);
int match = section.find(pattern, 0, byteAlign);
return match >= 0 ? int(match + offset) : -1;
}
// this is a simplified Boyer-Moore string searching algorithm
uchar lastOccurrence[256];
for(uint i = 0; i < 256; ++i)
lastOccurrence[i] = uchar(pattern.size());
for(uint i = 0; i < pattern.size() - 1; ++i)
lastOccurrence[unsigned(pattern[i])] = uchar(pattern.size() - i - 1);
for(uint i = pattern.size() - 1; i < v.size(); i += lastOccurrence[uchar(v.at(i))]) {
int iBuffer = i;
int iPattern = pattern.size() - 1;
while(iPattern >= 0 && v.at(iBuffer) == pattern[iPattern]) {
--iBuffer;
--iPattern;
}
if(-1 == iPattern && (iBuffer + 1) % byteAlign == 0)
return iBuffer + 1;
}
return -1;
}
/*!
* Wraps the accessors to a ByteVector to make the search algorithm access the
* elements in reverse.
*
* \see vectorFind()
* \see ByteVector::rfind()
*/
class ByteVectorMirror
{
public:
ByteVectorMirror(const ByteVector &source) : v(source) {}
const char operator[](int index) const
{
return v[v.size() - index - 1];
}
const char at(int index) const
{
return v.at(v.size() - index - 1);
}
ByteVectorMirror mid(uint index, uint length = 0xffffffff) const
{
return length == 0xffffffff ? v.mid(0, index) : v.mid(index - length, length);
}
uint size() const
{
return v.size();
}
int find(const ByteVectorMirror &pattern, uint offset = 0, int byteAlign = 1) const
{
ByteVectorMirror v(*this);
const int pos = vectorFind<ByteVectorMirror>(v, pattern, offset, byteAlign);
// If the offset is zero then we need to adjust the location in the search
// to be appropriately reversed. If not we need to account for the fact
// that the recursive call (called from the above line) has already ajusted
// for this but that the normal templatized find above will add the offset
// to the returned value.
//
// This is a little confusing at first if you don't first stop to think
// through the logic involved in the forward search.
if(pos == -1)
return -1;
if(offset == 0)
return size() - pos - pattern.size();
else
return pos - offset;
}
private:
const ByteVector v;
};
}
using namespace TagLib;
class ByteVector::ByteVectorPrivate : public RefCounter
{
public:
ByteVectorPrivate() : RefCounter() {}
ByteVectorPrivate(const std::vector<char> &v) : RefCounter(), data(v) {}
ByteVectorPrivate(TagLib::uint size, char value) : RefCounter(), data(size, value) {}
std::vector<char> data;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
ByteVector ByteVector::null;
ByteVector ByteVector::fromCString(const char *s, uint length)
{
ByteVector v;
if(length == 0xffffffff)
v.setData(s);
else
v.setData(s, length);
return v;
}
ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
{
ByteVector v(4, 0);
for(int i = 0; i < 4; i++)
v[i] = uchar(value >> ((mostSignificantByteFirst ? 3 - i : i) * 8) & 0xff);
return v;
}
ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst)
{
ByteVector v(8, 0);
for(int i = 0; i < 8; i++)
v[i] = uchar(value >> ((mostSignificantByteFirst ? 7 - i : i) * 8) & 0xff);
return v;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ByteVector::ByteVector()
{
d = new ByteVectorPrivate;
}
ByteVector::ByteVector(uint size, char value)
{
d = new ByteVectorPrivate(size, value);
}
ByteVector::ByteVector(const ByteVector &v) : d(v.d)
{
d->ref();
}
ByteVector::ByteVector(char c)
{
d = new ByteVectorPrivate;
d->data.push_back(c);
}
ByteVector::ByteVector(const char *data, uint length)
{
d = new ByteVectorPrivate;
setData(data, length);
}
ByteVector::ByteVector(const char *data)
{
d = new ByteVectorPrivate;
setData(data);
}
ByteVector::~ByteVector()
{
if(d->deref())
delete d;
}
void ByteVector::setData(const char *data, uint length)
{
detach();
for(uint i = 0; i < length; i++)
d->data.push_back(data[i]);
}
void ByteVector::setData(const char *data)
{
detach();
for(uint i = 0; data[i] != 0; i++)
d->data.push_back(data[i]);
}
char *ByteVector::data()
{
// A rather obscure feature of the C++ spec that I hadn't thought of that makes
// working with C libs much more effecient. There's more here:
//
// http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp
detach();
return &(d->data[0]);
}
const char *ByteVector::data() const
{
return &(d->data[0]);
}
ByteVector ByteVector::mid(uint index, uint length) const
{
ByteVector v;
ConstIterator endIt;
if(length < 0xffffffff && length + index < size())
endIt = d->data.begin() + index + length;
else
endIt = d->data.end();
v.d->data.insert(v.d->data.begin(), ConstIterator(d->data.begin() + index), endIt);
return v;
}
char ByteVector::at(uint index) const
{
return index < size() ? d->data[index] : 0;
}
int ByteVector::find(const ByteVector &pattern, uint offset, int byteAlign) const
{
return vectorFind<ByteVector>(*this, pattern, offset, byteAlign);
}
int ByteVector::rfind(const ByteVector &pattern, uint offset, int byteAlign) const
{
// Ok, this is a little goofy, but pretty cool after it sinks in. Instead of
// reversing the find method's Boyer-Moore search algorithm I created a "mirror"
// for a ByteVector to reverse the behavior of the accessors.
ByteVectorMirror v(*this);
ByteVectorMirror p(pattern);
return v.find(p, offset, byteAlign);
}
bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint patternOffset, uint patternLength) const
{
if(pattern.size() < patternLength)
patternLength = pattern.size();
// do some sanity checking -- all of these things are needed for the search to be valid
if(patternLength > size() || offset >= size() || patternOffset >= pattern.size() || patternLength == 0)
return false;
// loop through looking for a mismatch
for(uint i = 0; i < patternLength - patternOffset; i++) {
if(at(i + offset) != pattern[i + patternOffset])
return false;
}
return true;
}
bool ByteVector::startsWith(const ByteVector &pattern) const
{
return containsAt(pattern, 0);
}
bool ByteVector::endsWith(const ByteVector &pattern) const
{
return containsAt(pattern, size() - pattern.size());
}
int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const
{
if(pattern.size() > size())
return -1;
const int startIndex = size() - pattern.size();
// try to match the last n-1 bytes from the vector (where n is the pattern
// size) -- continue trying to match n-2, n-3...1 bytes
for(uint i = 1; i < pattern.size(); i++) {
if(containsAt(pattern, startIndex + i, 0, pattern.size() - i))
return startIndex + i;
}
return -1;
}
void ByteVector::append(const ByteVector &v)
{
detach();
for(uint i = 0; i < v.size(); i++)
d->data.push_back(v[i]);
}
void ByteVector::clear()
{
detach();
d->data.clear();
}
TagLib::uint ByteVector::size() const
{
return d->data.size();
}
ByteVector &ByteVector::resize(uint size, char padding)
{
if(d->data.size() < size) {
d->data.reserve(size);
d->data.insert(d->data.end(), size - d->data.size(), padding);
}
else
d->data.erase(d->data.begin() + size, d->data.end());
return *this;
}
ByteVector::Iterator ByteVector::begin()
{
return d->data.begin();
}
ByteVector::ConstIterator ByteVector::begin() const
{
return d->data.begin();
}
ByteVector::Iterator ByteVector::end()
{
return d->data.end();
}
ByteVector::ConstIterator ByteVector::end() const
{
return d->data.end();
}
bool ByteVector::isNull() const
{
return d == null.d;
}
bool ByteVector::isEmpty() const
{
return d->data.size() == 0;
}
TagLib::uint ByteVector::checksum() const
{
uint sum = 0;
for(ByteVector::ConstIterator it = begin(); it != end(); ++it)
sum = (sum << 8) ^ crcTable[((sum >> 24) & 0xff) ^ uchar(*it)];
return sum;
}
TagLib::uint ByteVector::toUInt(bool mostSignificantByteFirst) const
{
uint sum = 0;
int last = d->data.size() > 4 ? 3 : d->data.size() - 1;
for(int i = 0; i <= last; i++)
sum |= uchar(d->data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);
return sum;
}
long long ByteVector::toLongLong(bool mostSignificantByteFirst) const
{
// Just do all of the bit operations on the unsigned value and use an implicit
// cast on the way out.
unsigned long long sum = 0;
int last = d->data.size() > 8 ? 7 : d->data.size() - 1;
for(int i = 0; i <= last; i++)
sum |= (unsigned long long) uchar(d->data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);
return sum;
}
const char &ByteVector::operator[](int index) const
{
return d->data[index];
}
char &ByteVector::operator[](int index)
{
detach();
return d->data[index];
}
bool ByteVector::operator==(const ByteVector &v) const
{
if(size() != v.size())
return false;
for(uint i = 0; i < size(); i++) {
if(at(i) != v.at(i))
return false;
}
return true;
}
bool ByteVector::operator!=(const ByteVector &v) const
{
return !operator==(v);
}
bool ByteVector::operator==(const char *s) const
{
return operator==(fromCString(s));
}
bool ByteVector::operator!=(const char *s) const
{
return !operator==(s);
}
bool ByteVector::operator<(const ByteVector &v) const
{
for(uint i = 0; i < size() && i < v.size(); i++) {
if(at(i) < v.at(i))
return true;
else if(at(i) > v.at(i))
return false;
}
return size() < v.size();
}
bool ByteVector::operator>(const ByteVector &v) const
{
return !operator<(v);
}
ByteVector ByteVector::operator+(const ByteVector &v) const
{
ByteVector sum(*this);
sum.append(v);
return sum;
}
ByteVector &ByteVector::operator=(const ByteVector &v)
{
if(&v == this)
return *this;
if(d->deref())
delete d;
d = v.d;
d->ref();
return *this;
}
ByteVector &ByteVector::operator=(char c)
{
if(d->deref())
delete d;
*this = ByteVector(c);
return *this;
}
ByteVector &ByteVector::operator=(const char *data)
{
if(d->deref())
delete d;
*this = ByteVector(data);
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void ByteVector::detach()
{
if(d->count() > 1) {
d->deref();
d = new ByteVectorPrivate(d->data);
}
}
////////////////////////////////////////////////////////////////////////////////
// related functions
////////////////////////////////////////////////////////////////////////////////
std::ostream &operator<<(std::ostream &s, const ByteVector &v)
{
for(TagLib::uint i = 0; i < v.size(); i++)
s << v[i];
return s;
}

377
toolkit/tbytevector.h Normal file
View File

@ -0,0 +1,377 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_BYTEVECTOR_H
#define TAGLIB_BYTEVECTOR_H
#include "taglib.h"
#include <vector>
namespace TagLib {
//! A byte vector
/*!
* This class provides a byte vector with some methods that are useful for
* tagging purposes. Many of the search functions are tailored to what is
* useful for finding tag related paterns in a data array.
*/
class ByteVector
{
public:
#ifndef DO_NOT_DOCUMENT
typedef std::vector<char>::iterator Iterator;
typedef std::vector<char>::const_iterator ConstIterator;
#endif
/*!
* Constructs an empty byte vector.
*/
ByteVector();
/*!
* Construct a vector of size \a size with all values set to \a value by
* default.
*/
ByteVector(uint size, char value = 0);
/*!
* Contructs a byte vector that is a copy of \a v.
*/
ByteVector(const ByteVector &v);
/*!
* Contructs a byte vector that contains \a c.
*/
ByteVector(char c);
/*!
* Constructs a byte vector that copies \a data for up to \a length bytes.
*/
ByteVector(const char *data, uint length);
/*!
* Constructs a byte vector that copies \a data up to the first null
* byte. The behavior is undefined if \a data is not null terminated.
* This is particularly useful for constructing byte arrays from string
* constants.
*/
ByteVector(const char *data);
/*!
* Destroys this ByteVector instance.
*/
virtual ~ByteVector();
/*!
* Sets the data for the byte array using the first \a length bytes of \a data
*/
void setData(const char *data, uint length);
/*!
* Sets the data for the byte array copies \a data up to the first null
* byte. The behavior is undefined if \a data is not null terminated.
*/
void setData(const char *data);
/*!
* Returns a pointer to the internal data structure.
*
* \warning Care should be taken when modifying this data structure as it is
* easy to corrupt the ByteVector when doing so. Specifically, while the
* data may be changed, its length may not be.
*/
char *data();
/*!
* Returns a pointer to the internal data structure which may not be modified.
*/
const char *data() const;
/*!
* Returns a byte vector made up of the bytes starting at \a index and
* for \a length bytes. If \a length is not specified it will return the bytes
* from \a index to the end of the vector.
*/
ByteVector mid(uint index, uint length = 0xffffffff) const;
/*!
* This essentially performs the same as operator[](), but instead of causing
* a runtime error if the index is out of bounds, it will return a null byte.
*/
char at(uint index) const;
/*!
* Searches the ByteVector for \a pattern starting at \a offset and returns
* the offset. Returns -1 if the pattern was not found. If \a byteAlign is
* specified the pattern will only be matched if it starts on a byteDivisible
* by \a byteAlign.
*/
int find(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
/*!
* Searches the ByteVector for \a pattern starting from either the end of the
* vector or \a offset and returns the offset. Returns -1 if the pattern was
* not found. If \a byteAlign is specified the pattern will only be matched
* if it starts on a byteDivisible by \a byteAlign.
*/
int rfind(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
/*!
* Checks to see if the vector contains the \a pattern starting at position
* \a offset. Optionally, if you only want to search for part of the pattern
* you can specify an offset within the pattern to start from. Also, you can
* specify to only check for the first \a patternLength bytes of \a pattern with
* the \a patternLength argument.
*/
bool containsAt(const ByteVector &pattern, uint offset, uint patternOffset = 0, uint patternLength = 0xffffffff) const;
/*!
* Returns true if the vector starts with \a pattern.
*/
bool startsWith(const ByteVector &pattern) const;
/*!
* Returns true if the vector ends with \a pattern.
*/
bool endsWith(const ByteVector &pattern) const;
/*!
* Checks for a partial match of \a pattern at the end of the vector. It
* returns the offset of the partial match within the vector, or -1 if the
* pattern is not found. This method is particularly useful when searching for
* patterns that start in one vector and end in another. When combined with
* startsWith() it can be used to find a pattern that overlaps two buffers.
*
* \note This will not match the complete pattern at the end of the string; use
* endsWith() for that.
*/
int endsWithPartialMatch(const ByteVector &pattern) const;
/*!
* Appends \a v to the end of the ByteVector.
*/
void append(const ByteVector &v);
/*!
* Clears the data.
*/
void clear();
/*!
* Returns the size of the array.
*/
uint size() const;
/*!
* Resize the vector to \a size. If the vector is currently less than
* \a size, pad the remaining spaces with \a padding. Returns a reference
* to the resized vector.
*/
ByteVector &resize(uint size, char padding = 0);
/*!
* Returns an Iterator that points to the front of the vector.
*/
Iterator begin();
/*!
* Returns a ConstIterator that points to the front of the vector.
*/
ConstIterator begin() const;
/*!
* Returns an Iterator that points to the back of the vector.
*/
Iterator end();
/*!
* Returns a ConstIterator that points to the back of the vector.
*/
ConstIterator end() const;
/*!
* Returns true if the vector is null.
*
* \note A vector may be empty without being null.
* \see isEmpty()
*/
bool isNull() const;
/*!
* Returns true if the ByteVector is empty.
*
* \see size()
* \see isNull()
*/
bool isEmpty() const;
/*!
* Returns a CRC checksum of the byte vector's data.
*/
uint checksum() const;
/*!
* Converts the first 4 bytes of the vector to an unsigned integer.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 ==
* 0x01000000 == 1.
*
* \see fromUInt()
*/
uint toUInt(bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 8 bytes of the vector to a (signed) long long.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1,
* if false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
*
* \see fromUInt()
*/
long long toLongLong(bool mostSignificantByteFirst = true) const;
/*!
* Creates a 4 byte ByteVector based on \a value. If
* \a mostSignificantByteFirst is true, then this will operate left to right
* in building the ByteVector. For example if \a mostSignificantByteFirst is
* true then $00 00 00 01 == 0x00000001 == 1, if false, $01 00 00 00 ==
* 0x01000000 == 1.
*
* \see toUInt()
*/
static ByteVector fromUInt(uint value, bool mostSignificantByteFirst = true);
/*!
* Creates a 8 byte ByteVector based on \a value. If
* \a mostSignificantByteFirst is true, then this will operate left to right
* in building the ByteVector. For example if \a mostSignificantByteFirst is
* true then $00 00 00 01 == 0x0000000000000001 == 1, if false,
* $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
*
* \see toLongLong()
*/
static ByteVector fromLongLong(long long value, bool mostSignificantByteFirst = true);
/*!
* Returns a ByteVector based on the CString \a s.
*/
static ByteVector fromCString(const char *s, uint length = 0xffffffff);
/*!
* Returns a const refernence to the byte at \a index.
*/
const char &operator[](int index) const;
/*!
* Returns a reference to the byte at \a index.
*/
char &operator[](int index);
/*!
* Returns true if this ByteVector and \a v are equal.
*/
bool operator==(const ByteVector &v) const;
/*!
* Returns true if this ByteVector and \a v are not equal.
*/
bool operator!=(const ByteVector &v) const;
/*!
* Returns true if this ByteVector and the null terminated C string \a s
* contain the same data.
*/
bool operator==(const char *s) const;
/*!
* Returns true if this ByteVector and the null terminated C string \a s
* do not contain the same data.
*/
bool operator!=(const char *s) const;
/*!
* Returns true if this ByteVector is less than \a v. The value of the
* vectors is determined by evaluating the character from left to right, and
* in the event one vector is a superset of the other, the size is used.
*/
bool operator<(const ByteVector &v) const;
/*!
* Returns true if this ByteVector is greater than \a v.
*/
bool operator>(const ByteVector &v) const;
/*!
* Returns a vector that is \a v appended to this vector.
*/
ByteVector operator+(const ByteVector &v) const;
/*!
* Copies ByteVector \a v.
*/
ByteVector &operator=(const ByteVector &v);
/*!
* Copies ByteVector \a v.
*/
ByteVector &operator=(char c);
/*!
* Copies ByteVector \a v.
*/
ByteVector &operator=(const char *data);
/*!
* A static, empty ByteVector which is convenient and fast (since returning
* an empty or "null" value does not require instantiating a new ByteVector).
*/
static ByteVector null;
protected:
/*
* If this ByteVector is being shared via implicit sharing, do a deep copy
* of the data and separate from the shared members. This should be called
* by all non-const subclass members.
*/
void detach();
private:
class ByteVectorPrivate;
ByteVectorPrivate *d;
};
}
/*!
* \relates TagLib::ByteVector
* Streams the ByteVector \a v to the output stream \a s.
*/
std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v);
#endif

77
toolkit/tbytevectorlist.h Normal file
View File

@ -0,0 +1,77 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_BYTEVECTORLIST_H
#define TAGLIB_BYTEVECTORLIST_H
#include "tbytevector.h"
#include "tlist.h"
namespace TagLib {
//! A list of ByteVectors
/*!
* A List specialization with some handy features useful for ByteVectors.
*/
class ByteVectorList : public List<ByteVector>
{
public:
/*!
* Construct an empty ByteVectorList.
*/
ByteVectorList();
/*!
* Destroys this ByteVectorList instance.
*/
virtual ~ByteVectorList();
/*!
* Make a shallow, implicitly shared, copy of \a l. Because this is
* implicitly shared, this method is lightweight and suitable for
* pass-by-value usage.
*/
ByteVectorList(const ByteVectorList &l);
/*!
* Convert the ByteVectorList to a ByteVector separated by \a separator. By
* default a space is used.
*/
ByteVector toByteVector(const ByteVector &separator = " ") const;
/*!
* Splits the ByteVector \a v into several strings at \a pattern. This will
* not include the pattern in the returned ByteVectors.
*/
static ByteVectorList split(const ByteVector &v, const ByteVector &pattern,
int byteAlign = 1);
private:
class ByteVectorListPrivate;
ByteVectorListPrivate *d;
};
}
#endif

51
toolkit/tdebug.cpp Normal file
View File

@ -0,0 +1,51 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#include <iostream>
#include <bitset>
#include "tdebug.h"
#include "tstring.h"
using namespace TagLib;
#ifndef NDEBUG
void TagLib::debug(const String &s)
{
std::cerr << "TagLib: " << s << std::endl;
}
void TagLib::debugData(const ByteVector &v)
{
for(uint i = 0; i < v.size(); i++) {
std::cout << "*** [" << i << "] - '" << char(v[i]) << "' - int " << int(v[i])
<< std::endl;
std::bitset<8> b(v[i]);
for(int j = 0; j < 8; j++)
std::cout << i << ":" << j << " " << b.test(j) << std::endl;
std::cout << std::endl;
}
}
#endif

67
toolkit/tdebug.h Normal file
View File

@ -0,0 +1,67 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_DEBUG_H
#define TAGLIB_DEBUG_H
namespace TagLib {
class String;
class ByteVector;
#ifndef DO_NOT_DOCUMENT
#ifndef NDEBUG
/*!
* A simple function that prints debugging output to cerr if debugging is
* not disabled.
*
* \warning Do not use this outside of TagLib, it could lead to undefined
* symbols in your build if TagLib is built with NDEBUG defined and your
* application is not.
*
* \internal
*/
void debug(const String &s);
/*!
* For debugging binary data.
*
* \warning Do not use this outside of TagLib, it could lead to undefined
* symbols in your build if TagLib is built with NDEBUG defined and your
* application is not.
*
* \internal
*/
void debugData(const ByteVector &v);
#else
// Define these to an empty statement if debugging is disabled.
#define debug(x)
#define debugData(x)
#endif
#endif
}
#endif

484
toolkit/tfile.cpp Normal file
View File

@ -0,0 +1,484 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#include "tfile.h"
#include "tstring.h"
#include "tdebug.h"
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace TagLib;
class File::FilePrivate
{
public:
FilePrivate(const char *fileName) :
file(0),
name(fileName),
readOnly(true),
valid(true)
{}
~FilePrivate()
{
delete [] name;
}
FILE *file;
const char *name;
bool readOnly;
bool valid;
static const uint bufferSize = 1024;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
File::File(const char *file)
{
d = new FilePrivate(strdup(file));
d->readOnly = !isWritable(file);
d->file = fopen(file, d->readOnly ? "r" : "r+");
if(!d->file)
debug("Could not open file " + String(file));
}
File::~File()
{
if(d->file)
fclose(d->file);
delete d;
}
const char *File::name() const
{
return d->name;
}
ByteVector File::readBlock(ulong length)
{
if(!d->file) {
debug("File::readBlock() -- Invalid File");
return ByteVector::null;
}
ByteVector v(static_cast<uint>(length));
const int count = fread(v.data(), sizeof(char), length, d->file);
v.resize(count);
return v;
}
void File::writeBlock(const ByteVector &data)
{
if(!d->file)
return;
if(d->readOnly) {
debug("File::writeBlock() -- attempted to write to a file that is not writable");
return;
}
fwrite(data.data(), sizeof(char), data.size(), d->file);
}
long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
if(!d->file || pattern.size() > d->bufferSize)
return -1;
// The position in the file that the current buffer starts at.
long bufferOffset = fromOffset;
ByteVector buffer;
// These variables are used to keep track of a partial match that happens at
// the end of a buffer.
int previousPartialMatch = -1;
int beforePreviousPartialMatch = -1;
// Save the location of the current read pointer. We will restore the
// position using seek() before all returns.
long originalPosition = tell();
// Start the search at the offset.
seek(fromOffset);
// This loop is the crux of the find method. There are three cases that we
// want to account for:
//
// (1) The previously searched buffer contained a partial match of the search
// pattern and we want to see if the next one starts with the remainder of
// that pattern.
//
// (2) The search pattern is wholly contained within the current buffer.
//
// (3) The current buffer ends with a partial match of the pattern. We will
// note this for use in the next itteration, where we will check for the rest
// of the pattern.
//
// All three of these are done in two steps. First we check for the pattern
// and do things appropriately if a match (or partial match) is found. We
// then check for "before". The order is important because it gives priority
// to "real" matches.
for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
// (1) previous partial match
if(previousPartialMatch >= 0 && int(d->bufferSize) > previousPartialMatch) {
const int patternOffset = (d->bufferSize - previousPartialMatch);
if(buffer.containsAt(pattern, 0, patternOffset)) {
seek(originalPosition);
return bufferOffset - d->bufferSize + previousPartialMatch;
}
}
if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(d->bufferSize) > beforePreviousPartialMatch) {
const int beforeOffset = (d->bufferSize - beforePreviousPartialMatch);
if(buffer.containsAt(before, 0, beforeOffset)) {
seek(originalPosition);
return -1;
}
}
// (2) pattern contained in current buffer
long location = buffer.find(pattern);
if(location >= 0) {
seek(originalPosition);
return bufferOffset + location;
}
if(!before.isNull() && buffer.find(before) >= 0) {
seek(originalPosition);
return -1;
}
// (3) partial match
previousPartialMatch = buffer.endsWithPartialMatch(pattern);
if(!before.isNull())
beforePreviousPartialMatch = buffer.endsWithPartialMatch(before);
bufferOffset += d->bufferSize;
}
// Since we hit the end of the file, reset the status before continuing.
clear();
seek(originalPosition);
return -1;
}
long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
if(!d->file || pattern.size() > d->bufferSize)
return -1;
// The position in the file that the current buffer starts at.
ByteVector buffer;
// These variables are used to keep track of a partial match that happens at
// the end of a buffer.
/*
int previousPartialMatch = -1;
int beforePreviousPartialMatch = -1;
*/
// Save the location of the current read pointer. We will restore the
// position using seek() before all returns.
long originalPosition = tell();
// Start the search at the offset.
long bufferOffset;
if(fromOffset == 0) {
seek(-1 * d->bufferSize, End);
bufferOffset = tell();
}
else {
seek(fromOffset + -1 * d->bufferSize, Beginning);
bufferOffset = tell();
}
// See the notes in find() for an explanation of this algorithm.
for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
// TODO: (1) previous partial match
// (2) pattern contained in current buffer
long location = buffer.rfind(pattern);
if(location >= 0) {
seek(originalPosition);
return bufferOffset + location;
}
if(!before.isNull() && buffer.find(before) >= 0) {
seek(originalPosition);
return -1;
}
// TODO: (3) partial match
bufferOffset -= d->bufferSize;
seek(bufferOffset);
}
// Since we hit the end of the file, reset the status before continuing.
clear();
seek(originalPosition);
return -1;
}
void File::insert(const ByteVector &data, ulong start, ulong replace)
{
if(!d->file)
return;
if(data.size() == replace) {
seek(start);
writeBlock(data);
return;
}
else if(data.size() < replace) {
seek(start);
writeBlock(data);
removeBlock(start + data.size(), replace - data.size());
return;
}
// Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore
// and avoid TagLib's high level API for rendering just copying parts of
// the file that don't contain tag data.
//
// Now I'll explain the steps in this ugliness:
// First, make sure that we're working with a buffer that is longer than
// the *differnce* in the tag sizes. We want to avoid overwriting parts
// that aren't yet in memory, so this is necessary.
ulong bufferLength = bufferSize();
while(data.size() - replace > bufferLength)
bufferLength += bufferSize();
// Set where to start the reading and writing.
long readPosition = start + replace;
long writePosition = start;
ByteVector buffer;
ByteVector aboutToOverwrite(static_cast<uint>(bufferLength));
// This is basically a special case of the loop below. Here we're just
// doing the same steps as below, but since we aren't using the same buffer
// size -- instead we're using the tag size -- this has to be handled as a
// special case. We're also using File::writeBlock() just for the tag.
// That's a bit slower than using char *'s so, we're only doing it here.
seek(readPosition);
int bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
readPosition += bufferLength;
seek(writePosition);
writeBlock(data);
writePosition += data.size();
buffer = aboutToOverwrite;
// Ok, here's the main loop. We want to loop until the read fails, which
// means that we hit the end of the file.
while(bytesRead != 0) {
// Seek to the current read position and read the data that we're about
// to overwrite. Appropriately increment the readPosition.
seek(readPosition);
bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
aboutToOverwrite.resize(bytesRead);
readPosition += bufferLength;
// Check to see if we just read the last block. We need to call clear()
// if we did so that the last write succeeds.
if(ulong(bytesRead) < bufferLength)
clear();
// Seek to the write position and write our buffer. Increment the
// writePosition.
seek(writePosition);
fwrite(buffer.data(), sizeof(char), bufferLength, d->file);
writePosition += bufferLength;
// Make the current buffer the data that we read in the beginning.
buffer = aboutToOverwrite;
// Again, we need this for the last write. We don't want to write garbage
// at the end of our file, so we need to set the buffer size to the amount
// that we actually read.
bufferLength = bytesRead;
}
}
void File::removeBlock(ulong start, ulong length)
{
if(!d->file)
return;
ulong bufferLength = bufferSize();
long readPosition = start + length;
long writePosition = start;
ByteVector buffer(static_cast<uint>(bufferLength));
ulong bytesRead = true;
while(bytesRead != 0) {
seek(readPosition);
bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file);
buffer.resize(bytesRead);
readPosition += bytesRead;
// Check to see if we just read the last block. We need to call clear()
// if we did so that the last write succeeds.
if(bytesRead < bufferLength)
clear();
seek(writePosition);
fwrite(buffer.data(), sizeof(char), bytesRead, d->file);
writePosition += bytesRead;
}
truncate(writePosition);
}
bool File::readOnly() const
{
return d->readOnly;
}
bool File::isReadable(const char *file)
{
return access(file, R_OK) == 0;
}
bool File::isOpen() const
{
return d->file;
}
bool File::isValid() const
{
return d->file && d->valid;
}
void File::seek(long offset, Position p)
{
if(!d->file) {
debug("File::seek() -- trying to seek in a file that isn't opened.");
return;
}
switch(p) {
case Beginning:
fseek(d->file, offset, SEEK_SET);
break;
case Current:
fseek(d->file, offset, SEEK_CUR);
break;
case End:
fseek(d->file, offset, SEEK_END);
break;
}
}
void File::clear()
{
clearerr(d->file);
}
long File::tell() const
{
return ftell(d->file);
}
long File::length()
{
if(!d->file)
return 0;
long curpos = tell();
seek(0, End);
long endpos = tell();
seek(curpos, Beginning);
return endpos;
}
bool File::isWritable(const char *file)
{
return access(file, W_OK) == 0;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void File::setValid(bool valid)
{
d->valid = valid;
}
void File::truncate(long length)
{
ftruncate(fileno(d->file), length);
}
TagLib::uint File::bufferSize()
{
return FilePrivate::bufferSize;
}

240
toolkit/tfile.h Normal file
View File

@ -0,0 +1,240 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_FILE_H
#define TAGLIB_FILE_H
#include "taglib.h"
#include "tbytevector.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 File
{
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
};
/*!
* Destroys this File instance.
*/
virtual ~File();
/*!
* Returns the file name in the local file system encoding.
*/
const char *name() const;
/*!
* Returns a pointer to this file's tag. This should be reimplemented in
* the concrete subclasses.
*/
virtual Tag *tag() const = 0;
/*!
* Returns a pointer to this file's audio properties. This should be
* reimplemented in the concrete subclasses. If no audio properties were
* read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const = 0;
/*!
* Save the file and its associated tags. This should be reimplemented in
* the concrete subclasses.
*/
virtual void save() = 0;
/*!
* Reads a block of size \a length at the current get pointer.
*/
ByteVector readBlock(ulong length);
/*!
* Attempts to write the block \a data at the current get pointer. If the
* file is currently only opened read only -- i.e. readOnly() returns true --
* this attempts to reopen the file in read/write mode.
*
* \note This should be used instead of using the streaming output operator
* for a ByteVector. And even this function is significantly slower than
* doing output with a char[].
*/
void writeBlock(const ByteVector &data);
/*!
* Returns the offset in the file that \a pattern occurs at or -1 if it can
* not be found. If \a before is set, the search will only continue until the
* pattern \a before is found. This is useful for tagging purposes to search
* for a tag before the synch frame.
*
* Searching starts at \a fromOffset, which defaults to the beginning of the
* file.
*
* \note This has the practial limitation that \a pattern can not be longer
* than the buffer size used by readBlock(). Currently this is 1024 bytes.
*/
long find(const ByteVector &pattern,
long fromOffset = 0,
const ByteVector &before = ByteVector::null);
/*!
* Returns the offset in the file that \a pattern occurs at or -1 if it can
* not be found. If \a before is set, the search will only continue until the
* pattern \a before is found. This is useful for tagging purposes to search
* for a tag before the synch frame.
*
* Searching starts at \a fromOffset and proceeds from the that point to the
* beginning of the file and defaults to the end of the file.
*
* \note This has the practial limitation that \a pattern can not be longer
* than the buffer size used by readBlock(). Currently this is 1024 bytes.
*/
long rfind(const ByteVector &pattern,
long fromOffset = 0,
const ByteVector &before = ByteVector::null);
/*!
* Insert \a data at position \a start in the file overwriting \a replace
* bytes of the original content.
*
* \note This method is slow since it requires rewriting all of the file
* after the insertion point.
*/
void insert(const ByteVector &data, ulong start = 0, ulong replace = 0);
/*!
* Removes a block of the file starting a \a start and continuing for
* \a length bytes.
*
* \note This method is slow since it involves rewriting all of the file
* after the removed portion.
*/
void removeBlock(ulong start = 0, ulong length = 0);
/*!
* Returns true if the file is read only (or if the file can not be opened).
*/
bool readOnly() const;
/*!
* Since the file can currently only be opened as an argument to the
* constructor (sort-of by design), this returns if that open succeeded.
*/
bool isOpen() const;
/*!
* Returns true if the file is open and readble and valid information for
* the Tag and / or AudioProperties was found.
*/
bool isValid() 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 withing the file.
*/
long tell() const;
/*!
* Returns the length of the file.
*/
long length();
/*!
* Returns true if \a file can be opened for reading. If the file does not
* exist, this will return false.
*/
static bool isReadable(const char *file);
/*!
* Returns true if \a file can be opened for writing.
*/
static bool isWritable(const char *name);
protected:
/*!
* Construct a File object and opens the \a file. \a file should be a
* be a C-string in the local file system encoding.
*
* \note Constructor is protected since this class should only be
* instantiated through subclasses.
*/
File(const char *file);
/*!
* Marks the file as valid or invalid.
*
* \see isValid()
*/
void setValid(bool valid);
/*!
* Truncates the file to a \a length.
*/
void truncate(long length);
/*!
* Returns the buffer size that is used for internal buffering.
*/
static uint bufferSize();
private:
File(const File &);
File &operator=(const File &);
class FilePrivate;
FilePrivate *d;
};
}
#endif

234
toolkit/tlist.h Normal file
View File

@ -0,0 +1,234 @@
/***************************************************************************
copyright : (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_LIST_H
#define TAGLIB_LIST_H
#include "taglib.h"
#include <list>
namespace TagLib {
//! A generic, implicitly shared list.
/*!
* This is basic generic list that's somewhere between a std::list and a
* QValueList. This class is implicitly shared. For example:
*
* \code
*
* TagLib::List<int> l = someOtherIntList;
*
* \endcode
*
* The above example is very cheap. This also makes lists suitable for the
* return types of functions. The above example will just copy a pointer rather
* than copying the data in the list. When your \e shared list's data changes,
* only \e then will the data be copied.
*/
template <class T> class List
{
public:
#ifndef DO_NOT_DOCUMENT
typedef typename std::list<T>::iterator Iterator;
typedef typename std::list<T>::const_iterator ConstIterator;
#endif
/*!
* Constructs an empty list.
*/
List();
/*!
* Make a shallow, implicitly shared, copy of \a l. Because this is
* implicitly shared, this method is lightweight and suitable for
* pass-by-value usage.
*/
List(const List<T> &l);
/*!
* Destroys this List instance. If auto deletion is enabled and this list
* contains a pointer type all of the memebers are also deleted.
*/
virtual ~List();
/*!
* Returns an STL style iterator to the beginning of the list. See
* std::list::const_iterator for the semantics.
*/
Iterator begin();
/*!
* Returns an STL style constant iterator to the beginning of the list. See
* std::list::iterator for the semantics.
*/
ConstIterator begin() const;
/*!
* Returns an STL style iterator to the end of the list. See
* std::list::iterator for the semantics.
*/
Iterator end();
/*!
* Returns an STL style constant iterator to the end of the list. See
* std::list::const_iterator for the semantics.
*/
ConstIterator end() const;
/*!
* Inserts a copy of \a value before \a it.
*/
void insert(Iterator it, const T &value);
/*!
* Inserts the \a value into the list. This assumes that the list is
* currently sorted. If \a unique is true then the value will not
* be inserted if it is already in the list.
*/
void sortedInsert(const T &value, bool unique = false);
/*!
* Appends \a item to the end of the list.
*/
void append(const T &item);
/*!
* Appends all of the values in \a l to the end of the list.
*/
void append(const List<T> &l);
/*!
* Clears the list. If auto deletion is enabled and this list contains a
* pointer type the members are also deleted.
*
* \see setAutoDelete()
*/
void clear();
/*!
* Returns the number of elements in the list.
*/
uint size() const;
bool isEmpty() const;
/*!
* Find the first occurance of \a value.
*/
Iterator find(const T &value);
/*!
* Find the first occurance of \a value.
*/
ConstIterator find(const T &value) const;
/*!
* Returns true if the list contains \a value.
*/
bool contains(const T &value) const;
/*!
* Erase the item at \a it from the list.
*/
void erase(Iterator it);
/*!
* Returns a reference to the first item in the list.
*/
const T &front() const;
/*!
* Returns a reference to the first item in the list.
*/
T &front();
/*!
* Returns a reference to the last item in the list.
*/
const T &back() const;
/*!
* Returns a reference to the last item in the list.
*/
T &back();
/*!
* Auto delete the members of the list when the last reference to the list
* passes out of scope. This will have no effect on lists which do not
* contain a pointer type.
*
* \note This relies on partial template instantiation -- most modern C++
* compilers should now support this.
*/
void setAutoDelete(bool autoDelete);
/*!
* Returns a reference to item \a i in the list.
*
* \warning This method is slow. Use iterators to loop through the list.
*/
T &operator[](uint i);
/*!
* Returns a const reference to item \a i in the list.
*
* \warning This method is slow. Use iterators to loop through the list.
*/
const T &operator[](uint i) const;
/*!
* Make a shallow, implicitly shared, copy of \a l. Because this is
* implicitly shared, this method is lightweight and suitable for
* pass-by-value usage.
*/
List<T> &operator=(const List<T> &l);
/*!
* Compares this list with \a l and returns true if all of the elements are
* the same.
*/
bool operator==(const List<T> &l) const;
protected:
/*
* If this List is being shared via implicit sharing, do a deep copy of the
* data and separate from the shared members. This should be called by all
* non-const subclass members.
*/
void detach();
private:
#ifndef DO_NOT_DOCUMENT
template <class TP> class ListPrivate;
ListPrivate<T> *d;
#endif
};
}
// Since GCC doesn't support the "export" keyword, we have to include the
// implementation.
#include "tlist.tcc"
#endif

294
toolkit/tlist.tcc Normal file
View File

@ -0,0 +1,294 @@
/***************************************************************************
copyright : (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#include <tdebug.h>
#include <algorithm>
namespace TagLib {
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
// The functionality of List<T>::setAutoDelete() is implemented here partial
// template specialization. This is implemented in such a way that calling
// setAutoDelete() on non-pointer types will simply have no effect.
// A base for the generic and specialized private class types. New
// non-templatized members should be added here.
class ListPrivateBase : public RefCounter
{
public:
ListPrivateBase() : autoDelete(false) {}
bool autoDelete;
};
// A generic implementation
template <class T>
template <class TP> class List<T>::ListPrivate : public ListPrivateBase
{
public:
ListPrivate() : ListPrivateBase() {}
ListPrivate(const std::list<TP> &l) : ListPrivateBase(), list(l) {}
void clear() {
list.clear();
}
std::list<TP> list;
};
// A partial specialization for all pointer types that implements the
// setAutoDelete() functionality.
template <class T>
template <class TP> class List<T>::ListPrivate<TP *> : public ListPrivateBase
{
public:
ListPrivate() : ListPrivateBase() {}
ListPrivate(const std::list<TP *> &l) : ListPrivateBase(), list(l) {}
~ListPrivate() {
clear();
}
void clear() {
if(autoDelete) {
typename std::list<TP *>::const_iterator it = list.begin();
for(; it != list.end(); ++it)
delete *it;
}
list.clear();
}
std::list<TP *> list;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
template <class T>
List<T>::List()
{
d = new ListPrivate<T>;
}
template <class T>
List<T>::List(const List<T> &l) : d(l.d)
{
d->ref();
}
template <class T>
List<T>::~List()
{
if(d->deref())
delete d;
}
template <class T>
typename List<T>::Iterator List<T>::begin()
{
detach();
return d->list.begin();
}
template <class T>
typename List<T>::ConstIterator List<T>::begin() const
{
return d->list.begin();
}
template <class T>
typename List<T>::Iterator List<T>::end()
{
detach();
return d->list.end();
}
template <class T>
typename List<T>::ConstIterator List<T>::end() const
{
return d->list.end();
}
template <class T>
void List<T>::insert(Iterator it, const T &item)
{
detach();
d->list.insert(it, item);
}
template <class T>
void List<T>::sortedInsert(const T &value, bool unique)
{
detach();
Iterator it = begin();
while(*it < value && it != end())
++it;
if(unique && it != end() && *it == value)
return;
insert(it, value);
}
template <class T>
void List<T>::append(const T &item)
{
detach();
d->list.push_back(item);
}
template <class T>
void List<T>::append(const List<T> &l)
{
detach();
d->list.insert(d->list.end(), l.begin(), l.end());
}
template <class T>
void List<T>::clear()
{
detach();
d->clear();
}
template <class T>
TagLib::uint List<T>::size() const
{
return d->list.size();
}
template <class T>
bool List<T>::isEmpty() const
{
return d->list.empty();
}
template <class T>
typename List<T>::Iterator List<T>::find(const T &value)
{
return std::find(d->list.begin(), d->list.end(), value);
}
template <class T>
typename List<T>::ConstIterator List<T>::find(const T &value) const
{
return std::find(d->list.begin(), d->list.end(), value);
}
template <class T>
bool List<T>::contains(const T &value) const
{
return std::find(d->list.begin(), d->list.end(), value) != d->list.end();
}
template <class T>
void List<T>::erase(Iterator it)
{
d->list.erase(it);
}
template <class T>
const T &List<T>::front() const
{
return d->list.front();
}
template <class T>
T &List<T>::front()
{
detach();
return d->list.front();
}
template <class T>
const T &List<T>::back() const
{
return d->list.back();
}
template <class T>
void List<T>::setAutoDelete(bool autoDelete)
{
d->autoDelete = autoDelete;
}
template <class T>
T &List<T>::back()
{
detach();
return d->list.back();
}
template <class T>
T &List<T>::operator[](uint i)
{
Iterator it = d->list.begin();
for(uint j = 0; j < i; j++)
++it;
return *it;
}
template <class T>
const T &List<T>::operator[](uint i) const
{
ConstIterator it = d->list.begin();
for(uint j = 0; j < i; j++)
++it;
return *it;
}
template <class T>
List<T> &List<T>::operator=(const List<T> &l)
{
if(&l == this)
return *this;
if(d->deref())
delete d;
d = l.d;
d->ref();
return *this;
}
template <class T>
bool List<T>::operator==(const List<T> &l) const
{
return d->list == l.d->list;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
template <class T>
void List<T>::detach()
{
if(d->count() > 1) {
d->deref();
d = new ListPrivate<T>(d->list);
}
}
} // namespace TagLib

162
toolkit/tmap.h Normal file
View File

@ -0,0 +1,162 @@
/***************************************************************************
copyright : (C) 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_MAP_H
#define TAGLIB_MAP_H
#include "taglib.h"
#include <map>
namespace TagLib {
//! A generic, implicitly shared map.
/*!
* This implements a standard map container that associates a key with a value
* and has fast key-based lookups. This map is also implicitly shared making
* it suitable for pass-by-value usage.
*/
template <class Key, class T> class Map
{
public:
#ifndef DO_NOT_DOCUMENT
typedef typename std::map<Key, T>::iterator Iterator;
typedef typename std::map<Key, T>::const_iterator ConstIterator;
#endif
/*!
* Constructs an empty Map.
*/
Map();
/*!
* Make a shallow, implicitly shared, copy of \a m. Because this is
* implicitly shared, this method is lightweight and suitable for
* pass-by-value usage.
*/
Map(const Map<Key, T> &m);
/*!
* Destroys this instance of the Map.
*/
virtual ~Map();
/*!
* Returns an STL style iterator to the beginning of the map. See
* std::map::iterator for the semantics.
*/
Iterator begin();
/*!
* Returns an STL style iterator to the beginning of the map. See
* std::map::const_iterator for the semantics.
*/
ConstIterator begin() const;
/*!
* Returns an STL style iterator to the end of the map. See
* std::map::iterator for the semantics.
*/
Iterator end();
/*!
* Returns an STL style iterator to the end of the map. See
* std::map::const_iterator for the semantics.
*/
ConstIterator end() const;
/*!
* Inserts \a value under \a key in the map. If a value for \a key already
* exists it will be overwritten.
*/
void insert(const Key &key, const T &value);
/*!
* Removes all of the elements from elements from the map. This however
* will not delete pointers if the mapped type is a pointer type.
*/
void clear();
/*!
* The number of elements in the map.
*
* \see isEmpty()
*/
uint size() const;
/*!
* Returns true if the map is empty.
*
* \see size()
*/
bool isEmpty() const;
/*!
* Returns true if the map contains an instance of \a key.
*/
bool contains(const Key &key) const;
/*!
* Returns a reference to the value associated with \a key.
*
* \note This has undefined behavior if the key is not present in the map.
*/
const T &operator[](const Key &key) const;
/*!
* Returns a reference to the value associated with \a key.
*
* \note This has undefined behavior if the key is not present in the map.
*/
T &operator[](const Key &key);
/*!
* Make a shallow, implicitly shared, copy of \a m. Because this is
* implicitly shared, this method is lightweight and suitable for
* pass-by-value usage.
*/
Map<Key, T> &operator=(const Map<Key, T> &m);
protected:
/*
* If this List is being shared via implicit sharing, do a deep copy of the
* data and separate from the shared members. This should be called by all
* non-const subclass members.
*/
void detach();
private:
#ifndef DO_NOT_DOCUMENT
template <class KeyP, class TP> class MapPrivate;
MapPrivate<Key, T> *d;
#endif
};
}
// Since GCC doesn't support the "export" keyword, we have to include the
// implementation.
#include "tmap.tcc"
#endif

154
toolkit/tmap.tcc Normal file
View File

@ -0,0 +1,154 @@
/***************************************************************************
copyright : (C) 2002 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
namespace TagLib {
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
template <class Key, class T>
template <class KeyP, class TP> class Map<Key, T>::MapPrivate : public RefCounter
{
public:
MapPrivate() : RefCounter() {}
MapPrivate(const std::map<KeyP, TP> &m) : RefCounter(), map(m) {}
std::map<KeyP, TP> map;
};
template <class Key, class T>
Map<Key, T>::Map()
{
d = new MapPrivate<Key, T>;
}
template <class Key, class T>
Map<Key, T>::Map(const Map<Key, T> &m) : d(m.d)
{
d->ref();
}
template <class Key, class T>
Map<Key, T>::~Map()
{
if(d->deref())
delete(d);
}
template <class Key, class T>
typename Map<Key, T>::Iterator Map<Key, T>::begin()
{
detach();
return d->map.begin();
}
template <class Key, class T>
typename Map<Key, T>::ConstIterator Map<Key, T>::begin() const
{
return d->map.begin();
}
template <class Key, class T>
typename Map<Key, T>::Iterator Map<Key, T>::end()
{
detach();
return d->map.end();
}
template <class Key, class T>
typename Map<Key, T>::ConstIterator Map<Key, T>::end() const
{
return d->map.end();
}
template <class Key, class T>
void Map<Key, T>::insert(const Key &key, const T &value)
{
detach();
std::pair<Key, T> item(key, value);
d->map.insert(item);
}
template <class Key, class T>
void Map<Key, T>::clear()
{
detach();
d->map.clear();
}
template <class Key, class T>
bool Map<Key, T>::isEmpty() const
{
return d->map.empty();
}
template <class Key, class T>
bool Map<Key, T>::contains(const Key &key) const
{
return d->map.find(key) != d->map.end();
}
template <class Key, class T>
TagLib::uint Map<Key, T>::size() const
{
return d->map.size();
}
template <class Key, class T>
const T &Map<Key, T>::operator[](const Key &key) const
{
return d->map[key];
}
template <class Key, class T>
T &Map<Key, T>::operator[](const Key &key)
{
return d->map[key];
}
template <class Key, class T>
Map<Key, T> &Map<Key, T>::operator=(const Map<Key, T> &m)
{
if(&m == this)
return *this;
if(d->deref())
delete(d);
d = m.d;
d->ref();
return *this;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
template <class Key, class T>
void Map<Key, T>::detach()
{
if(d->count() > 1) {
d->deref();
d = new MapPrivate<Key, T>(d->map);
}
}
} // namespace TagLib

652
toolkit/tstring.cpp Normal file
View File

@ -0,0 +1,652 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#include "tstring.h"
#include "unicode.h"
#include "tdebug.h"
#include <iostream>
namespace TagLib {
inline unsigned short byteSwap(unsigned short x)
{
return ((x) >> 8) & 0xff | ((x) & 0xff) << 8;
}
inline unsigned short combine(unsigned char c1, unsigned char c2)
{
return (c1 << 8) | c2;
}
}
using namespace TagLib;
class String::StringPrivate : public RefCounter
{
public:
StringPrivate(const wstring &s) :
RefCounter(),
data(s),
CString(0) {}
StringPrivate() :
RefCounter(),
CString(0) {}
~StringPrivate() {
delete [] CString;
}
wstring data;
/*!
* This is only used to hold the a pointer to the most recent value of
* toCString.
*/
char *CString;
};
String String::null;
////////////////////////////////////////////////////////////////////////////////
String::String()
{
d = new StringPrivate;
}
String::String(const String &s) : d(s.d)
{
d->ref();
}
String::String(const std::string &s, Type t)
{
d = new StringPrivate;
if(t == UTF16 || t == UTF16BE) {
debug("String::String() -- A std::string should not contain UTF16.");
return;
}
for(std::string::const_iterator it = s.begin(); it != s.end(); it++)
d->data += uchar(*it);
prepare(t);
}
String::String(const wstring &s, Type t)
{
d = new StringPrivate(s);
prepare(t);
}
String::String(const wchar_t *s, Type t)
{
d = new StringPrivate(s);
prepare(t);
}
String::String(const char *s, Type t)
{
d = new StringPrivate;
if(t == UTF16 || t == UTF16BE) {
debug("String::String() -- A const char * should not contain UTF16.");
return;
}
for(int i = 0; s[i] != 0; i++)
d->data += uchar(s[i]);
prepare(t);
}
String::String(wchar_t c, Type t)
{
d = new StringPrivate;
d->data += c;
prepare(t);
}
String::String(char c, Type t)
{
d = new StringPrivate;
if(t == UTF16 || t == UTF16BE) {
debug("String::String() -- A std::string should not contain UTF16.");
return;
}
d->data += uchar(c);
prepare(t);
}
String::String(const ByteVector &v, Type t)
{
d = new StringPrivate;
if(t == Latin1 || t == UTF8) {
for(uint i = 0; i < v.size() && v[i]; i++)
d->data += uchar(v[i]);
}
else {
for(uint i = 0; i + 1 < v.size() && combine(v[i], v[i + 1]); i += 2)
d->data += combine(v[i], v[i + 1]);
}
prepare(t);
}
////////////////////////////////////////////////////////////////////////////////
String::~String()
{
if(d->deref())
delete d;
}
std::string String::to8Bit(bool unicode) const
{
std::string s;
s.resize(d->data.size());
if(!unicode) {
std::string::iterator targetIt = s.begin();
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
*targetIt = char(*it);
++targetIt;
}
return s;
}
const int outputBufferSize = d->data.size() * 3 + 1;
Unicode::UTF16 *sourceBuffer = new Unicode::UTF16[d->data.size() + 1];
Unicode::UTF8 *targetBuffer = new Unicode::UTF8[outputBufferSize];
for(unsigned int i = 0; i < d->data.size(); i++)
sourceBuffer[i] = Unicode::UTF16(d->data[i]);
const Unicode::UTF16 *source = sourceBuffer;
Unicode::UTF8 *target = targetBuffer;
Unicode::ConversionResult result =
Unicode::ConvertUTF16toUTF8(&source, sourceBuffer + d->data.size(),
&target, targetBuffer + outputBufferSize,
Unicode::lenientConversion);
if(result != Unicode::conversionOK)
debug("String::to8Bit() - Unicode conversion error.");
int newSize = target - targetBuffer;
s.resize(newSize);
targetBuffer[newSize] = 0;
s = (char *) targetBuffer;
delete [] sourceBuffer;
delete [] targetBuffer;
return s;
}
const char *String::toCString(bool unicode) const
{
delete [] d->CString;
std::string buffer = to8Bit(unicode);
d->CString = new char[buffer.size() + 1];
strcpy(d->CString, buffer.c_str());
return d->CString;
}
int String::find(const String &s, int offset) const
{
wstring::size_type position = d->data.find(s.d->data, offset);
if(position != wstring::npos)
return position;
else
return -1;
}
String String::substr(uint position, uint n) const
{
if(n > position + d->data.size())
n = d->data.size() - position;
String s;
s.d->data = d->data.substr(position, n);
return s;
}
String &String::append(const String &s)
{
detach();
d->data += s.d->data;
return *this;
}
String String::upper() const
{
String s;
static int shift = 'A' - 'a';
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) {
if(*it >= 'a' && *it <= 'z')
s.d->data.push_back(*it + shift);
else
s.d->data.push_back(*it);
}
return s;
}
TagLib::uint String::size() const
{
return d->data.size();
}
bool String::isEmpty() const
{
return d->data.size() == 0;
}
bool String::isNull() const
{
return d == null.d;
}
ByteVector String::data(Type t) const
{
ByteVector v;
switch(t) {
case Latin1:
{
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
v.append(char(*it));
break;
}
case UTF8:
{
std::string s = to8Bit(true);
v.setData(s.c_str(), s.length());
break;
}
case UTF16:
{
// Assume that if we're doing UTF16 and not UTF16BE that we want little
// endian encoding. (Byte Order Mark)
v.append(char(0xff));
v.append(char(0xfe));
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
char c1 = *it & 0xff;
char c2 = *it >> 8;
v.append(c1);
v.append(c2);
}
}
case UTF16BE:
{
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
char c1 = *it >> 8;
char c2 = *it & 0xff;
v.append(c2);
v.append(c1);
}
break;
}
}
return v;
}
int String::toInt() const
{
int value = 0;
bool negative = d->data[0] == '-';
uint i = negative ? 1 : 0;
for(; i < d->data.size() && d->data[i] >= '0' && d->data[i] <= '9'; i++)
value = value * 10 + (d->data[i] - '0');
if(negative)
value = value * -1;
return value;
}
String String::stripWhiteSpace() const
{
wstring::const_iterator begin = d->data.begin();
wstring::const_iterator end = d->data.end();
while(*begin == '\t' || *begin == '\n' || *begin == '\f' ||
*begin == '\r' || *begin == ' ' && begin != end)
{
++begin;
}
if(begin == end)
return null;
// There must be at least one non-whitespace charater here for us to have
// gotten this far, so we should be safe not doing bounds checking.
do {
--end;
} while(*end == '\t' || *end == '\n' ||
*end == '\f' || *end == '\r' || *end == ' ');
return String(wstring(begin, end + 1));
}
String String::number(int n) // static
{
if(n == 0)
return String("0");
String charStack;
bool negative = n < 0;
if(negative)
n = n * -1;
while(n > 0) {
int remainder = n % 10;
charStack += char(remainder + '0');
n = (n - remainder) / 10;
}
String s;
if(negative)
s += '-';
for(int i = charStack.d->data.size() - 1; i >= 0; i--)
s += charStack.d->data[i];
return s;
}
bool String::operator==(const String &s) const
{
return d == s.d || d->data == s.d->data;
}
String &String::operator+=(const String &s)
{
detach();
d->data += s.d->data;
return *this;
}
String &String::operator+=(const wchar_t *s)
{
detach();
d->data += s;
return *this;
}
String &String::operator+=(const char *s)
{
detach();
for(int i = 0; s[i] != 0; i++)
d->data += uchar(s[i]);
return *this;
}
String &String::operator+=(wchar_t c)
{
detach();
d->data += c;
return *this;
}
String &String::operator+=(char c)
{
d->data += uchar(c);
return *this;
}
String &String::operator=(const String &s)
{
if(&s == this)
return *this;
if(d->deref())
delete d;
d = s.d;
d->ref();
return *this;
}
String &String::operator=(const std::string &s)
{
if(d->deref())
delete d;
d = new StringPrivate;
d->data.resize(s.size());
wstring::iterator targetIt = d->data.begin();
for(std::string::const_iterator it = s.begin(); it != s.end(); it++) {
*targetIt = uchar(*it);
++targetIt;
}
return *this;
}
String &String::operator=(const wstring &s)
{
if(d->deref())
delete d;
d = new StringPrivate(s);
return *this;
}
String &String::operator=(const wchar_t *s)
{
if(d->deref())
delete d;
d = new StringPrivate(s);
return *this;
}
String &String::operator=(char c)
{
if(d->deref())
delete d;
d = new StringPrivate;
d->data += uchar(c);
return *this;
}
String &String::operator=(wchar_t c)
{
if(d->deref())
delete d;
d = new StringPrivate;
d->data += c;
return *this;
}
String &String::operator=(const char *s)
{
if(d->deref())
delete d;
d = new StringPrivate;
for(int i = 0; s[i] != 0; i++)
d->data += uchar(s[i]);
return *this;
}
String &String::operator=(const ByteVector &v)
{
if(d->deref())
delete d;
d = new StringPrivate;
d->data.resize(v.size());
wstring::iterator targetIt = d->data.begin();
uint i = 0;
for(; i < v.size() && v[i]; i++) {
*targetIt = uchar(v[i]);
++targetIt;
}
// If we hit a null in the ByteVector, shrink the string again.
d->data.resize(i);
return *this;
}
bool String::operator<(const String &s) const
{
return d->data < s.d->data;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void String::detach()
{
if(d->count() > 1) {
d->deref();
d = new StringPrivate(d->data);
}
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void String::prepare(Type t)
{
switch(t) {
case UTF16:
{
if(d->data.size() > 1) {
bool swap = d->data[0] != 0xfeff;
d->data.erase(d->data.begin(), d->data.begin() + 1);
if(swap) {
for(uint i = 0; i < d->data.size(); i++)
d->data[i] = byteSwap((unsigned short)d->data[i]);
}
}
else {
debug("String::prepare() - Invalid UTF16 string.");
d->data.erase(d->data.begin(), d->data.end());
}
break;
}
case UTF8:
{
int bufferSize = d->data.size() + 1;
Unicode::UTF8 *sourceBuffer = new Unicode::UTF8[bufferSize];
Unicode::UTF16 *targetBuffer = new Unicode::UTF16[bufferSize];
unsigned int i = 0;
for(; i < d->data.size(); i++)
sourceBuffer[i] = Unicode::UTF8(d->data[i]);
sourceBuffer[i] = 0;
const Unicode::UTF8 *source = sourceBuffer;
Unicode::UTF16 *target = targetBuffer;
Unicode::ConversionResult result =
Unicode::ConvertUTF8toUTF16(&source, sourceBuffer + bufferSize,
&target, targetBuffer + bufferSize,
Unicode::lenientConversion);
if(result != Unicode::conversionOK)
debug("String::prepare() - Unicode conversion error.");
int newSize = target - targetBuffer - 1;
d->data.resize(newSize);
for(int i = 0; i < newSize; i++)
d->data[i] = targetBuffer[i];
delete [] sourceBuffer;
delete [] targetBuffer;
}
default:
break;
}
}
////////////////////////////////////////////////////////////////////////////////
// related functions
////////////////////////////////////////////////////////////////////////////////
const TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2)
{
String s(s1);
s.append(s2);
return s;
}
const TagLib::String operator+(const char *s1, const TagLib::String &s2)
{
String s(s1);
s.append(s2);
return s;
}
const TagLib::String operator+(const TagLib::String &s1, const char *s2)
{
String s(s1);
s.append(s2);
return s;
}
std::ostream &operator<<(std::ostream &s, const String &str)
{
s << str.to8Bit();
return s;
}

372
toolkit/tstring.h Normal file
View File

@ -0,0 +1,372 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_STRING_H
#define TAGLIB_STRING_H
#include "taglib.h"
#include "tbytevector.h"
#include <string>
/*!
* Converts a TagLib::String to a QString without a requirement to link to Qt.
*/
#define QStringToTString(s) TagLib::String(s.utf8().data(), TagLib::String::UTF8)
/*!
* Converts a TagLib::String to a QString without a requirement to link to Qt.
*/
#define TStringToQString(s) QString::fromUtf8(s.toCString(true))
namespace TagLib {
//! A \e wide string class suitable for unicode.
/*!
* This is an implicitly shared \e wide string. For storage it uses
* TagLib::wstring, but as this is an <i>implementation detail</i> this of
* course could change. Strings are stored internally as UTF-16BE. (Without
* the BOM (Byte Order Mark)
*
* The use of implicit sharing means that copying a string is cheap, the only
* \e cost comes into play when the copy is modified. Prior to that the string
* just has a pointer to the data of the \e parent String. This also makes
* this class suitable as a function return type.
*
* In addition to adding implicit sharing, this class keeps track of four
* possible encodings, which are the four supported by the ID3v2 standard.
*/
class String
{
public:
/**
* The four types of string encodings supported by the ID3v2 specification.
* ID3v1 is assumed to be Latin1 and Ogg Vorbis comments use UTF8.
*/
enum Type {
/*!
* IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
*/
Latin1 = 0,
/*!
* UTF16 with a <i>byte order mark</i>. 16 bit characters.
*/
UTF16 = 1,
/*!
* UTF16 <i>big endian</i>. 16 bit characters. This is the encoding used
* internally by TagLib.
*/
UTF16BE = 2,
/*!
* UTF8 encoding. Characters are usually 8 bits but can be up to 32.
*/
UTF8 = 3
};
/*!
* Constructs an empty String.
*/
String();
/*!
* Make a shallow, implicitly shared, copy of \a s. Because this is
* implicitly shared, this method is lightweight and suitable for
* pass-by-value usage.
*/
String(const String &s);
/*!
* Makes a deep copy of the data in \a s.
*
* \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
* used with other codecs it will simply print a warning and exit.
*/
String(const std::string &s, Type t = Latin1);
/*!
* Makes a deep copy of the data in \a s.
*/
String(const wstring &s, Type t = UTF16BE);
/*!
* Makes a deep copy of the data in \a s.
*/
String(const wchar_t *s, Type t = UTF16BE);
/*!
* Makes a deep copy of the data in \a c.
*
* \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
* used with other codecs it will simply print a warning and exit.
*/
String(char c, Type t = Latin1);
/*!
* Makes a deep copy of the data in \a c.
*/
String(wchar_t c, Type t = Latin1);
/*!
* Makes a deep copy of the data in \a s.
*
* \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
* used with other codecs it will simply print a warning and exit.
*/
String(const char *s, Type t = Latin1);
/*!
* Makes a deep copy of the data in \a s.
*
* \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
* used with other codecs it will simply print a warning and exit.
*/
String(const ByteVector &v, Type t = Latin1);
/*!
* Destroys this String instance.
*/
virtual ~String();
/*!
* If \a unicode if false (the default) this will return a \e Latin1 encoded
* std::string. If it is true the returned std::wstring will be UTF-8
* encoded.
*/
std::string to8Bit(bool unicode = false) const;
/*!
* Creates and returns a C-String based on the data. This string is still
* owned by the String (class) and as such should not be deleted by the user.
*
* If \a unicode if false (the default) this string will be encoded in
* \e Latin1. If it is true the returned C-String will be UTF-8 encoded.
*
* This string remains valid until the String instance is destroyed or
* another export method is called.
*
* \warning This however has the side effect that this C-String will remain
* in memory <b>in addition to</b> other memory that is consumed by the
* String instance. So, this method should not be used on large strings or
* where memory is critical.
*/
const char *toCString(bool unicode = false) const;
/*!
* Finds the first occurance of pattern \a s in this string starting from
* \a offset. If the pattern is not found, -1 is returned.
*/
int find(const String &s, int offset = 0) const;
/*!
* Extract a substring from this string starting at \a position and
* continuing for \a n characters.
*/
String substr(uint position, uint n = 0xffffffff) const;
/*!
* Append \a s to the current string and return a reference to the current
* string.
*/
String &append(const String &s);
/*!
* Returns an upper case version of the string.
*
* \warning This only works for the characters in US-ASCII, i.e. A-Z.
*/
String upper() const;
/*!
* Returns the size of the string.
*/
uint size() const;
/*!
* Returns true if the string is empty.
*
* \see isNull()
*/
bool isEmpty() const;
/*!
* Returns true if this string is null -- i.e. it is a copy of the
* String::null string.
*
* \note A string can be empty and not null.
* \see isEmpty()
*/
bool isNull() const;
/*!
* Returns a ByteVector containing the string's data. If \a t is Latin1 or
* UTF8, this will return a vector of 8 bit characters, otherwise it will use
* 16 bit characters.
*/
ByteVector data(Type t) const;
/*!
* Convert the string to an integer.
*/
int toInt() const;
/*!
* Returns a string with the leading and trailing whitespace stripped.
*/
String stripWhiteSpace() const;
/*!
* Converts the base-10 integer \a n to a string.
*/
static String number(int n);
/*!
* Compares each character of the String with each character of \a s and
* returns true if the strings match.
*/
bool operator==(const String &s) const;
/*!
* Appends \a s to the end of the String.
*/
String &operator+=(const String &s);
/*!
* Appends \a s to the end of the String.
*/
String &operator+=(const wchar_t* s);
/*!
* Appends \a s to the end of the String.
*/
String &operator+=(const char* s);
/*!
* Appends \a s to the end of the String.
*/
String &operator+=(wchar_t c);
/*!
* Appends \a c to the end of the String.
*/
String &operator+=(char c);
/*!
* Performs a shallow, implicitly shared, copy of \a s, overwriting the
* String's current data.
*/
String &operator=(const String &s);
/*!
* Performs a deep copy of the data in \a s.
*/
String &operator=(const std::string &s);
/*!
* Performs a deep copy of the data in \a s.
*/
String &operator=(const wstring &s);
/*!
* Performs a deep copy of the data in \a s.
*/
String &operator=(const wchar_t *s);
/*!
* Performs a deep copy of the data in \a s.
*/
String &operator=(char c);
/*!
* Performs a deep copy of the data in \a s.
*/
String &operator=(wchar_t c);
/*!
* Performs a deep copy of the data in \a s.
*/
String &operator=(const char *s);
/*!
* Performs a deep copy of the data in \a v.
*/
String &operator=(const ByteVector &v);
/*!
* To be able to use this class in a Map, this operator needed to be
* implemented. Returns true if \a s is less than this string in a bytewise
* comparison.
*/
bool operator<(const String &s) const;
/*!
* A null string provided for convenience.
*/
static String null;
protected:
/*
* If this String is being shared via implicit sharing, do a deep copy of the
* data and separate from the shared members. This should be called by all
* non-const subclass members.
*/
void detach();
private:
/*!
* This checks to see if the string is in \e UTF-16 (with BOM) or \e UTF-8
* format and if so converts it to \e UTF-16BE for internal use. \e Latin1
* does not require conversion since it is a subset of \e UTF-16BE and
* \e UTF16-BE requires no conversion since it is used internally.
*/
void prepare(Type t);
class StringPrivate;
StringPrivate *d;
};
}
/*!
* \relates TagLib::String
*/
const TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2);
/*!
* \relates TagLib::String
*/
const TagLib::String operator+(const char *s1, const TagLib::String &s2);
/*!
* \relates TagLib::String
*/
const TagLib::String operator+(const TagLib::String &s1, const char *s2);
/*!
* \relates TagLib::String
* Send the string to an output stream.
*/
std::ostream &operator<<(std::ostream &s, const TagLib::String &str);
#endif

98
toolkit/tstringlist.cpp Normal file
View File

@ -0,0 +1,98 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#include "tstringlist.h"
using namespace TagLib;
class StringListPrivate
{
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
StringList StringList::split(const String &s, const String &pattern)
{
StringList l;
int previousOffset = 0;
for(int offset = s.find(pattern); offset != -1; offset = s.find(pattern, offset + 1)) {
l.append(s.substr(previousOffset, offset - previousOffset));
previousOffset = offset + 1;
}
l.append(s.substr(previousOffset, s.size() - previousOffset));
return l;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
StringList::StringList() : List<String>()
{
}
StringList::StringList(const StringList &l) : List<String>(l)
{
}
StringList::StringList(const String &s) : List<String>()
{
append(s);
}
StringList::~StringList()
{
}
String StringList::toString(const String &separator) const
{
String s;
ConstIterator it = begin();
while(it != end()) {
s += *it;
it++;
if(it != end())
s += separator;
}
return s;
}
////////////////////////////////////////////////////////////////////////////////
// related functions
////////////////////////////////////////////////////////////////////////////////
std::ostream &operator<<(std::ostream &s, const StringList &l)
{
s << l.toString();
return s;
}

89
toolkit/tstringlist.h Normal file
View File

@ -0,0 +1,89 @@
/***************************************************************************
copyright : (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
***************************************************************************/
#ifndef TAGLIB_STRINGLIST_H
#define TAGLIB_STRINGLIST_H
#include "tstring.h"
#include "tlist.h"
#include <iostream>
namespace TagLib {
//! A list of strings
/*!
* This is a spcialization of the List class with some members convention for
* string operations.
*/
class StringList : public List<String>
{
public:
/*!
* Constructs an empty StringList.
*/
StringList();
/*!
* Make a shallow, implicitly shared, copy of \a l. Because this is
* implicitly shared, this method is lightweight and suitable for
* pass-by-value usage.
*/
StringList(const StringList &l);
/*!
* Constructs a StringList with \a s as a member.
*/
StringList(const String &s);
/*!
* Destroys this StringList instance.
*/
virtual ~StringList();
/*!
* Concatenate the list of strings into one string separated by \a separator.
*/
String toString(const String &separator = " ") const;
/*!
* Splits the String \a s into several strings at \a pattern. This will not include
* the pattern in the returned strings.
*/
static StringList split(const String &s, const String &pattern);
private:
class StringListPrivate;
StringListPrivate *d;
};
}
/*!
* \related TagLib::StringList
* Send the StringList to an output stream.
*/
std::ostream &operator<<(std::ostream &s, const TagLib::StringList &l);
#endif

303
toolkit/unicode.cpp Normal file
View File

@ -0,0 +1,303 @@
/*******************************************************************************
* *
* THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB *
* AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. *
* AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY. *
* *
*******************************************************************************/
/*
* Copyright 2001 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/*
* This file has been modified by Scott Wheeler <wheeler@kde.org> to remove
* the UTF32 conversion functions and to place the appropriate functions
* in their own C++ namespace.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Sept 2001: fixed const & error conditions per
mods suggested by S. Parent & A. Lillich.
See the header file "ConvertUTF.h" for complete documentation.
------------------------------------------------------------------------ */
#include "unicode.h"
#include <stdio.h>
#define UNI_SUR_HIGH_START (UTF32)0xD800
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
#define false 0
#define true 1
namespace Unicode {
static const int halfShift = 10; /* used for shifting by 10 bits */
static const UTF32 halfBase = 0x0010000UL;
static const UTF32 halfMask = 0x3FFUL;
/*
* Index into the table below with the first byte of a UTF-8 sequence to
* get the number of trailing bytes that are supposed to follow it.
*/
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
/*
* Magic values subtracted from a buffer value during UTF8 conversion.
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
/*
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... six byte sequence.)
*/
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
/* --------------------------------------------------------------------- */
/* The interface converts a whole buffer to avoid function-call overhead.
* Constants have been gathered. Loops & conditionals have been removed as
* much as possible for efficiency, in favor of drop-through switches.
* (See "Note A" at the bottom of the file for equivalent code.)
* If your compiler supports it, the "isLegalUTF8" call can be turned
* into an inline function.
*/
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END && source < sourceEnd) {
UTF32 ch2 = *source;
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else if ((flags == strictConversion) && (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
/* Figure out how many bytes the result will require */
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch < (UTF32)0x200000) { bytesToWrite = 4;
} else { bytesToWrite = 2;
ch = UNI_REPLACEMENT_CHAR;
}
// printf("bytes to write = %i\n", bytesToWrite);
target += bytesToWrite;
if (target > targetEnd) {
source = oldSource; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6;
case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6;
case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6;
case 1: *--target = ch | firstByteMark[bytesToWrite];
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
/*
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
* This must be called with the length pre-determined by the first byte.
* If not calling this from ConvertUTF8to*, then the length can be set by:
* length = trailingBytesForUTF8[*source]+1;
* and the sequence is illegal right away if there aren't that many bytes
* available.
* If presented with a length > 4, this returns false. The Unicode
* definition of UTF-8 goes up to 4-byte sequences.
*/
static Boolean isLegalUTF8(const UTF8 *source, int length) {
UTF8 a;
const UTF8 *srcptr = source+length;
switch (length) {
default: return false;
/* Everything else falls through when "true"... */
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
switch (*source) {
/* no fall-through in this inner switch */
case 0xE0: if (a < 0xA0) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false;
}
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
if (*source > 0xF4) return false;
}
return true;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return whether a UTF-8 sequence is legal or not.
* This is not used here; it's just exported.
*/
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
int length = trailingBytesForUTF8[*source]+1;
if (source+length > sourceEnd) {
return false;
}
return isLegalUTF8(source, length);
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
if ((flags == strictConversion) && (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = ch; /* normal case */
}
} else if (ch > UNI_MAX_UTF16) {
if (flags == strictConversion) {
result = sourceIllegal;
source -= (extraBytesToRead+1); /* return to the start */
break; /* Bail out; shouldn't continue */
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (ch >> halfShift) + UNI_SUR_HIGH_START;
*target++ = (ch & halfMask) + UNI_SUR_LOW_START;
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
}
/* ---------------------------------------------------------------------
Note A.
The fall-through switches in UTF-8 reading code save a
temp variable, some decrements & conditionals. The switches
are equivalent to the following loop:
{
int tmpBytesToRead = extraBytesToRead+1;
do {
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
} while (tmpBytesToRead > 0);
}
In UTF-8 writing code, the switches on "bytesToWrite" are
similarly unrolled loops.
--------------------------------------------------------------------- */

149
toolkit/unicode.h Normal file
View File

@ -0,0 +1,149 @@
#ifndef TAGLIB_UNICODE_H
#define TAGLIB_UNICODE_H
/*******************************************************************************
* *
* THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB *
* AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. *
* AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY. *
* *
*******************************************************************************/
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
/*
* Copyright 2001 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/*
* This file has been modified by Scott Wheeler <wheeler@kde.org> to remove
* the UTF32 conversion functions and to place the appropriate functions
* in their own C++ namespace.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Header file.
Several funtions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file.
Each of these routines takes pointers to input buffers and output
buffers. The input buffers are const.
Each routine converts the text between *sourceStart and sourceEnd,
putting the result into the buffer between *targetStart and
targetEnd. Note: the end pointers are *after* the last item: e.g.
*(sourceEnd - 1) is the last item.
The return result indicates whether the conversion was successful,
and if not, whether the problem was in the source or target buffers.
(Only the first encountered problem is indicated.)
After the conversion, *sourceStart and *targetStart are both
updated to point to the end of last text successfully converted in
the respective buffers.
Input parameters:
sourceStart - pointer to a pointer to the source buffer.
The contents of this are modified on return so that
it points at the next thing to be converted.
targetStart - similarly, pointer to pointer to the target buffer.
sourceEnd, targetEnd - respectively pointers to the ends of the
two buffers, for overflow checking only.
These conversion functions take a ConversionFlags argument. When this
flag is set to strict, both irregular sequences and isolated surrogates
will cause an error. When the flag is set to lenient, both irregular
sequences and isolated surrogates are converted.
Whether the flag is strict or lenient, all illegal sequences will cause
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
must check for illegal sequences.
When the flag is set to lenient, characters over 0x10FFFF are converted
to the replacement character; otherwise (when the flag is set to strict)
they constitute an error.
Output parameters:
The value "sourceIllegal" is returned from some routines if the input
sequence is malformed. When "sourceIllegal" is returned, the source
value will point to the illegal value that caused the problem. E.g.,
in UTF-8 when a sequence is malformed, it points to the start of the
malformed sequence.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Fixes & updates, Sept 2001.
------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------
The following 4 definitions are compiler-specific.
The C standard does not guarantee that wchar_t has at least
16 bits, so wchar_t is no less portable than unsigned short!
All should be unsigned values to avoid sign extension during
bit mask & shift operations.
------------------------------------------------------------------------ */
/* Some fundamental constants */
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
#define UNI_MAX_BMP (UTF32)0x0000FFFF
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
namespace Unicode {
typedef unsigned long UTF32; /* at least 32 bits */
typedef unsigned short UTF16; /* at least 16 bits */
typedef unsigned char UTF8; /* typically 8 bits */
typedef unsigned char Boolean; /* 0 or 1 */
typedef enum {
conversionOK = 0, /* conversion successful */
sourceExhausted = 1, /* partial character in source, but hit end */
targetExhausted = 2, /* insuff. room in target for conversion */
sourceIllegal = 3 /* source sequence is illegal/malformed */
} ConversionResult;
typedef enum {
strictConversion = 0,
lenientConversion
} ConversionFlags;
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
} // namespace Unicode
/* --------------------------------------------------------------------- */
#endif
#endif