diff options
Diffstat (limited to 'libs/taglib/taglib/toolkit/tstring.cpp')
-rw-r--r-- | libs/taglib/taglib/toolkit/tstring.cpp | 786 |
1 files changed, 786 insertions, 0 deletions
diff --git a/libs/taglib/taglib/toolkit/tstring.cpp b/libs/taglib/taglib/toolkit/tstring.cpp new file mode 100644 index 0000000000..cd274367e6 --- /dev/null +++ b/libs/taglib/taglib/toolkit/tstring.cpp @@ -0,0 +1,786 @@ +/*************************************************************************** + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * + * USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include "tstring.h" +#include "unicode.h" +#include "tdebug.h" + +#include <iostream> + +#include <string.h> + +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 || t == UTF16LE) { + debug("String::String() -- A std::string should not contain UTF16."); + return; + } + + int length = s.length(); + d->data.resize(length); + wstring::iterator targetIt = d->data.begin(); + + for(std::string::const_iterator it = s.begin(); it != s.end(); it++) { + *targetIt = uchar(*it); + ++targetIt; + } + + 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 || t == UTF16LE) { + debug("String::String() -- A const char * should not contain UTF16."); + return; + } + + int length = ::strlen(s); + d->data.resize(length); + + wstring::iterator targetIt = d->data.begin(); + + for(int i = 0; i < length; i++) { + *targetIt = uchar(s[i]); + ++targetIt; + } + + 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 || t == UTF16LE) { + 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(v.isEmpty()) + return; + + if(t == Latin1 || t == UTF8) { + + int length = 0; + d->data.resize(v.size()); + wstring::iterator targetIt = d->data.begin(); + for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) { + *targetIt = uchar(*it); + ++targetIt; + ++length; + } + d->data.resize(length); + } + else { + d->data.resize(v.size() / 2); + wstring::iterator targetIt = d->data.begin(); + + for(ByteVector::ConstIterator it = v.begin(); + it != v.end() && it + 1 != v.end() && combine(*it, *(it + 1)); + it += 2) + { + *targetIt = combine(*it, *(it + 1)); + ++targetIt; + } + } + 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; +} + +TagLib::wstring String::toWString() const +{ + return d->data; +} + +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; +} + +String::Iterator String::begin() +{ + return d->data.begin(); +} + +String::ConstIterator String::begin() const +{ + return d->data.begin(); +} + +String::Iterator String::end() +{ + return d->data.end(); +} + +String::ConstIterator String::end() const +{ + return d->data.end(); +} + +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; +} + +bool String::startsWith(const String &s) const +{ + if(s.length() > length()) + return false; + + return substr(0, s.length()) == s; +} + +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(); +} + +TagLib::uint String::length() const +{ + return 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); + } + break; + } + 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(c1); + v.append(c2); + } + break; + } + case UTF16LE: + { + 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); + } + 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 != end && + (*begin == '\t' || *begin == '\n' || *begin == '\f' || + *begin == '\r' || *begin == ' ')) + { + ++begin; + } + + if(begin == end) + return null; + + // There must be at least one non-whitespace character 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)); +} + +bool String::isLatin1() const +{ + for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { + if(*it >= 256) + return false; + } + return true; +} + +bool String::isAscii() const +{ + for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { + if(*it >= 128) + return false; + } + return true; +} + +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; +} + +TagLib::wchar &String::operator[](int i) +{ + return d->data[i]; +} + +const TagLib::wchar &String::operator[](int i) const +{ + return d->data[i]; +} + +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; + + int length = ::strlen(s); + d->data.resize(length); + + wstring::iterator targetIt = d->data.begin(); + for(int i = 0; i < length; i++) { + *targetIt = uchar(s[i]); + ++targetIt; + } + + 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(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) { + *targetIt = uchar(*it); + ++targetIt; + ++i; + } + + // 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 && (d->data[0] == 0xfeff || d->data[0] == 0xfffe)) { + 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 ? target - targetBuffer - 1 : 0; + d->data.resize(newSize); + + for(int i = 0; i < newSize; i++) + d->data[i] = targetBuffer[i]; + + delete [] sourceBuffer; + delete [] targetBuffer; + + break; + } + case UTF16LE: + { + for(uint i = 0; i < d->data.size(); i++) + d->data[i] = byteSwap((unsigned short)d->data[i]); + break; + } + 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; +} |