mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
Merge branch 'master' into merge-master-to-taglib2
# Conflicts: # config.h.cmake # taglib/ogg/xiphcomment.cpp # taglib/taglib_config.h.cmake # taglib/toolkit/tbytevector.cpp # taglib/toolkit/trefcounter.cpp # taglib/toolkit/tstring.cpp # taglib/toolkit/tutils.h # taglib/toolkit/tzlib.cpp # taglib/xm/xmfile.cpp # tests/test_string.cpp # tests/test_xiphcomment.cpp
This commit is contained in:
commit
d8114059ee
@ -13,6 +13,14 @@ if(DEFINED ENABLE_STATIC)
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
if(APPLE)
|
||||
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
|
||||
if(BUILD_FRAMEWORK)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
#set(CMAKE_MACOSX_RPATH 1)
|
||||
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
endif()
|
||||
@ -48,11 +56,6 @@ set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to
|
||||
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})")
|
||||
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix")
|
||||
|
||||
if(APPLE)
|
||||
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
|
||||
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
@ -57,64 +57,55 @@ endif()
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic<unsigned int> x;
|
||||
x.fetch_add(1);
|
||||
x.fetch_sub(1);
|
||||
std::atomic_int x;
|
||||
++x;
|
||||
--x;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_ATOMIC)
|
||||
|
||||
if(NOT HAVE_STD_ATOMIC)
|
||||
find_package(Boost COMPONENTS atomic)
|
||||
if(Boost_ATOMIC_FOUND)
|
||||
set(HAVE_BOOST_ATOMIC 1)
|
||||
else()
|
||||
set(HAVE_BOOST_ATOMIC 0)
|
||||
endif()
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_BOOST_ATOMIC)
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
" HAVE_MAC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_ATOMIC)
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@ -259,15 +250,6 @@ if(NOT ZLIB_SOURCE)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_ZLIB)
|
||||
find_package(Boost COMPONENTS iostreams zlib)
|
||||
if(Boost_IOSTREAMS_FOUND AND Boost_ZLIB_FOUND)
|
||||
set(HAVE_BOOST_ZLIB 1)
|
||||
else()
|
||||
set(HAVE_BOOST_ZLIB 0)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether CppUnit is installed.
|
||||
|
3
NEWS
3
NEWS
@ -4,6 +4,9 @@
|
||||
* Added support for classical music tags of iTunes 12.5.
|
||||
* Dropped support for Windows 9x and NT 4.0 or older.
|
||||
* Fixed reading MP4 atoms with zero length.
|
||||
* Fixed handling of redundant UTF-8 sequences in Win32.
|
||||
* Fixed handling of lowercase field names in Vorbis Comments.
|
||||
* Fixed possible file corruptions when saving Ogg files.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.11.1 (Oct 24, 2016)
|
||||
|
@ -1,5 +1,8 @@
|
||||
/* config.h. Generated by cmake from config.h.cmake */
|
||||
|
||||
#ifndef TAGLIB_CONFIG_H
|
||||
#define TAGLIB_CONFIG_H
|
||||
|
||||
/* Defined if required for large files support */
|
||||
#cmakedefine _LARGE_FILES ${_LARGE_FILES}
|
||||
#cmakedefine _LARGEFILE_SOURCE ${_LARGEFILE_SOURCE}
|
||||
@ -15,7 +18,6 @@
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_STD_ATOMIC 1
|
||||
#cmakedefine HAVE_BOOST_ATOMIC 1
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
@ -34,10 +36,10 @@
|
||||
|
||||
/* Defined if zlib is installed */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
#cmakedefine HAVE_BOOST_ZLIB 1
|
||||
|
||||
/* Indicates whether debug messages are shown even in release mode */
|
||||
#cmakedefine TRACE_IN_RELEASE 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
|
||||
#endif
|
||||
|
@ -35,7 +35,7 @@ elseif(HAVE_ZLIB_SOURCE)
|
||||
include_directories(${ZLIB_SOURCE})
|
||||
endif()
|
||||
|
||||
if(HAVE_BOOST_BYTESWAP OR HAVE_BOOST_ATOMIC OR HAVE_BOOST_ZLIB)
|
||||
if(Boost_FOUND)
|
||||
include_directories(${Boost_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
@ -377,18 +377,10 @@ set(tag_LIB_SRCS
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
|
||||
target_link_libraries(tag ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_BOOST_ATOMIC)
|
||||
target_link_libraries(tag ${Boost_ATOMIC_LIBRARY})
|
||||
endif()
|
||||
|
||||
if(HAVE_BOOST_ZLIB)
|
||||
target_link_libraries(tag ${Boost_IOSTREAMS_LIBRARY} ${Boost_ZLIB_LIBRARY})
|
||||
endif()
|
||||
|
||||
set_target_properties(tag PROPERTIES
|
||||
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
|
||||
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
|
||||
@ -398,7 +390,13 @@ set_target_properties(tag PROPERTIES
|
||||
PUBLIC_HEADER "${tag_HDRS}"
|
||||
)
|
||||
if(BUILD_FRAMEWORK)
|
||||
set_target_properties(tag PROPERTIES FRAMEWORK TRUE)
|
||||
unset(INSTALL_NAME_DIR)
|
||||
set_target_properties(tag PROPERTIES
|
||||
FRAMEWORK TRUE
|
||||
MACOSX_RPATH 1
|
||||
VERSION "A"
|
||||
SOVERSION "A"
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS tag
|
||||
|
@ -228,7 +228,7 @@ void APE::Tag::setGenre(const String &s)
|
||||
|
||||
void APE::Tag::setYear(unsigned int i)
|
||||
{
|
||||
if(i <= 0)
|
||||
if(i == 0)
|
||||
removeItem("YEAR");
|
||||
else
|
||||
addValue("YEAR", String::number(i), true);
|
||||
@ -236,7 +236,7 @@ void APE::Tag::setYear(unsigned int i)
|
||||
|
||||
void APE::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
if(i <= 0)
|
||||
if(i == 0)
|
||||
removeItem("TRACK");
|
||||
else
|
||||
addValue("TRACK", String::number(i), true);
|
||||
|
@ -112,7 +112,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Copies the contents of \a other into this item.
|
||||
*/
|
||||
ASF::Attribute &operator=(const Attribute &other);
|
||||
Attribute &operator=(const Attribute &other);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the Attribute by the content of \a other.
|
||||
|
@ -153,8 +153,8 @@ bool FLAC::File::save()
|
||||
}
|
||||
|
||||
// Create new vorbis comments
|
||||
|
||||
Tag::duplicate(&d->tag, xiphComment(true), false);
|
||||
if(!hasXiphComment())
|
||||
Tag::duplicate(&d->tag, xiphComment(true), false);
|
||||
|
||||
d->xiphCommentData = xiphComment()->render(false);
|
||||
|
||||
|
@ -1028,10 +1028,10 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
if((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) {
|
||||
int first = 0, second = 0;
|
||||
StringList parts = StringList::split(it->second.front(), "/");
|
||||
if(!parts.isEmpty()) {
|
||||
first = parts[0].toInt();
|
||||
int first = parts[0].toInt();
|
||||
int second = 0;
|
||||
if(parts.size() > 1) {
|
||||
second = parts[1].toInt();
|
||||
}
|
||||
|
@ -36,7 +36,9 @@ class TableOfContentsFrame::TableOfContentsFramePrivate
|
||||
{
|
||||
public:
|
||||
TableOfContentsFramePrivate() :
|
||||
tagHeader(0)
|
||||
tagHeader(0),
|
||||
isTopLevel(false),
|
||||
isOrdered(false)
|
||||
{
|
||||
embeddedFrameList.setAutoDelete(true);
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ class ID3v2::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate() :
|
||||
factory(0),
|
||||
file(0),
|
||||
tagOffset(0),
|
||||
extendedHeader(0),
|
||||
@ -375,7 +376,7 @@ void ID3v2::Tag::setGenre(const String &s)
|
||||
|
||||
void ID3v2::Tag::setYear(unsigned int i)
|
||||
{
|
||||
if(i <= 0) {
|
||||
if(i == 0) {
|
||||
removeFrames("TDRC");
|
||||
return;
|
||||
}
|
||||
@ -384,7 +385,7 @@ void ID3v2::Tag::setYear(unsigned int i)
|
||||
|
||||
void ID3v2::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
if(i <= 0) {
|
||||
if(i == 0) {
|
||||
removeFrames("TRCK");
|
||||
return;
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ void Ogg::File::writePacket(unsigned int i, const ByteVector &packet)
|
||||
ByteVectorList packets = firstPage->packets();
|
||||
packets[i - firstPage->firstPacketIndex()] = packet;
|
||||
|
||||
if(firstPage != lastPage && lastPage->packetCount() > 2) {
|
||||
if(firstPage != lastPage && lastPage->packetCount() > 1) {
|
||||
ByteVectorList lastPagePackets = lastPage->packets();
|
||||
lastPagePackets.erase(lastPagePackets.begin());
|
||||
packets.append(lastPagePackets);
|
||||
|
@ -208,15 +208,15 @@ List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
|
||||
|
||||
static const unsigned int SplitSize = 32 * 255;
|
||||
|
||||
// Force repagination if the packets are too large for a page.
|
||||
// Force repagination if the segment table will exceed the size limit.
|
||||
|
||||
if(strategy != Repaginate) {
|
||||
|
||||
size_t totalSize = packets.size();
|
||||
size_t tableSize = 0;
|
||||
for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
|
||||
totalSize += it->size();
|
||||
tableSize += it->size() / 255 + 1;
|
||||
|
||||
if(totalSize > 255 * 255)
|
||||
if(tableSize > 255)
|
||||
strategy = Repaginate;
|
||||
}
|
||||
|
||||
|
@ -271,10 +271,14 @@ bool Ogg::XiphComment::checkKey(const String &key)
|
||||
{
|
||||
if(key.size() < 1)
|
||||
return false;
|
||||
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
|
||||
// forbid non-printable, non-ascii, '=' (#61) and '~' (#126)
|
||||
if (*it < 32 || *it >= 128 || *it == 61 || *it == 126)
|
||||
|
||||
// A key may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
|
||||
|
||||
for(String::ConstIterator it = key.begin(); it != key.end(); it++) {
|
||||
if(*it < 0x20 || *it > 0x7D || *it == 0x3D)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -285,11 +289,18 @@ String Ogg::XiphComment::vendorID() const
|
||||
|
||||
void Ogg::XiphComment::addField(const String &key, const String &value, bool replace)
|
||||
{
|
||||
if(!checkKey(key)) {
|
||||
debug("Ogg::XiphComment::addField() - Invalid key. Field not added.");
|
||||
return;
|
||||
}
|
||||
|
||||
const String upperKey = key.upper();
|
||||
|
||||
if(replace)
|
||||
removeFields(key.upper());
|
||||
removeFields(upperKey);
|
||||
|
||||
if(!key.isEmpty() && !value.isEmpty())
|
||||
d->fieldListMap[key.upper()].append(value);
|
||||
d->fieldListMap[upperKey].append(value);
|
||||
}
|
||||
|
||||
void Ogg::XiphComment::removeFields(const String &key)
|
||||
@ -444,86 +455,69 @@ void Ogg::XiphComment::parse(const ByteVector &data)
|
||||
const unsigned int commentLength = data.toUInt32LE(pos);
|
||||
pos += 4;
|
||||
|
||||
ByteVector entry = data.mid(pos, commentLength);
|
||||
|
||||
const ByteVector entry = data.mid(pos, commentLength);
|
||||
pos += commentLength;
|
||||
|
||||
// Don't go past data end
|
||||
|
||||
if(pos > data.size())
|
||||
break;
|
||||
|
||||
// Handle Pictures separately
|
||||
if(entry.startsWith("METADATA_BLOCK_PICTURE=")) {
|
||||
|
||||
// We need base64 encoded data including padding
|
||||
if((entry.size() - 23) > 3 && ((entry.size() - 23) % 4) == 0) {
|
||||
|
||||
// Decode base64 picture data
|
||||
ByteVector picturedata = ByteVector::fromBase64(entry.mid(23));
|
||||
if(picturedata.size()) {
|
||||
|
||||
// Decode Flac Picture
|
||||
FLAC::Picture * picture = new FLAC::Picture();
|
||||
if(picture->parse(picturedata)) {
|
||||
|
||||
d->pictureList.append(picture);
|
||||
|
||||
// continue to next field
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
delete picture;
|
||||
debug("Failed to decode FlacPicture block");
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug("Failed to decode base64 encoded data");
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug("Invalid base64 encoded data");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle old picture standard
|
||||
if(entry.startsWith("COVERART=")) {
|
||||
|
||||
if((entry.size() - 9) > 3 && ((entry.size() - 9) % 4) == 0) {
|
||||
|
||||
// Decode base64 picture data
|
||||
ByteVector picturedata = ByteVector::fromBase64(entry.mid(9));
|
||||
if (picturedata.size()) {
|
||||
|
||||
// Assume it's some type of image file
|
||||
FLAC::Picture * picture = new FLAC::Picture();
|
||||
picture->setData(picturedata);
|
||||
picture->setMimeType("image/");
|
||||
picture->setType(FLAC::Picture::Other);
|
||||
d->pictureList.append(picture);
|
||||
|
||||
// continue to next field
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
debug("Failed to decode base64 encoded data");
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug("Invalid base64 encoded data");
|
||||
}
|
||||
}
|
||||
|
||||
// Check for field separator
|
||||
size_t sep = entry.find('=');
|
||||
if(sep == ByteVector::npos()) {
|
||||
debug("Discarding invalid comment field.");
|
||||
|
||||
const size_t sep = entry.find('=');
|
||||
if(sep == 0 || sep == ByteVector::npos()) {
|
||||
debug("Ogg::XiphComment::parse() - Discarding a field. Separator not found.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse key and value
|
||||
String key = String(entry.mid(0, sep), String::UTF8);
|
||||
String value = String(entry.mid(sep + 1), String::UTF8);
|
||||
addField(key, value, false);
|
||||
// Parse the key
|
||||
|
||||
const String key = String(entry.mid(0, sep), String::UTF8).upper();
|
||||
if(!checkKey(key)) {
|
||||
debug("Ogg::XiphComment::parse() - Discarding a field. Invalid key.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(key == "METADATA_BLOCK_PICTURE" || key == "COVERART") {
|
||||
|
||||
// Handle Pictures separately
|
||||
|
||||
const ByteVector picturedata = ByteVector::fromBase64(entry.mid(sep + 1));
|
||||
if(picturedata.isEmpty()) {
|
||||
debug("Ogg::XiphComment::parse() - Discarding a field. Invalid base64 data");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(key[0] == L'M') {
|
||||
|
||||
// Decode FLAC Picture
|
||||
|
||||
FLAC::Picture * picture = new FLAC::Picture();
|
||||
if(picture->parse(picturedata)) {
|
||||
d->pictureList.append(picture);
|
||||
}
|
||||
else {
|
||||
delete picture;
|
||||
debug("Ogg::XiphComment::parse() - Failed to decode FLAC Picture block");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Assume it's some type of image file
|
||||
|
||||
FLAC::Picture * picture = new FLAC::Picture();
|
||||
picture->setData(picturedata);
|
||||
picture->setMimeType("image/");
|
||||
picture->setType(FLAC::Picture::Other);
|
||||
d->pictureList.append(picture);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Parse the text
|
||||
|
||||
addField(key, String(entry.mid(sep + 1), String::UTF8), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
taglib/taglib_config.h.cmake
Normal file
11
taglib/taglib_config.h.cmake
Normal file
@ -0,0 +1,11 @@
|
||||
/* taglib_config.h. Generated by cmake from taglib_config.h.cmake */
|
||||
|
||||
#ifndef TAGLIB_TAGLIB_CONFIG_H
|
||||
#define TAGLIB_TAGLIB_CONFIG_H
|
||||
|
||||
/* These values are no longer used. This file is present only for compatibility reasons. */
|
||||
|
||||
#define TAGLIB_WITH_ASF 1
|
||||
#define TAGLIB_WITH_MP4 1
|
||||
|
||||
#endif
|
@ -29,7 +29,6 @@
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
@ -164,7 +163,7 @@ TFloat toFloat(const ByteVector &v, size_t offset)
|
||||
} tmp;
|
||||
::memcpy(&tmp, v.data() + offset, sizeof(TInt));
|
||||
|
||||
if(ENDIAN != Utils::floatByteOrder())
|
||||
if(ENDIAN != Utils::systemByteOrder())
|
||||
tmp.i = Utils::byteSwap(tmp.i);
|
||||
|
||||
return tmp.f;
|
||||
@ -179,7 +178,7 @@ ByteVector fromFloat(TFloat value)
|
||||
} tmp;
|
||||
tmp.f = value;
|
||||
|
||||
if(ENDIAN != Utils::floatByteOrder())
|
||||
if(ENDIAN != Utils::systemByteOrder())
|
||||
tmp.i = Utils::byteSwap(tmp.i);
|
||||
|
||||
return ByteVector(reinterpret_cast<char *>(&tmp), sizeof(TInt));
|
||||
@ -482,46 +481,60 @@ ByteVector &ByteVector::replace(char oldByte, char newByte)
|
||||
|
||||
ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &with)
|
||||
{
|
||||
// TODO: This takes O(n!) time in the worst case. Rewrite it to run in O(n) time.
|
||||
|
||||
if(pattern.size() == 0 || pattern.size() > size())
|
||||
return *this;
|
||||
|
||||
if(pattern.size() == 1 && with.size() == 1)
|
||||
return replace(pattern[0], with[0]);
|
||||
|
||||
const size_t withSize = with.size();
|
||||
const size_t patternSize = pattern.size();
|
||||
const ptrdiff_t diff = withSize - patternSize;
|
||||
// Check if there is at least one occurrence of the pattern.
|
||||
|
||||
size_t offset = 0;
|
||||
while (true) {
|
||||
offset = find(pattern, offset);
|
||||
if(offset == npos())
|
||||
break;
|
||||
size_t offset = find(pattern, 0);
|
||||
if(offset == ByteVector::npos())
|
||||
return *this;
|
||||
|
||||
if(pattern.size() == with.size()) {
|
||||
|
||||
// We think this case might be common enough to optimize it.
|
||||
|
||||
detach();
|
||||
do
|
||||
{
|
||||
::memcpy(data() + offset, with.data(), with.size());
|
||||
offset = find(pattern, offset + pattern.size());
|
||||
} while(offset != ByteVector::npos());
|
||||
}
|
||||
else {
|
||||
|
||||
if(diff < 0) {
|
||||
::memmove(
|
||||
data() + offset + withSize,
|
||||
data() + offset + patternSize,
|
||||
size() - offset - patternSize);
|
||||
resize(size() + diff);
|
||||
}
|
||||
else if(diff > 0) {
|
||||
resize(size() + diff);
|
||||
::memmove(
|
||||
data() + offset + withSize,
|
||||
data() + offset + patternSize,
|
||||
size() - diff - offset - patternSize);
|
||||
// Loop once to calculate the result size.
|
||||
|
||||
size_t dstSize = size();
|
||||
do
|
||||
{
|
||||
dstSize += with.size() - pattern.size();
|
||||
offset = find(pattern, offset + pattern.size());
|
||||
} while(offset != ByteVector::npos());
|
||||
|
||||
// Loop again to copy modified data to the new vector.
|
||||
|
||||
ByteVector dst(dstSize);
|
||||
size_t dstOffset = 0;
|
||||
|
||||
offset = 0;
|
||||
while(true) {
|
||||
const size_t next = find(pattern, offset);
|
||||
if(next == ByteVector::npos()) {
|
||||
::memcpy(dst.data() + dstOffset, data() + offset, size() - offset);
|
||||
break;
|
||||
}
|
||||
|
||||
::memcpy(dst.data() + dstOffset, data() + offset, next - offset);
|
||||
dstOffset += next - offset;
|
||||
|
||||
::memcpy(dst.data() + dstOffset, with.data(), with.size());
|
||||
dstOffset += with.size();
|
||||
|
||||
offset = next + pattern.size();
|
||||
}
|
||||
|
||||
::memcpy(data() + offset, with.data(), with.size());
|
||||
|
||||
offset += withSize;
|
||||
if(offset > size() - patternSize)
|
||||
break;
|
||||
swap(dst);
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
@ -31,14 +31,9 @@
|
||||
|
||||
#if defined(HAVE_STD_ATOMIC)
|
||||
# include <atomic>
|
||||
# define ATOMIC_INT std::atomic<int>
|
||||
# define ATOMIC_INC(x) x.fetch_add(1)
|
||||
# define ATOMIC_DEC(x) (x.fetch_sub(1) - 1)
|
||||
#elif defined(HAVE_BOOST_ATOMIC)
|
||||
# include <boost/atomic.hpp>
|
||||
# define ATOMIC_INT boost::atomic<int>
|
||||
# define ATOMIC_INC(x) x.fetch_add(1)
|
||||
# define ATOMIC_DEC(x) (x.fetch_sub(1) - 1)
|
||||
# define ATOMIC_INT std::atomic_int
|
||||
# define ATOMIC_INC(x) (++x)
|
||||
# define ATOMIC_DEC(x) (--x)
|
||||
#elif defined(HAVE_GCC_ATOMIC)
|
||||
# define ATOMIC_INT int
|
||||
# define ATOMIC_INC(x) ::__sync_add_and_fetch(&x, 1)
|
||||
|
@ -88,7 +88,7 @@ namespace
|
||||
#ifdef _WIN32
|
||||
|
||||
len = ::MultiByteToWideChar(
|
||||
CP_UTF8, 0, src, static_cast<int>(srcLength), dst, static_cast<int>(dstLength));
|
||||
CP_UTF8, MB_ERR_INVALID_CHARS, src, static_cast<int>(srcLength), dst, static_cast<int>(dstLength));
|
||||
|
||||
#else
|
||||
|
||||
@ -448,7 +448,10 @@ bool String::startsWith(const String &s) const
|
||||
|
||||
String String::substr(size_t position, size_t length) const
|
||||
{
|
||||
return String(d->data->substr(position, length));
|
||||
if(position == 0 && length >= size())
|
||||
return *this;
|
||||
else
|
||||
return String(d->data->substr(position, length));
|
||||
}
|
||||
|
||||
String &String::append(const String &s)
|
||||
@ -501,7 +504,7 @@ ByteVector String::data(Type t) const
|
||||
ByteVector v(size(), 0);
|
||||
char *p = v.data();
|
||||
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); it++)
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); ++it)
|
||||
*p++ = static_cast<char>(*it);
|
||||
|
||||
return v;
|
||||
@ -529,7 +532,7 @@ ByteVector String::data(Type t) const
|
||||
*p++ = '\xff';
|
||||
*p++ = '\xfe';
|
||||
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); it++) {
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); ++it) {
|
||||
*p++ = static_cast<char>(*it & 0xff);
|
||||
*p++ = static_cast<char>(*it >> 8);
|
||||
}
|
||||
@ -541,7 +544,7 @@ ByteVector String::data(Type t) const
|
||||
ByteVector v(size() * 2, 0);
|
||||
char *p = v.data();
|
||||
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); it++) {
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); ++it) {
|
||||
*p++ = static_cast<char>(*it >> 8);
|
||||
*p++ = static_cast<char>(*it & 0xff);
|
||||
}
|
||||
@ -553,7 +556,7 @@ ByteVector String::data(Type t) const
|
||||
ByteVector v(size() * 2, 0);
|
||||
char *p = v.data();
|
||||
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); it++) {
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); ++it) {
|
||||
*p++ = static_cast<char>(*it & 0xff);
|
||||
*p++ = static_cast<char>(*it >> 8);
|
||||
}
|
||||
@ -598,7 +601,7 @@ String String::stripWhiteSpace() const
|
||||
|
||||
bool String::isLatin1() const
|
||||
{
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); it++) {
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); ++it) {
|
||||
if(*it >= 256)
|
||||
return false;
|
||||
}
|
||||
@ -607,7 +610,7 @@ bool String::isLatin1() const
|
||||
|
||||
bool String::isAscii() const
|
||||
{
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); it++) {
|
||||
for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); ++it) {
|
||||
if(*it >= 128)
|
||||
return false;
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ namespace TagLib
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the integer byte order of the system.
|
||||
* Returns the byte order of the system.
|
||||
*/
|
||||
inline ByteOrder systemByteOrder()
|
||||
{
|
||||
@ -237,26 +237,6 @@ namespace TagLib
|
||||
else
|
||||
return BigEndian;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the IEEE754 byte order of the system.
|
||||
*/
|
||||
inline ByteOrder floatByteOrder()
|
||||
{
|
||||
union {
|
||||
double d;
|
||||
char c;
|
||||
} u;
|
||||
|
||||
// 1.0 is stored in memory like 0x3FF0000000000000 in canonical form.
|
||||
// So the first byte is zero if little endian.
|
||||
|
||||
u.d = 1.0;
|
||||
if(u.c == 0)
|
||||
return LittleEndian;
|
||||
else
|
||||
return BigEndian;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,23 +27,19 @@
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ZLIB)
|
||||
#ifdef HAVE_ZLIB
|
||||
# include <zlib.h>
|
||||
#elif defined(HAVE_BOOST_ZLIB)
|
||||
# include <boost/iostreams/filtering_streambuf.hpp>
|
||||
# include <boost/iostreams/filter/zlib.hpp>
|
||||
# include <tstring.h>
|
||||
# include <tdebug.h>
|
||||
#endif
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "tzlib.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
bool zlib::isAvailable()
|
||||
{
|
||||
#if defined(HAVE_ZLIB) || defined(HAVE_BOOST_ZLIB)
|
||||
#ifdef HAVE_ZLIB
|
||||
|
||||
return true;
|
||||
|
||||
@ -56,7 +52,7 @@ bool zlib::isAvailable()
|
||||
|
||||
ByteVector zlib::decompress(const ByteVector &data)
|
||||
{
|
||||
#if defined(HAVE_ZLIB)
|
||||
#ifdef HAVE_ZLIB
|
||||
|
||||
z_stream stream = {};
|
||||
|
||||
@ -102,38 +98,6 @@ ByteVector zlib::decompress(const ByteVector &data)
|
||||
|
||||
return outData;
|
||||
|
||||
#elif defined(HAVE_BOOST_ZLIB)
|
||||
|
||||
using namespace boost::iostreams;
|
||||
|
||||
struct : public sink
|
||||
{
|
||||
ByteVector data;
|
||||
|
||||
typedef char char_type;
|
||||
typedef sink_tag category;
|
||||
|
||||
std::streamsize write(char const* s, std::streamsize n)
|
||||
{
|
||||
const size_t originalSize = data.size();
|
||||
|
||||
data.resize(static_cast<size_t>(originalSize + n));
|
||||
::memcpy(data.data() + originalSize, s, static_cast<size_t>(n));
|
||||
|
||||
return n;
|
||||
}
|
||||
} sink;
|
||||
|
||||
try {
|
||||
zlib_decompressor().write(sink, data.data(), data.size());
|
||||
}
|
||||
catch(const zlib_error &) {
|
||||
debug("zlib::decompress() - Error reading compressed stream.");
|
||||
return ByteVector();
|
||||
}
|
||||
|
||||
return sink.data;
|
||||
|
||||
#else
|
||||
|
||||
return ByteVector();
|
||||
|
@ -590,9 +590,9 @@ void XM::File::read(bool)
|
||||
unsigned int count = 4 + instrument.read(*this, instrumentHeaderSize - 4U);
|
||||
READ_ASSERT(count == std::min(instrumentHeaderSize, instrument.size() + 4));
|
||||
|
||||
unsigned int sampleHeaderSize = 0;
|
||||
long offset = 0;
|
||||
if(sampleCount > 0) {
|
||||
unsigned int sampleHeaderSize = 0;
|
||||
sumSampleCount += sampleCount;
|
||||
// wouldn't know which header size to assume otherwise:
|
||||
READ_ASSERT(instrumentHeaderSize >= count + 4 && readU32L(sampleHeaderSize));
|
||||
|
BIN
tests/data/lowercase-fields.ogg
Normal file
BIN
tests/data/lowercase-fields.ogg
Normal file
Binary file not shown.
@ -46,6 +46,7 @@ class TestByteVector : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testIntegerConversion);
|
||||
CPPUNIT_TEST(testFloatingPointConversion);
|
||||
CPPUNIT_TEST(testReplace);
|
||||
CPPUNIT_TEST(testReplaceAndDetach);
|
||||
CPPUNIT_TEST(testIterator);
|
||||
CPPUNIT_TEST(testResize);
|
||||
CPPUNIT_TEST(testAppend1);
|
||||
@ -312,6 +313,45 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testReplaceAndDetach()
|
||||
{
|
||||
{
|
||||
ByteVector a("abcdabf");
|
||||
ByteVector b = a;
|
||||
a.replace(ByteVector("a"), ByteVector("x"));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a);
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b);
|
||||
}
|
||||
{
|
||||
ByteVector a("abcdabf");
|
||||
ByteVector b = a;
|
||||
a.replace('a', 'x');
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("xbcdxbf"), a);
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b);
|
||||
}
|
||||
{
|
||||
ByteVector a("abcdabf");
|
||||
ByteVector b = a;
|
||||
a.replace(ByteVector("ab"), ByteVector("xy"));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("xycdxyf"), a);
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b);
|
||||
}
|
||||
{
|
||||
ByteVector a("abcdabf");
|
||||
ByteVector b = a;
|
||||
a.replace(ByteVector("a"), ByteVector("<a>"));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("<a>bcd<a>bf"), a);
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabf"), b);
|
||||
}
|
||||
{
|
||||
ByteVector a("ab<c>dab<c>");
|
||||
ByteVector b = a;
|
||||
a.replace(ByteVector("<c>"), ByteVector("c"));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("abcdabc"), a);
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("ab<c>dab<c>"), b);
|
||||
}
|
||||
}
|
||||
|
||||
void testIterator()
|
||||
{
|
||||
ByteVector v1("taglib");
|
||||
|
@ -62,6 +62,7 @@ class TestFLAC : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testUpdateID3v2);
|
||||
CPPUNIT_TEST(testEmptyID3v2);
|
||||
CPPUNIT_TEST(testStripTags);
|
||||
CPPUNIT_TEST(testRemoveXiphField);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -493,6 +494,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testRemoveXiphField()
|
||||
{
|
||||
ScopedFileCopy copy("silence-44-s", ".flac");
|
||||
|
||||
{
|
||||
FLAC::File f(copy.fileName().c_str());
|
||||
f.xiphComment(true)->setTitle("XiphComment Title");
|
||||
f.ID3v2Tag(true)->setTitle("ID3v2 Title");
|
||||
f.save();
|
||||
}
|
||||
{
|
||||
FLAC::File f(copy.fileName().c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(String("XiphComment Title"), f.xiphComment()->title());
|
||||
f.xiphComment()->removeFields("TITLE");
|
||||
f.save();
|
||||
}
|
||||
{
|
||||
FLAC::File f(copy.fileName().c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(String(), f.xiphComment()->title());
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestFLAC);
|
||||
|
@ -32,12 +32,13 @@ using namespace TagLib;
|
||||
class TestList : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestList);
|
||||
CPPUNIT_TEST(testList);
|
||||
CPPUNIT_TEST(testAppend);
|
||||
CPPUNIT_TEST(testDetach);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
||||
void testList()
|
||||
void testAppend()
|
||||
{
|
||||
List<int> l1;
|
||||
List<int> l2;
|
||||
@ -51,14 +52,25 @@ public:
|
||||
l3.append(2);
|
||||
l3.append(3);
|
||||
l3.append(4);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)4, l1.size());
|
||||
CPPUNIT_ASSERT(l1 == l3);
|
||||
|
||||
List<int> l4 = l1;
|
||||
List<int>::Iterator it = l4.find(3);
|
||||
*it = 33;
|
||||
CPPUNIT_ASSERT_EQUAL(l1[2], 3);
|
||||
CPPUNIT_ASSERT_EQUAL(l4[2], 33);
|
||||
}
|
||||
|
||||
void testDetach()
|
||||
{
|
||||
List<int> l1;
|
||||
l1.append(1);
|
||||
l1.append(2);
|
||||
l1.append(3);
|
||||
l1.append(4);
|
||||
|
||||
List<int> l2 = l1;
|
||||
List<int>::Iterator it = l2.find(3);
|
||||
*it = 33;
|
||||
CPPUNIT_ASSERT_EQUAL(3, l1[2]);
|
||||
CPPUNIT_ASSERT_EQUAL(33, l2[2]);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestList);
|
||||
|
@ -34,6 +34,7 @@ class TestMap : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestMap);
|
||||
CPPUNIT_TEST(testInsert);
|
||||
CPPUNIT_TEST(testDetach);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -42,19 +43,28 @@ public:
|
||||
{
|
||||
Map<String, int> m1;
|
||||
m1.insert("foo", 3);
|
||||
m1.insert("bar", 5);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)2, m1.size());
|
||||
CPPUNIT_ASSERT_EQUAL(3, m1["foo"]);
|
||||
CPPUNIT_ASSERT_EQUAL(5, m1["bar"]);
|
||||
m1.insert("foo", 7);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)2, m1.size());
|
||||
CPPUNIT_ASSERT_EQUAL(7, m1["foo"]);
|
||||
CPPUNIT_ASSERT_EQUAL(5, m1["bar"]);
|
||||
}
|
||||
|
||||
m1.insert("alice", 5);
|
||||
m1.insert("bob", 9);
|
||||
void testDetach()
|
||||
{
|
||||
Map<String, int> m1;
|
||||
m1.insert("alice", 5);
|
||||
m1.insert("bob", 9);
|
||||
m1.insert("carol", 11);
|
||||
|
||||
Map<String, int> m2 = m1;
|
||||
Map<String, int>::Iterator it = m2.find("bob");
|
||||
(*it).second = 99;
|
||||
CPPUNIT_ASSERT_EQUAL(m1["bob"], 9);
|
||||
CPPUNIT_ASSERT_EQUAL(m2["bob"], 99);
|
||||
CPPUNIT_ASSERT_EQUAL(9, m1["bob"]);
|
||||
CPPUNIT_ASSERT_EQUAL(99, m2["bob"]);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -42,7 +42,8 @@ class TestOGG : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestOGG);
|
||||
CPPUNIT_TEST(testSimple);
|
||||
CPPUNIT_TEST(testSplitPackets);
|
||||
CPPUNIT_TEST(testSplitPackets1);
|
||||
CPPUNIT_TEST(testSplitPackets2);
|
||||
CPPUNIT_TEST(testDictInterface1);
|
||||
CPPUNIT_TEST(testDictInterface2);
|
||||
CPPUNIT_TEST(testAudioProperties);
|
||||
@ -67,7 +68,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testSplitPackets()
|
||||
void testSplitPackets1()
|
||||
{
|
||||
ScopedFileCopy copy("empty", ".ogg");
|
||||
string newname = copy.fileName();
|
||||
@ -110,6 +111,33 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testSplitPackets2()
|
||||
{
|
||||
ScopedFileCopy copy("empty", ".ogg");
|
||||
string newname = copy.fileName();
|
||||
|
||||
const String text = longText(60890, true);
|
||||
|
||||
{
|
||||
Ogg::Vorbis::File f(newname.c_str());
|
||||
f.tag()->setTitle(text);
|
||||
f.save();
|
||||
}
|
||||
{
|
||||
Ogg::Vorbis::File f(newname.c_str());
|
||||
CPPUNIT_ASSERT(f.isValid());
|
||||
CPPUNIT_ASSERT_EQUAL(text, f.tag()->title());
|
||||
|
||||
f.tag()->setTitle("ABCDE");
|
||||
f.save();
|
||||
}
|
||||
{
|
||||
Ogg::Vorbis::File f(newname.c_str());
|
||||
CPPUNIT_ASSERT(f.isValid());
|
||||
CPPUNIT_ASSERT_EQUAL(String("ABCDE"), f.tag()->title());
|
||||
}
|
||||
}
|
||||
|
||||
void testDictInterface1()
|
||||
{
|
||||
ScopedFileCopy copy("empty", ".ogg");
|
||||
|
@ -50,6 +50,7 @@ class TestString : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testEncodeNonLatin1);
|
||||
CPPUNIT_TEST(testEncodeEmpty);
|
||||
CPPUNIT_TEST(testIterator);
|
||||
CPPUNIT_TEST(testRedundantUTF8);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -109,6 +110,9 @@ public:
|
||||
CPPUNIT_ASSERT(String(" foo ").stripWhiteSpace() == String("foo"));
|
||||
CPPUNIT_ASSERT(String("foo ").stripWhiteSpace() == String("foo"));
|
||||
CPPUNIT_ASSERT(String(" foo").stripWhiteSpace() == String("foo"));
|
||||
CPPUNIT_ASSERT(String("foo").stripWhiteSpace() == String("foo"));
|
||||
CPPUNIT_ASSERT(String("f o o").stripWhiteSpace() == String("f o o"));
|
||||
CPPUNIT_ASSERT(String(" f o o ").stripWhiteSpace() == String("f o o"));
|
||||
|
||||
CPPUNIT_ASSERT(memcmp(String("foo").data(String::Latin1).data(), "foo", 3) == 0);
|
||||
CPPUNIT_ASSERT(memcmp(String("f").data(String::Latin1).data(), "f", 1) == 0);
|
||||
@ -265,6 +269,8 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(String("01"), String("0123456").substr(0, 2));
|
||||
CPPUNIT_ASSERT_EQUAL(String("12"), String("0123456").substr(1, 2));
|
||||
CPPUNIT_ASSERT_EQUAL(String("123456"), String("0123456").substr(1, 200));
|
||||
CPPUNIT_ASSERT_EQUAL(String("0123456"), String("0123456").substr(0, 7));
|
||||
CPPUNIT_ASSERT_EQUAL(String("0123456"), String("0123456").substr(0, 200));
|
||||
}
|
||||
|
||||
void testNewline()
|
||||
@ -332,6 +338,15 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(L'i', *it1);
|
||||
CPPUNIT_ASSERT_EQUAL(L'I', *it2);
|
||||
}
|
||||
|
||||
void testRedundantUTF8()
|
||||
{
|
||||
CPPUNIT_ASSERT_EQUAL(String("/"), String(ByteVector("\x2F"), String::UTF8));
|
||||
CPPUNIT_ASSERT(String(ByteVector("\xC0\xAF"), String::UTF8).isEmpty());
|
||||
CPPUNIT_ASSERT(String(ByteVector("\xE0\x80\xAF"), String::UTF8).isEmpty());
|
||||
CPPUNIT_ASSERT(String(ByteVector("\xF0\x80\x80\xAF"), String::UTF8).isEmpty());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestString);
|
||||
|
@ -42,10 +42,12 @@ class TestXiphComment : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testSetYear);
|
||||
CPPUNIT_TEST(testTrack);
|
||||
CPPUNIT_TEST(testSetTrack);
|
||||
CPPUNIT_TEST(testInvalidKeys);
|
||||
CPPUNIT_TEST(testInvalidKeys1);
|
||||
CPPUNIT_TEST(testInvalidKeys2);
|
||||
CPPUNIT_TEST(testClearComment);
|
||||
CPPUNIT_TEST(testRemoveFields);
|
||||
CPPUNIT_TEST(testPicture);
|
||||
CPPUNIT_TEST(testLowercaseFields);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -90,19 +92,32 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(String("3"), cmt.fieldListMap()["TRACKNUMBER"].front());
|
||||
}
|
||||
|
||||
void testInvalidKeys()
|
||||
void testInvalidKeys1()
|
||||
{
|
||||
PropertyMap map;
|
||||
map[""] = String("invalid key: empty string");
|
||||
map["A=B"] = String("invalid key: contains '='");
|
||||
map["A~B"] = String("invalid key: contains '~'");
|
||||
map["A\x7F" "B"] = String("invalid key: contains '\x7F'");
|
||||
map[L"A\x3456" "B"] = String("invalid key: Unicode");
|
||||
|
||||
Ogg::XiphComment cmt;
|
||||
PropertyMap unsuccessful = cmt.setProperties(map);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)3, unsuccessful.size());
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)5, unsuccessful.size());
|
||||
CPPUNIT_ASSERT(cmt.properties().isEmpty());
|
||||
}
|
||||
|
||||
void testInvalidKeys2()
|
||||
{
|
||||
Ogg::XiphComment cmt;
|
||||
cmt.addField("", "invalid key: empty string");
|
||||
cmt.addField("A=B", "invalid key: contains '='");
|
||||
cmt.addField("A~B", "invalid key: contains '~'");
|
||||
cmt.addField("A\x7F" "B", "invalid key: contains '\x7F'");
|
||||
cmt.addField(L"A\x3456" "B", "invalid key: Unicode");
|
||||
CPPUNIT_ASSERT_EQUAL(0U, cmt.fieldCount());
|
||||
}
|
||||
|
||||
void testClearComment()
|
||||
{
|
||||
ScopedFileCopy copy("empty", ".ogg");
|
||||
@ -178,6 +193,23 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testLowercaseFields()
|
||||
{
|
||||
const ScopedFileCopy copy("lowercase-fields", ".ogg");
|
||||
{
|
||||
Ogg::Vorbis::File f(copy.fileName().c_str());
|
||||
List<FLAC::Picture *> lst = f.tag()->pictureList();
|
||||
CPPUNIT_ASSERT_EQUAL(String("TEST TITLE"), f.tag()->title());
|
||||
CPPUNIT_ASSERT_EQUAL(String("TEST ARTIST"), f.tag()->artist());
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)1, lst.size());
|
||||
f.save();
|
||||
}
|
||||
{
|
||||
Ogg::Vorbis::File f(copy.fileName().c_str());
|
||||
CPPUNIT_ASSERT(f.find("METADATA_BLOCK_PICTURE") > 0);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestXiphComment);
|
||||
|
Loading…
Reference in New Issue
Block a user