mirror of
https://github.com/taglib/taglib.git
synced 2025-07-25 16:34:26 -04:00
Merge branch 'taglib2' into tonumber4-merge
Conflicts: ConfigureChecks.cmake config-taglib.h.cmake
This commit is contained in:
@ -17,24 +17,14 @@ endif()
|
||||
|
||||
# Determine whether or not your compiler supports move semantics.
|
||||
check_cxx_source_compiles("
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic error \"-Wc++11-extensions\"
|
||||
#endif
|
||||
#include <utility>
|
||||
int func(int &&x) { return x - 1; }
|
||||
int main() { return func(std::move(1)); }
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic error \"-Wc++11-extensions\"
|
||||
#endif
|
||||
#include <utility>
|
||||
int func(int &&x) { return x - 1; }
|
||||
int main() { return func(std::move(1)); }
|
||||
" SUPPORT_MOVE_SEMANTICS)
|
||||
|
||||
# Determine whether or not your compiler supports template alias.
|
||||
check_cxx_source_compiles("
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic error \"-Wc++11-extensions\"
|
||||
#endif
|
||||
#include <vector>
|
||||
template <typename T> using myVector = std::vector<T>;
|
||||
int main() { return 0; }
|
||||
" SUPPORT_TEMPLATE_ALIAS)
|
||||
|
||||
# Determine which kind of byte swap functions your compiler supports.
|
||||
|
||||
# GCC's __builtin_bswap* should be checked individually
|
||||
@ -110,38 +100,103 @@ endif()
|
||||
|
||||
# Determine where shared_ptr<T> is defined regardless of C++11 support.
|
||||
check_cxx_source_compiles("
|
||||
#include <memory>
|
||||
int main() { std::tr1::shared_ptr<int> x; return 0; }
|
||||
#include <memory>
|
||||
int main() { std::tr1::shared_ptr<int> x; return 0; }
|
||||
" HAVE_STD_SHARED_PTR)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
if(NOT HAVE_STD_SHARED_PTR)
|
||||
check_cxx_source_compiles("
|
||||
#include <tr1/memory>
|
||||
int main() { std::tr1::shared_ptr<int> x; return 0; }
|
||||
" HAVE_TR1_SHARED_PTR)
|
||||
" HAVE_TR1_SHARED_PTR)
|
||||
|
||||
if(NOT HAVE_TR1_SHARED_PTR)
|
||||
check_cxx_source_compiles("
|
||||
#include <boost/shared_ptr.hpp>
|
||||
int main() { boost::shared_ptr<int> x; return 0; }
|
||||
" HAVE_BOOST_SHARED_PTR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine which kind of atomic operations your compiler supports.
|
||||
if(NOT HAVE_STD_SHARED_PTR AND NOT HAVE_TR1_SHARED_PTR AND NOT HAVE_BOOST_SHARED_PTR)
|
||||
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_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
int main() {
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
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()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports some safer version of sprintf.
|
||||
check_cxx_source_compiles("
|
||||
#include <boost/shared_ptr.hpp>
|
||||
int main() { boost::shared_ptr<int> x; return 0; }
|
||||
" HAVE_BOOST_SHARED_PTR)
|
||||
#include <cstdio>
|
||||
int main() { char buf[20]; snprintf(buf, 20, \"%d\", 1); return 0; }
|
||||
" HAVE_SNPRINTF)
|
||||
|
||||
if(NOT HAVE_SNPRINTF)
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
int main() { char buf[20]; sprintf_s(buf, \"%d\", 1); return 0; }
|
||||
" HAVE_SPRINTF_S)
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports codecvt header.
|
||||
check_cxx_source_compiles("
|
||||
#include <codecvt>
|
||||
int main() { std::codecvt_utf8_utf16<wchar_t> x; return 0; }
|
||||
#include <codecvt>
|
||||
int main() { std::codecvt_utf8_utf16<wchar_t> x; return 0; }
|
||||
" HAVE_CODECVT)
|
||||
|
||||
# check for libz using the cmake supplied FindZLIB.cmake
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
set(HAVE_ZLIB 1)
|
||||
set(HAVE_ZLIB 1)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
set(HAVE_ZLIB 0)
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
|
||||
find_package(CppUnit)
|
||||
if(NOT CppUnit_FOUND AND BUILD_TESTS)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
set(BUILD_TESTS OFF)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
set(BUILD_TESTS OFF)
|
||||
endif()
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
/* config-taglib.h. Generated by cmake from config-taglib.h.cmake */
|
||||
|
||||
/* Define if you have libz */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
|
||||
/* Indicates the endianness of your target system */
|
||||
#cmakedefine TAGLIB_LITTLE_ENDIAN 1
|
||||
#cmakedefine TAGLIB_BIG_ENDIAN 1
|
||||
@ -10,9 +7,6 @@
|
||||
/* Defined if your compiler supports the move semantics */
|
||||
#cmakedefine SUPPORT_MOVE_SEMANTICS 1
|
||||
|
||||
/* Defined if your compiler supports the template alias */
|
||||
#cmakedefine SUPPORT_TEMPLATE_ALIAS 1
|
||||
|
||||
/* Defined if your compiler supports some byte swap functions */
|
||||
#cmakedefine HAVE_GCC_BYTESWAP_16 1
|
||||
#cmakedefine HAVE_GCC_BYTESWAP_32 1
|
||||
@ -27,6 +21,16 @@
|
||||
#cmakedefine HAVE_TR1_SHARED_PTR 1
|
||||
#cmakedefine HAVE_BOOST_SHARED_PTR 1
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
#cmakedefine HAVE_IA64_ATOMIC 1
|
||||
|
||||
/* Defined if your compiler supports some safer version of sprintf */
|
||||
#cmakedefine HAVE_SNPRINTF 1
|
||||
#cmakedefine HAVE_SPRINTF_S 1
|
||||
|
||||
/* Defined if your compiler has <codecvt> header */
|
||||
#cmakedefine HAVE_CODECVT 1
|
||||
|
||||
|
@ -24,6 +24,8 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ebml
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ebml/matroska
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
@ -130,6 +132,12 @@ set(tag_HDRS
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
ebml/ebmlfile.h
|
||||
ebml/ebmlelement.h
|
||||
ebml/ebmlconstants.h
|
||||
ebml/matroska/ebmlmatroskafile.h
|
||||
ebml/matroska/ebmlmatroskaconstants.h
|
||||
ebml/matroska/ebmlmatroskaaudio.h
|
||||
)
|
||||
|
||||
set(mpeg_SRCS
|
||||
@ -295,11 +303,22 @@ set(toolkit_SRCS
|
||||
toolkit/unicode.cpp
|
||||
)
|
||||
|
||||
set(ebml_SRCS
|
||||
ebml/ebmlfile.cpp
|
||||
ebml/ebmlelement.cpp
|
||||
)
|
||||
|
||||
set(matroska_SRCS
|
||||
ebml/matroska/ebmlmatroskafile.cpp
|
||||
ebml/matroska/ebmlmatroskaaudio.cpp
|
||||
)
|
||||
|
||||
set(tag_LIB_SRCS
|
||||
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
|
||||
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
|
||||
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
${ebml_SRCS} ${matroska_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
|
@ -205,7 +205,7 @@ namespace TagLib
|
||||
ByteVector render(const String &name, int kind = 0) const;
|
||||
|
||||
class AttributePrivate;
|
||||
RefCountPtr<AttributePrivate> d;
|
||||
TAGLIB_SHARED_PTR<AttributePrivate> d;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,7 @@ namespace TagLib
|
||||
#endif
|
||||
private:
|
||||
class PicturePrivate;
|
||||
RefCountPtr<PicturePrivate> d;
|
||||
TAGLIB_SHARED_PTR<PicturePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
63
taglib/ebml/ebmlconstants.h
Normal file
63
taglib/ebml/ebmlconstants.h
Normal file
@ -0,0 +1,63 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBML_CONSTANTS
|
||||
#define TAGLIB_EBML_CONSTANTS
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <iostream>
|
||||
#include "tdebug.h"
|
||||
#endif
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
//! Shorter representation of the type.
|
||||
typedef unsigned long long int ulli;
|
||||
|
||||
//! The id of an EBML Void element that is just a placeholder.
|
||||
const ulli Void = 0xecL;
|
||||
|
||||
//! The id of an EBML CRC32 element that contains a crc32 value.
|
||||
const ulli CRC32 = 0xc3L;
|
||||
|
||||
//! A namespace containing the ids of the EBML header's elements.
|
||||
namespace Header {
|
||||
const ulli EBML = 0x1a45dfa3L;
|
||||
const ulli EBMLVersion = 0x4286L;
|
||||
const ulli EBMLReadVersion = 0x42f7L;
|
||||
const ulli EBMLMaxIDWidth = 0x42f2L;
|
||||
const ulli EBMLMaxSizeWidth = 0x42f3L;
|
||||
const ulli DocType = 0x4282L;
|
||||
const ulli DocTypeVersion = 0x4287L;
|
||||
const ulli DocTypeReadVersion = 0x4285L;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
496
taglib/ebml/ebmlelement.cpp
Normal file
496
taglib/ebml/ebmlelement.cpp
Normal file
@ -0,0 +1,496 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Element::ElementPrivate
|
||||
{
|
||||
public:
|
||||
// The id of this element.
|
||||
ulli id;
|
||||
|
||||
// The position of the element, where the header begins.
|
||||
offset_t position;
|
||||
|
||||
// The size of the element as read from the header. Note: Actually an ulli but
|
||||
// due to the variable integer size limited, thus offset_t is ok.
|
||||
offset_t size;
|
||||
|
||||
// The position of the element's data.
|
||||
offset_t data;
|
||||
|
||||
// The element's children.
|
||||
List<Element *> children;
|
||||
|
||||
// True: Treated this element as container and read children.
|
||||
bool populated;
|
||||
|
||||
// The parent element. If NULL (0) this is the document root.
|
||||
Element *parent;
|
||||
|
||||
// The file used to read and write.
|
||||
File *document;
|
||||
|
||||
// Destructor: Clean up all children.
|
||||
~ElementPrivate()
|
||||
{
|
||||
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
|
||||
delete *i;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads a variable length integer from the file at the given position
|
||||
// and saves its value to result. If cutOne is true the first one of the
|
||||
// binary representation of the result is removed (required for size). If
|
||||
// cutOne is false the one will remain in the result (required for id).
|
||||
// This method returns the position directly after the read integer.
|
||||
offset_t readVInt(offset_t position, ulli *result, bool cutOne = true)
|
||||
{
|
||||
document->seek(position);
|
||||
|
||||
// Determine the length of the integer
|
||||
char firstByte = document->readBlock(1)[0];
|
||||
uint byteSize = 1;
|
||||
for(uint i = 0; i < 8 && ((firstByte << i) & (1 << 7)) == 0; ++i)
|
||||
++byteSize;
|
||||
|
||||
// Load the integer
|
||||
document->seek(position);
|
||||
ByteVector vint = document->readBlock(byteSize);
|
||||
|
||||
// Cut the one if requested
|
||||
if(cutOne)
|
||||
vint[0] = (vint[0] & (~(1 << (8 - byteSize))));
|
||||
|
||||
// Store the result and return the current position
|
||||
if(result)
|
||||
*result = static_cast<ulli>(vint.toInt64BE(0));
|
||||
return position + byteSize;
|
||||
}
|
||||
|
||||
// Returns a BytVector containing the given number in the variable integer
|
||||
// format. Truncates numbers > 2^56 (^ means potency in this case).
|
||||
// If addOne is true, the ByteVector will remain the One to determine the
|
||||
// integer's length.
|
||||
// If shortest is true, the ByteVector will be as short as possible (required
|
||||
// for the id)
|
||||
ByteVector createVInt(ulli number, bool addOne = true, bool shortest = true)
|
||||
{
|
||||
ByteVector vint = ByteVector::fromUInt64BE(static_cast<signed long long>(number));
|
||||
|
||||
// Do we actually need to calculate the length of the variable length
|
||||
// integer? If not, then prepend the 0b0000 0001 if necessary and return the
|
||||
// vint.
|
||||
if(!shortest) {
|
||||
if(addOne)
|
||||
vint[0] = 1;
|
||||
return vint;
|
||||
}
|
||||
|
||||
// Calculate the minimal length of the variable length integer
|
||||
uint byteSize = vint.size();
|
||||
for(uint i = 0; byteSize > 0 && vint[i] == 0; ++i)
|
||||
--byteSize;
|
||||
|
||||
if(!addOne)
|
||||
return ByteVector(vint.data() + vint.size() - byteSize, byteSize);
|
||||
|
||||
ulli firstByte = (1 << (vint.size() - byteSize));
|
||||
// The most significant byte loses #bytSize bits for storing information.
|
||||
// Therefore, we might need to increase byteSize.
|
||||
if(number >= (firstByte << (8 * (byteSize - 1))) && byteSize < vint.size())
|
||||
++byteSize;
|
||||
// Add the one at the correct position
|
||||
uint firstBytePosition = vint.size() - byteSize;
|
||||
vint[firstBytePosition] |= (1 << firstBytePosition);
|
||||
return ByteVector(vint.data() + firstBytePosition, byteSize);
|
||||
}
|
||||
|
||||
// Returns a void element within this element which is at least "least" in
|
||||
// size. Uses best fit method. Returns a null pointer if no suitable element
|
||||
// was found.
|
||||
Element *searchVoid(offset_t least = 0L)
|
||||
{
|
||||
Element *currentBest = 0;
|
||||
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
|
||||
if((*i)->d->id == Void &&
|
||||
// We need room for the header if we don't remove the element.
|
||||
((((*i)->d->size + (*i)->d->data - (*i)->d->position) == least || ((*i)->d->size >= least)) &&
|
||||
// best fit
|
||||
(!currentBest || (*i)->d->size < currentBest->d->size))
|
||||
) {
|
||||
currentBest = *i;
|
||||
}
|
||||
}
|
||||
return currentBest;
|
||||
}
|
||||
|
||||
// Replaces this element by a Void element. Returns true on success and false
|
||||
// on error.
|
||||
bool makeVoid()
|
||||
{
|
||||
ulli realSize = size + data - position;
|
||||
ByteVector header(createVInt(Void, false));
|
||||
ulli leftSize = realSize - (header.size() + sizeof(ulli));
|
||||
// Does not make sense to create a Void element
|
||||
if (leftSize > realSize)
|
||||
return false;
|
||||
header.append(createVInt(leftSize, true, false));
|
||||
// Write to file
|
||||
document->seek(position);
|
||||
document->writeBlock(header);
|
||||
// Update data
|
||||
data = position + header.size();
|
||||
size = leftSize;
|
||||
return true;
|
||||
|
||||
// XXX: We actually should merge Voids, if possible.
|
||||
}
|
||||
|
||||
// Reading constructor: Reads all unknown information from the file.
|
||||
ElementPrivate(File *p_document, Element *p_parent = 0, offset_t p_position = 0) :
|
||||
id(0),
|
||||
position(p_position),
|
||||
data(0),
|
||||
populated(false),
|
||||
parent(p_parent),
|
||||
document(p_document)
|
||||
{
|
||||
if(parent) {
|
||||
ulli ssize;
|
||||
data = readVInt(readVInt(position, &id, false), &ssize);
|
||||
size = static_cast<offset_t>(ssize);
|
||||
}
|
||||
else {
|
||||
document->seek(0, File::End);
|
||||
size = document->tell();
|
||||
}
|
||||
}
|
||||
|
||||
// Writing constructor: Takes given information, calculates missing information
|
||||
// and writes everything to the file.
|
||||
// Tries to use void elements if available in the parent.
|
||||
ElementPrivate(ulli p_id, File *p_document, Element *p_parent,
|
||||
offset_t p_position, offset_t p_size) :
|
||||
id(p_id),
|
||||
position(p_position),
|
||||
size(p_size),
|
||||
populated(true), // It is a new element so we know, there are no children.
|
||||
parent(p_parent),
|
||||
document(p_document)
|
||||
{
|
||||
// header
|
||||
ByteVector content(createVInt(id, false).append(createVInt(size, true, false)));
|
||||
data = position + content.size();
|
||||
// space for children
|
||||
content.resize(data - position + size);
|
||||
|
||||
Element *freeSpace;
|
||||
if (!(freeSpace = searchVoid(content.size()))) {
|
||||
// We have to make room
|
||||
document->insert(content, position);
|
||||
// Update parents
|
||||
for(Element *current = parent; current->d->parent; current = current->d->parent) {
|
||||
current->d->size += content.size();
|
||||
// Create new header and write it.
|
||||
ByteVector parentHeader(createVInt(current->d->id, false).append(createVInt(current->d->size, true, false)));
|
||||
uint oldHeaderSize = current->d->data - current->d->position;
|
||||
if(oldHeaderSize < parentHeader.size()) {
|
||||
ByteVector secondHeader(createVInt(current->d->id, false).append(createVInt(current->d->size)));
|
||||
if(oldHeaderSize == secondHeader.size()) {
|
||||
// Write the header where the old one was.
|
||||
document->seek(current->d->position);
|
||||
document->writeBlock(secondHeader);
|
||||
continue; // Very important here!
|
||||
}
|
||||
}
|
||||
// Insert the new header
|
||||
document->insert(parentHeader, current->d->position, oldHeaderSize);
|
||||
current->d->data = current->d->position + parentHeader.size();
|
||||
}
|
||||
}
|
||||
else {
|
||||
document->seek(freeSpace->d->position);
|
||||
if((freeSpace->d->size + freeSpace->d->data - freeSpace->d->position)
|
||||
== static_cast<offset_t>(content.size())) {
|
||||
// Write to file
|
||||
document->writeBlock(content);
|
||||
// Update parent
|
||||
for(List<Element *>::Iterator i = parent->d->children.begin();
|
||||
i != parent->d->children.end(); ++i) {
|
||||
if(freeSpace == *i)
|
||||
parent->d->children.erase(i);
|
||||
}
|
||||
delete freeSpace;
|
||||
}
|
||||
else {
|
||||
ulli newSize = freeSpace->d->size - content.size();
|
||||
ByteVector newVoid(createVInt(Void, false).append(createVInt(newSize, true, false)));
|
||||
|
||||
// Check if the original size of the size field was really 8 byte
|
||||
if (static_cast<offset_t>(newVoid.size()) != (freeSpace->d->data - freeSpace->d->position))
|
||||
newVoid = createVInt(Void, false).append(createVInt(newSize));
|
||||
// Update freeSpace
|
||||
freeSpace->d->size = newSize;
|
||||
freeSpace->d->data = freeSpace->d->position + newVoid.size();
|
||||
// Write to file
|
||||
document->writeBlock(newVoid.resize(newVoid.size() + newSize).append(content));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Element::~Element()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Element::Element(EBML::File *document)
|
||||
: d(new EBML::Element::ElementPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::getChild(EBML::ulli id)
|
||||
{
|
||||
populate();
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
|
||||
++i) {
|
||||
if ((*i)->d->id == id)
|
||||
return *i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<EBML::Element *> EBML::Element::getChildren(EBML::ulli id)
|
||||
{
|
||||
populate();
|
||||
List<Element *> result;
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
|
||||
++i) {
|
||||
if ((*i)->d->id == id)
|
||||
result.append(*i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<EBML::Element *> EBML::Element::getChildren()
|
||||
{
|
||||
populate();
|
||||
return d->children;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::getParent()
|
||||
{
|
||||
return d->parent;
|
||||
}
|
||||
|
||||
ByteVector EBML::Element::getAsBinary()
|
||||
{
|
||||
d->document->seek(d->data);
|
||||
return d->document->readBlock(d->size);
|
||||
}
|
||||
|
||||
String EBML::Element::getAsString()
|
||||
{
|
||||
return String(getAsBinary(), String::UTF8);
|
||||
}
|
||||
|
||||
signed long long EBML::Element::getAsInt()
|
||||
{
|
||||
// The debug note about returning 0 because of empty data is irrelevant. The
|
||||
// behavior is as expected.
|
||||
return getAsBinary().toInt64BE(0);
|
||||
}
|
||||
|
||||
EBML::ulli EBML::Element::getAsUnsigned()
|
||||
{
|
||||
// The debug note about returning 0 because of empty data is irrelevant. The
|
||||
// behavior is as expected.
|
||||
return static_cast<ulli>(getAsBinary().toInt64BE(0));
|
||||
}
|
||||
|
||||
long double EBML::Element::getAsFloat()
|
||||
{
|
||||
// Very dirty implementation!
|
||||
ByteVector bin = getAsBinary();
|
||||
uint size = bin.size();
|
||||
ulli sum = 0.0L;
|
||||
|
||||
// For 0 byte floats and any float that is not defined in the ebml spec.
|
||||
if (size != 4 && size != 8 /*&& size() != 10*/) // XXX: Currently no support for 10 bit floats.
|
||||
return sum;
|
||||
|
||||
// From toNumber; Might not be portable, since it requires IEEE floats.
|
||||
uint last = size - 1;
|
||||
for(uint i = 0; i <= last; i++)
|
||||
sum |= (ulli) uchar(bin[i]) << ((last - i) * 8);
|
||||
|
||||
if (size == 4) {
|
||||
float result = *reinterpret_cast<float *>(&sum);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
double result = *reinterpret_cast<double *>(&sum);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id)
|
||||
{
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(id, d->document, this, d->data + d->size, 0)
|
||||
);
|
||||
d->children.append(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, const ByteVector &binary)
|
||||
{
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(id, d->document, this, d->data + d->size, binary.size())
|
||||
);
|
||||
d->document->seek(elem->d->data);
|
||||
d->document->writeBlock(binary);
|
||||
d->children.append(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, const String &string)
|
||||
{
|
||||
return addElement(id, string.data(String::UTF8));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, signed long long number)
|
||||
{
|
||||
return addElement(id, ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, EBML::ulli number)
|
||||
{
|
||||
return addElement(id, ByteVector::fromUInt64BE(static_cast<signed long long>(number)));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, long double number)
|
||||
{
|
||||
// Probably, we will never need this method.
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChildren(EBML::ulli id, bool useVoid)
|
||||
{
|
||||
bool result = false;
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
if((*i)->d->id == id) {
|
||||
removeChild(*i, useVoid);
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChildren(bool useVoid)
|
||||
{
|
||||
// Maybe a better implementation, because we probably create a lot of voids
|
||||
// in a row where a huge Void would be more appropriate.
|
||||
if (d->children.isEmpty())
|
||||
return false;
|
||||
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
removeChild(*i, useVoid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChild(Element *element, bool useVoid)
|
||||
{
|
||||
if (!d->children.contains(element))
|
||||
return false;
|
||||
|
||||
if(!useVoid || !element->d->makeVoid()) {
|
||||
d->document->removeBlock(element->d->position, element->d->size);
|
||||
// Update parents
|
||||
for(Element* current = this; current; current = current->d->parent)
|
||||
current->d->size -= element->d->size;
|
||||
// Update this element
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
if(element == *i)
|
||||
d->children.erase(i);
|
||||
delete element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EBML::Element::setAsBinary(const ByteVector &binary)
|
||||
{
|
||||
// Maybe: Search for void element after this one
|
||||
d->document->insert(binary, d->data, d->size);
|
||||
}
|
||||
|
||||
void EBML::Element::setAsString(const String &string)
|
||||
{
|
||||
setAsBinary(string.data(String::UTF8));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsInt(signed long long number)
|
||||
{
|
||||
setAsBinary(ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsUnsigned(EBML::ulli number)
|
||||
{
|
||||
setAsBinary(ByteVector::fromUInt64BE(static_cast<signed long long>(number)));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsFloat(long double)
|
||||
{
|
||||
// Probably, we will never need this method.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EBML::Element::populate()
|
||||
{
|
||||
if(!d->populated) {
|
||||
d->populated = true;
|
||||
offset_t end = d->data + d->size;
|
||||
|
||||
for(offset_t i = d->data; i < end;) {
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(d->document, this, i)
|
||||
);
|
||||
d->children.append(elem);
|
||||
i = elem->d->data + elem->d->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Element::Element(EBML::Element::ElementPrivate *pe) : d(pe)
|
||||
{}
|
276
taglib/ebml/ebmlelement.h
Normal file
276
taglib/ebml/ebmlelement.h
Normal file
@ -0,0 +1,276 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLELEMENT_H
|
||||
#define TAGLIB_EBMLELEMENT_H
|
||||
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tstring.h"
|
||||
|
||||
#include "ebmlfile.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
/*!
|
||||
* Represents an element of the EBML. The only instance of this child, that
|
||||
* is directly used is the root element. Every other element is accessed
|
||||
* via pointers to the elements within the root element.
|
||||
*
|
||||
* Just create one root instance per file to prevent race conditions.
|
||||
*
|
||||
* Changes of the document tree will be directly written back to the file.
|
||||
* Invalid values (exceeding the maximal value defined in the RFC) will be
|
||||
* truncated.
|
||||
*
|
||||
* This class should not be used by library users since the proper file
|
||||
* class should handle the internals.
|
||||
*
|
||||
* NOTE: Currently does not adjust CRC32 values.
|
||||
*/
|
||||
class TAGLIB_EXPORT Element
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the element.
|
||||
~Element();
|
||||
|
||||
/*!
|
||||
* Creates an root element using document.
|
||||
*/
|
||||
Element(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the first found child element with the given id. Returns a null
|
||||
* pointer if the child does not exist.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *getChild(const ulli id);
|
||||
|
||||
/*!
|
||||
* Returns a list of all child elements with the given id. Returns an
|
||||
* empty list if no such element exists.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
List<Element *> getChildren(const ulli id);
|
||||
|
||||
/*!
|
||||
* Returns a list of every child elements available. Returns an empty list
|
||||
* if there are no children.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
List<Element *> getChildren();
|
||||
|
||||
/*!
|
||||
* Returns the parent element or null if no such element exists.
|
||||
*/
|
||||
Element *getParent();
|
||||
|
||||
/*!
|
||||
* Returns the raw content of the element.
|
||||
*/
|
||||
ByteVector getAsBinary();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as a string.
|
||||
*/
|
||||
String getAsString();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as an signed integer.
|
||||
*
|
||||
* Do not call this method if *this element is not an INT element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
signed long long getAsInt();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as an unsigned integer.
|
||||
*
|
||||
* Do not call this method if *this element is not an UINT element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
ulli getAsUnsigned();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as a floating point
|
||||
* type.
|
||||
*
|
||||
* Do not call this method if *this element is not an FLOAT element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* NOTE: There are 10 byte floats defined, therefore we might need a long
|
||||
* double to store the value.
|
||||
*/
|
||||
long double getAsFloat();
|
||||
|
||||
/*!
|
||||
* Adds an empty element with given id to this element. Returns a pointer
|
||||
* to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given binary, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, const ByteVector &binary);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given string, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, const String &string);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given integer, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, signed long long number);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given unsigned integer, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, ulli number);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given floating point value, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* This method is not implemented!
|
||||
*/
|
||||
Element *addElement(ulli id, long double number);
|
||||
|
||||
/*!
|
||||
* Removes all children with the given id. Returns false if there was no
|
||||
* such element.
|
||||
* If useVoid is true, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* Every pointer to a removed element is invalidated.
|
||||
*/
|
||||
bool removeChildren(ulli id, bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Removes all children. Returns false if this element had no children.
|
||||
* If useVoid ist rue, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* Every pointer to a removed element is invalidated.
|
||||
*/
|
||||
bool removeChildren(bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Removes the given element.
|
||||
* If useVoid is true, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* The pointer to the given element is invalidated.
|
||||
*/
|
||||
bool removeChild(Element *element, bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Writes the given binary to this element.
|
||||
*/
|
||||
void setAsBinary(const ByteVector &binary);
|
||||
|
||||
/*!
|
||||
* Writes the given string to this element.
|
||||
*/
|
||||
void setAsString(const String &string);
|
||||
|
||||
/*!
|
||||
* Writes the given integer to this element.
|
||||
*/
|
||||
void setAsInt(signed long long number);
|
||||
|
||||
/*!
|
||||
* Writes the given unsigned integer to this element.
|
||||
*/
|
||||
void setAsUnsigned(ulli number);
|
||||
|
||||
/*!
|
||||
* Writes the given floating point variable to this element.
|
||||
*
|
||||
* This method is not implemented!
|
||||
*/
|
||||
void setAsFloat(long double number);
|
||||
|
||||
private:
|
||||
//! Non-copyable
|
||||
Element(const Element &);
|
||||
//! Non-copyable
|
||||
Element &operator=(const File &);
|
||||
|
||||
//! Lazy parsing. This method will be triggered when trying to access
|
||||
//! children.
|
||||
void populate();
|
||||
|
||||
class ElementPrivate;
|
||||
ElementPrivate *d;
|
||||
|
||||
//! Creates a new Element from an ElementPrivate. (The constructor takes
|
||||
//! ownership of the pointer and will delete it when the element is
|
||||
//! destroyed.
|
||||
Element(ElementPrivate *pe);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
102
taglib/ebml/ebmlfile.cpp
Normal file
102
taglib/ebml/ebmlfile.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(File *document) : root(document)
|
||||
{
|
||||
}
|
||||
|
||||
// Performs a few basic checks and creates the FilePrivate if they were
|
||||
// successful.
|
||||
static FilePrivate *checkAndCreate(File* document)
|
||||
{
|
||||
document->seek(0);
|
||||
ByteVector magical = document->readBlock(4);
|
||||
if(static_cast<ulli>(magical.toInt64BE(0)) != Header::EBML)
|
||||
return 0;
|
||||
FilePrivate *d = new FilePrivate(document);
|
||||
Element *head = d->root.getChild(Header::EBML);
|
||||
Element *p;
|
||||
if(!head ||
|
||||
!((p = head->getChild(Header::EBMLVersion)) && p->getAsUnsigned() == 1L) ||
|
||||
!((p = head->getChild(Header::EBMLReadVersion)) && p->getAsUnsigned() == 1L) ||
|
||||
// Actually 4 is the current maximum of the EBML spec, but we support up to 8
|
||||
!((p = head->getChild(Header::EBMLMaxIDWidth)) && p->getAsUnsigned() <= 8) ||
|
||||
!((p = head->getChild(Header::EBMLMaxSizeWidth)) && p->getAsUnsigned() <= 8)
|
||||
) {
|
||||
delete d;
|
||||
return 0;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
Element root;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::File::getDocumentRoot()
|
||||
{
|
||||
if(!d && isValid())
|
||||
d = new FilePrivate(this);
|
||||
return &d->root;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::File::File(FileName file) :
|
||||
TagLib::File(file)
|
||||
{
|
||||
if(isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
}
|
||||
}
|
||||
|
||||
EBML::File::File(IOStream *stream) :
|
||||
TagLib::File(stream)
|
||||
{
|
||||
if(isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
}
|
||||
}
|
86
taglib/ebml/ebmlfile.h
Normal file
86
taglib/ebml/ebmlfile.h
Normal file
@ -0,0 +1,86 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLFILE_H
|
||||
#define TAGLIB_EBMLFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
|
||||
#include "ebmlconstants.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! A namespace for the classes used by EBML-based metadata files
|
||||
namespace EBML {
|
||||
|
||||
class Element;
|
||||
|
||||
/*!
|
||||
* Represents an EBML file. It offers access to the root element which can
|
||||
* be used to obtain the necessary information and to change the file
|
||||
* according to changes.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the file.
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the document root element of the EBML file.
|
||||
*/
|
||||
Element *getDocumentRoot();
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Constructs an instance of an EBML file from \a file.
|
||||
*
|
||||
* This constructor is protected since an object should be created
|
||||
* through a specific subclass.
|
||||
*/
|
||||
File(FileName file);
|
||||
|
||||
/*!
|
||||
* Constructs an instance of an EBML file from an IOStream.
|
||||
*
|
||||
* This constructor is protected since an object should be created
|
||||
* through a specific subclass.
|
||||
*/
|
||||
File(IOStream *stream);
|
||||
|
||||
private:
|
||||
//! Non-copyable
|
||||
File(const File&);
|
||||
File &operator=(const File &);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
120
taglib/ebml/matroska/ebmlmatroskaaudio.cpp
Normal file
120
taglib/ebml/matroska/ebmlmatroskaaudio.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlmatroskaconstants.h"
|
||||
#include "ebmlmatroskaaudio.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Matroska::AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
AudioPropertiesPrivate(File *p_document) :
|
||||
document(p_document),
|
||||
length(0),
|
||||
bitrate(0),
|
||||
channels(1),
|
||||
samplerate(8000)
|
||||
{
|
||||
Element *elem = document->getDocumentRoot()->getChild(Constants::Segment);
|
||||
Element *info = elem->getChild(Constants::SegmentInfo);
|
||||
Element *value;
|
||||
|
||||
if(info && (value = info->getChild(Constants::Duration))) {
|
||||
length = static_cast<int>(value->getAsFloat());
|
||||
if((value = info->getChild(Constants::TimecodeScale))){
|
||||
length /= (value->getAsUnsigned() * (1 / 1000000000));
|
||||
}
|
||||
}
|
||||
|
||||
info = elem->getChild(Constants::Tracks);
|
||||
if(!info || !(info = info->getChild(Constants::TrackEntry)) ||
|
||||
!(info = info->getChild(Constants::Audio))) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Dirty bitrate:
|
||||
document->seek(0, File::End);
|
||||
bitrate = ((8 * document->tell()) / length) / 1000;
|
||||
|
||||
if((value = info->getChild(Constants::Channels)))
|
||||
channels = value->getAsUnsigned();
|
||||
|
||||
if((value = info->getChild(Constants::SamplingFrequency)))
|
||||
samplerate = static_cast<int>(value->getAsFloat());
|
||||
}
|
||||
|
||||
// The corresponding file
|
||||
File *document;
|
||||
|
||||
// The length of the file
|
||||
int length;
|
||||
|
||||
// The bitrate
|
||||
int bitrate;
|
||||
|
||||
// The amount of channels
|
||||
int channels;
|
||||
|
||||
// The sample rate
|
||||
int samplerate;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::AudioProperties::AudioProperties(File *document) :
|
||||
TagLib::AudioProperties(TagLib::AudioProperties::Fast),
|
||||
d(new AudioPropertiesPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Matroska::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::length() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->samplerate;
|
||||
}
|
88
taglib/ebml/matroska/ebmlmatroskaaudio.h
Normal file
88
taglib/ebml/matroska/ebmlmatroskaaudio.h
Normal file
@ -0,0 +1,88 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLMATROSKAAUDIO_H
|
||||
#define TAGLIB_EBMLMATROSKAAUDIO_H
|
||||
|
||||
#include "ebmlmatroskafile.h"
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
namespace Matroska {
|
||||
|
||||
/*!
|
||||
* This class represents the audio properties of a matroska file.
|
||||
* Currently all information are read from the container format and
|
||||
* could be inexact.
|
||||
*/
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
//! Destructor
|
||||
virtual ~AudioProperties();
|
||||
|
||||
/*!
|
||||
* Constructs an instance from a file.
|
||||
*/
|
||||
AudioProperties(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the length of the file.
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the bit rate of the file. Since the container format does not
|
||||
* offer a proper value, it ist currently calculated by dividing the
|
||||
* file size by the length.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the amount of channels of the file.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate of the file.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
|
||||
private:
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
140
taglib/ebml/matroska/ebmlmatroskaconstants.h
Normal file
140
taglib/ebml/matroska/ebmlmatroskaconstants.h
Normal file
@ -0,0 +1,140 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLMATROSKACONSTANTS_H
|
||||
#define TAGLIB_EBMLMATROSKACONSTANTS_H
|
||||
|
||||
#include "ebmlconstants.h"
|
||||
#include "tstring.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
namespace Matroska {
|
||||
|
||||
namespace Constants {
|
||||
|
||||
//! ID of an Matroska segment.
|
||||
const ulli Segment = 0x18538067L;
|
||||
|
||||
//! ID of the tags element.
|
||||
const ulli Tags = 0x1254c367L;
|
||||
|
||||
//! ID of the tag element.
|
||||
const ulli Tag = 0x7373L;
|
||||
|
||||
//! ID of the targets element.
|
||||
const ulli Targets = 0x63c0L;
|
||||
|
||||
//! ID of the target type value element.
|
||||
const ulli TargetTypeValue = 0x68caL;
|
||||
|
||||
//! ID of the target type element.
|
||||
const ulli TargetType = 0x63caL;
|
||||
|
||||
//! ID of a simple tag element.
|
||||
const ulli SimpleTag = 0x67c8L;
|
||||
|
||||
//! ID of the tag name.
|
||||
const ulli TagName = 0x45a3L;
|
||||
|
||||
//! ID of the tag content.
|
||||
const ulli TagString = 0x4487L;
|
||||
|
||||
//! The DocType of a matroska file.
|
||||
const String DocTypeMatroska = "matroska";
|
||||
|
||||
//! The DocType of a WebM file.
|
||||
const String DocTypeWebM = "webm";
|
||||
|
||||
|
||||
|
||||
//! The TITLE entry
|
||||
const String TITLE = "TITLE";
|
||||
|
||||
//! The ARTIST entry
|
||||
const String ARTIST = "ARTIST";
|
||||
|
||||
//! The COMMENT entry
|
||||
const String COMMENT = "COMMENT";
|
||||
|
||||
//! The GENRE entry
|
||||
const String GENRE = "GENRE";
|
||||
|
||||
//! The DATE_RELEASE entry
|
||||
const String DATE_RELEASE = "DATE_RELEASE";
|
||||
|
||||
//! The PART_NUMBER entry
|
||||
const String PART_NUMBER = "PART_NUMBER";
|
||||
|
||||
//! The TargetTypeValue of the most common grouping level (e.g. album)
|
||||
const ulli MostCommonGroupingValue = 50;
|
||||
|
||||
//! The TargetTypeValue of the most common parts of a group (e.g. track)
|
||||
const ulli MostCommonPartValue = 30;
|
||||
|
||||
//! Name of the TargetType of an album.
|
||||
const String ALBUM = "ALBUM";
|
||||
|
||||
//! Name of the TargetType of a track.
|
||||
const String TRACK = "TRACK";
|
||||
|
||||
|
||||
|
||||
// For AudioProperties
|
||||
|
||||
//! ID of the Info block within the Segment.
|
||||
const ulli SegmentInfo = 0x1549a966L;
|
||||
|
||||
//! ID of the duration element.
|
||||
const ulli Duration = 0x4489L;
|
||||
|
||||
//! ID of TimecodeScale element.
|
||||
const ulli TimecodeScale = 0x2ad7b1L;
|
||||
|
||||
//! ID of the Tracks container
|
||||
const ulli Tracks = 0x1654ae6bL;
|
||||
|
||||
//! ID of a TrackEntry element.
|
||||
const ulli TrackEntry = 0xaeL;
|
||||
|
||||
//! ID of the Audio container.
|
||||
const ulli Audio = 0xe1L;
|
||||
|
||||
//! ID of the SamplingFrequency element.
|
||||
const ulli SamplingFrequency = 0xb5L;
|
||||
|
||||
//! ID of the Channels element.
|
||||
const ulli Channels = 0x9fL;
|
||||
|
||||
//! ID of the BitDepth element.
|
||||
const ulli BitDepth = 0x6264L;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
549
taglib/ebml/matroska/ebmlmatroskafile.cpp
Normal file
549
taglib/ebml/matroska/ebmlmatroskafile.cpp
Normal file
@ -0,0 +1,549 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlmatroskaconstants.h"
|
||||
#include "ebmlmatroskaaudio.h"
|
||||
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Matroska::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
// Returns true if simpleTag has a TagName and TagString child and writes
|
||||
// their contents into name and value.
|
||||
bool extractContent(Element *simpleTag, String &name, String &value)
|
||||
{
|
||||
Element *n = simpleTag->getChild(Constants::TagName);
|
||||
Element *v = simpleTag->getChild(Constants::TagString);
|
||||
if(!n || !v)
|
||||
return false;
|
||||
|
||||
name = n->getAsString();
|
||||
value = v->getAsString();
|
||||
return true;
|
||||
}
|
||||
|
||||
FilePrivate(File *p_document) : tag(0), document(p_document)
|
||||
{
|
||||
// Just get the first segment, because "Typically a Matroska file is
|
||||
// composed of 1 segment."
|
||||
Element* elem = document->getDocumentRoot()->getChild(Constants::Segment);
|
||||
|
||||
// We take the first tags element (there shouldn't be more), if there is
|
||||
// non such element, consider the file as not compatible.
|
||||
if(!elem || !(elem = elem->getChild(Constants::Tags))) {
|
||||
document->setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load all Tag entries
|
||||
List<Element *> entries = elem->getChildren(Constants::Tag);
|
||||
for(List<Element *>::Iterator i = entries.begin(); i != entries.end(); ++i) {
|
||||
Element *target = (*i)->getChild(Constants::Targets);
|
||||
ulli ttvalue = 0;
|
||||
if(target && (target = (*i)->getChild(Constants::TargetTypeValue)))
|
||||
ttvalue = target->getAsUnsigned();
|
||||
|
||||
// Load all SimpleTags
|
||||
PropertyMap tagEntries;
|
||||
List<Element *> simpleTags = (*i)->getChildren(Constants::SimpleTag);
|
||||
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end();
|
||||
++j) {
|
||||
String name, value;
|
||||
if(!extractContent(*j, name, value))
|
||||
continue;
|
||||
tagEntries.insert(name, StringList(value));
|
||||
}
|
||||
|
||||
tags.append(std::pair<PropertyMap, std::pair<Element *, ulli> >(tagEntries, std::pair<Element *, ulli>(*i, ttvalue)));
|
||||
}
|
||||
}
|
||||
|
||||
// Creates Tag and AudioProperties. Late creation because both require a fully
|
||||
// functional FilePrivate (well AudioProperties doesn't...)
|
||||
void lateCreate()
|
||||
{
|
||||
tag = new Tag(document);
|
||||
audio = new AudioProperties(document);
|
||||
}
|
||||
|
||||
// Checks the EBML header and creates the FilePrivate.
|
||||
static FilePrivate *checkAndCreate(File *document)
|
||||
{
|
||||
Element *elem = document->getDocumentRoot()->getChild(Header::EBML);
|
||||
Element *child = elem->getChild(Header::DocType);
|
||||
if(child) {
|
||||
String dt = child->getAsString();
|
||||
if (dt == Constants::DocTypeMatroska || dt == Constants::DocTypeWebM) {
|
||||
FilePrivate *fp = new FilePrivate(document);
|
||||
return fp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The tags with their Element and TargetTypeValue
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > > tags;
|
||||
|
||||
// The tag
|
||||
Tag *tag;
|
||||
|
||||
// The audio properties
|
||||
AudioProperties *audio;
|
||||
|
||||
// The corresponding file.
|
||||
File *document;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::File::~File()
|
||||
{
|
||||
delete d->tag;
|
||||
delete d->audio;
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Matroska::File::File(FileName file) : EBML::File(file), d(0)
|
||||
{
|
||||
if(isValid() && isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
else
|
||||
d->lateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Matroska::File::File(IOStream *stream) : EBML::File(stream), d(0)
|
||||
{
|
||||
if(isValid() && isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
else
|
||||
d->lateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
Tag *EBML::Matroska::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap EBML::Matroska::File::properties() const
|
||||
{
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
if(best == d->tags.end() || best->second.second > i->second.second)
|
||||
best = i;
|
||||
}
|
||||
return best->first;
|
||||
}
|
||||
|
||||
PropertyMap EBML::Matroska::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
if(best == d->tags.end() || best->second.second > i->second.second)
|
||||
best = i;
|
||||
}
|
||||
|
||||
std::pair<PropertyMap, std::pair<Element *, ulli> > replace(properties, best->second);
|
||||
d->tags.erase(best);
|
||||
d->tags.prepend(replace);
|
||||
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
AudioProperties *EBML::Matroska::File::audioProperties() const
|
||||
{
|
||||
return d->audio;
|
||||
}
|
||||
|
||||
bool EBML::Matroska::File::save()
|
||||
{
|
||||
if(readOnly())
|
||||
return false;
|
||||
|
||||
// C++11 features would be nice: for(auto &i : d->tags) { /* ... */ }
|
||||
// Well, here we just iterate over each extracted element.
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
|
||||
for(PropertyMap::Iterator j = i->first.begin(); j != i->first.end(); ++j) {
|
||||
|
||||
// No element? Create it!
|
||||
if(!i->second.first) {
|
||||
// Should be save, since we already checked, when creating the object.
|
||||
Element *container = d->document->getDocumentRoot()
|
||||
->getChild(Constants::Segment)->getChild(Constants::Tags);
|
||||
|
||||
// Create Targets container
|
||||
i->second.first = container->addElement(Constants::Tag);
|
||||
Element *target = i->second.first->addElement(Constants::Targets);
|
||||
|
||||
if(i->second.second == Constants::MostCommonPartValue)
|
||||
target->addElement(Constants::TargetType, Constants::TRACK);
|
||||
else if(i->second.second == Constants::MostCommonGroupingValue)
|
||||
target->addElement(Constants::TargetType, Constants::ALBUM);
|
||||
|
||||
target->addElement(Constants::TargetTypeValue, i->second.second);
|
||||
}
|
||||
|
||||
// Find entries
|
||||
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
|
||||
StringList::Iterator str = j->second.begin();
|
||||
List<Element *>::Iterator k = simpleTags.begin();
|
||||
for(; k != simpleTags.end(); ++k) {
|
||||
|
||||
String name, value;
|
||||
if(!d->extractContent(*k, name, value))
|
||||
continue;
|
||||
|
||||
// Write entry from StringList
|
||||
if(name == j->first) {
|
||||
if(str == j->second.end()) {
|
||||
// We have all StringList elements but still found another element
|
||||
// with the same name? Let's delete it!
|
||||
i->second.first->removeChild(*k);
|
||||
}
|
||||
else {
|
||||
if(value != *str) {
|
||||
// extractContent already checked for availability
|
||||
(*k)->getChild(Constants::TagString)->setAsString(*str);
|
||||
}
|
||||
++str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't write the complete StringList, we have to write the rest.
|
||||
for(; str != j->second.end(); ++str) {
|
||||
Element *stag = i->second.first->addElement(Constants::SimpleTag);
|
||||
stag->addElement(Constants::TagName, j->first);
|
||||
stag->addElement(Constants::TagString, *str);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we have to find elements that are not in the PropertyMap and
|
||||
// remove them.
|
||||
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
|
||||
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end(); ++j) {
|
||||
|
||||
String name, value;
|
||||
if(!d->extractContent(*j, name, value))
|
||||
continue;
|
||||
|
||||
if(i->first.find(name) == i->first.end()){
|
||||
i->second.first->removeChild(*j);}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Tag
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class EBML::Matroska::File::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
// Creates a TagPrivate instance
|
||||
TagPrivate(File *p_document) :
|
||||
document(p_document),
|
||||
title(document->d->tags.end()),
|
||||
artist(document->d->tags.end()),
|
||||
album(document->d->tags.end()),
|
||||
comment(document->d->tags.end()),
|
||||
genre(document->d->tags.end()),
|
||||
year(document->d->tags.end()),
|
||||
track(document->d->tags.end())
|
||||
{
|
||||
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
// Just save it, if the title is more specific, or there is no title yet.
|
||||
if(i->first.find(Constants::TITLE) != i->first.end() &&
|
||||
(title == document->d->tags.end() ||
|
||||
title->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
title = i;
|
||||
}
|
||||
|
||||
// Same goes for artist.
|
||||
if(i->first.find(Constants::ARTIST) != i->first.end() &&
|
||||
(artist == document->d->tags.end() ||
|
||||
artist->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
artist = i;
|
||||
}
|
||||
|
||||
// Here, we also look for a title (the album title), but since we
|
||||
// specified the granularity, we have to search for it exactly.
|
||||
// Therefore it is possible, that title and album are the same (if only
|
||||
// the title of the album is given).
|
||||
if(i->first.find(Constants::TITLE) != i->first.end() &&
|
||||
i->second.second == Constants::MostCommonGroupingValue) {
|
||||
|
||||
album = i;
|
||||
}
|
||||
|
||||
// Again the same as title and artist.
|
||||
if(i->first.find(Constants::COMMENT) != i->first.end() &&
|
||||
(comment == document->d->tags.end() ||
|
||||
comment->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
comment = i;
|
||||
}
|
||||
|
||||
// Same goes for genre.
|
||||
if(i->first.find(Constants::GENRE) != i->first.end() &&
|
||||
(genre == document->d->tags.end() ||
|
||||
genre->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
genre = i;
|
||||
}
|
||||
|
||||
// And year (in our case: DATE_REALEASE)
|
||||
if(i->first.find(Constants::DATE_RELEASE) != i->first.end() &&
|
||||
(year == document->d->tags.end() ||
|
||||
year->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
year = i;
|
||||
}
|
||||
|
||||
// And track (in our case: PART_NUMBER)
|
||||
if(i->first.find(Constants::PART_NUMBER) != i->first.end() &&
|
||||
(track == document->d->tags.end() ||
|
||||
track->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
track = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Searches for the Tag with given TargetTypeValue (returns the first one)
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator
|
||||
find(ulli ttv)
|
||||
{
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
if(i->second.second == ttv)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the given information
|
||||
void update(
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator t,
|
||||
const String &tagname,
|
||||
const String &s
|
||||
)
|
||||
{
|
||||
t->first.find(tagname)->second.front() = s;
|
||||
}
|
||||
|
||||
// Inserts a tag with given information
|
||||
void insert(const String &tagname, const ulli ttv, const String &s)
|
||||
{
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
if(i->second.second == ttv) {
|
||||
i->first.insert(tagname, StringList(s));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found? Create new!
|
||||
PropertyMap pm;
|
||||
pm.insert(tagname, StringList(s));
|
||||
document->d->tags.append(
|
||||
std::pair<PropertyMap, std::pair<Element *, ulli> >(pm,
|
||||
std::pair<Element *, ulli>(0, ttv)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// The PropertyMap from the Matroska::File
|
||||
File *document;
|
||||
|
||||
// Iterators to the tags.
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator title;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator artist;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator album;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator comment;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator genre;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator year;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator track;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::File::Tag::~Tag()
|
||||
{
|
||||
delete e;
|
||||
}
|
||||
|
||||
EBML::Matroska::File::Tag::Tag(EBML::Matroska::File *document) :
|
||||
e(new EBML::Matroska::File::Tag::TagPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::title() const
|
||||
{
|
||||
if(e->title != e->document->d->tags.end())
|
||||
return e->title->first.find(Constants::TITLE)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::artist() const
|
||||
{
|
||||
if(e->artist != e->document->d->tags.end())
|
||||
return e->artist->first.find(Constants::ARTIST)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::album() const
|
||||
{
|
||||
if(e->album != e->document->d->tags.end())
|
||||
return e->album->first.find(Constants::TITLE)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::comment() const
|
||||
{
|
||||
if(e->comment != e->document->d->tags.end())
|
||||
return e->comment->first.find(Constants::COMMENT)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::genre() const
|
||||
{
|
||||
if(e->genre != e->document->d->tags.end())
|
||||
return e->genre->first.find(Constants::GENRE)->second.front();
|
||||
else
|
||||
return String::null;
|
||||
}
|
||||
|
||||
uint EBML::Matroska::File::Tag::year() const
|
||||
{
|
||||
if(e->year != e->document->d->tags.end())
|
||||
return e->year->first.find(Constants::DATE_RELEASE)->second.front().toInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint EBML::Matroska::File::Tag::track() const
|
||||
{
|
||||
if(e->track != e->document->d->tags.end())
|
||||
return e->track->first.find(Constants::PART_NUMBER)->second.front().toInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setTitle(const String &s)
|
||||
{
|
||||
if(e->title != e->document->d->tags.end())
|
||||
e->update(e->title, Constants::TITLE, s);
|
||||
else
|
||||
e->insert(Constants::TITLE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setArtist(const String &s)
|
||||
{
|
||||
if(e->artist != e->document->d->tags.end())
|
||||
e->update(e->artist, Constants::ARTIST, s);
|
||||
else
|
||||
e->insert(Constants::ARTIST, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setAlbum(const String &s)
|
||||
{
|
||||
if(e->album != e->document->d->tags.end())
|
||||
e->update(e->album, Constants::TITLE, s);
|
||||
else
|
||||
e->insert(Constants::TITLE, Constants::MostCommonGroupingValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setComment(const String &s)
|
||||
{
|
||||
if(e->comment != e->document->d->tags.end())
|
||||
e->update(e->comment, Constants::COMMENT, s);
|
||||
else
|
||||
e->insert(Constants::COMMENT, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setGenre(const String &s)
|
||||
{
|
||||
if(e->genre != e->document->d->tags.end())
|
||||
e->update(e->genre, Constants::GENRE, s);
|
||||
else
|
||||
e->insert(Constants::GENRE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setYear(uint i)
|
||||
{
|
||||
String s = String::number(i);
|
||||
if(e->year != e->document->d->tags.end())
|
||||
e->update(e->year, Constants::DATE_RELEASE, s);
|
||||
else
|
||||
e->insert(Constants::DATE_RELEASE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setTrack(uint i)
|
||||
{
|
||||
String s = String::number(i);
|
||||
if(e->track != e->document->d->tags.end())
|
||||
e->update(e->track, Constants::PART_NUMBER, s);
|
||||
else
|
||||
e->insert(Constants::PART_NUMBER, Constants::MostCommonPartValue, s);
|
||||
}
|
201
taglib/ebml/matroska/ebmlmatroskafile.h
Normal file
201
taglib/ebml/matroska/ebmlmatroskafile.h
Normal file
@ -0,0 +1,201 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef TAGLIB_EBMLMATROSKAFILE_H
|
||||
#define TAGLIB_EBMLMATROSKAFILE_H
|
||||
|
||||
#include "ebmlelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
//! Implementation for reading Matroska tags.
|
||||
namespace Matroska {
|
||||
|
||||
/*!
|
||||
* Implements the TagLib::File API and offers access to the tags of the
|
||||
* matroska file.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public EBML::File
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the file.
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska File from a file name.
|
||||
*/
|
||||
File(FileName file);
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska File from a stream.
|
||||
*/
|
||||
File(IOStream *stream);
|
||||
|
||||
/*!
|
||||
* Returns the pointer to a tag that allow access on common tags.
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Exports the tags to a PropertyMap. Due to the diversity of the
|
||||
* Matroska format (e.g. multiple media streams in one file, each with
|
||||
* its own tags), only the best fitting tags are taken into account.
|
||||
* There are no unsupported tags.
|
||||
*/
|
||||
virtual PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Sets the tags of the file to those specified in properties. The
|
||||
* returned PropertyMap is always empty.
|
||||
* Note: Only the best fitting tags are taken into account.
|
||||
*/
|
||||
virtual PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to this file's audio properties.
|
||||
*
|
||||
* I'm glad about not having a setAudioProperties method ;)
|
||||
*/
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Saves the file. Returns true on success.
|
||||
*/
|
||||
bool save();
|
||||
|
||||
/*!
|
||||
* Offers access to a few common tag entries.
|
||||
*/
|
||||
class Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
//! Destroys the tag.
|
||||
~Tag();
|
||||
|
||||
/*!
|
||||
* Creates a new Tag for Matroska files. The given properties are gained
|
||||
* by the Matroska::File.
|
||||
*/
|
||||
Tag(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String title() const;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String artist() const;
|
||||
|
||||
/*!
|
||||
* Returns the album name; if no album name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String album() const;
|
||||
|
||||
/*!
|
||||
* Returns the track comment; if no comment is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String comment() const;
|
||||
|
||||
/*!
|
||||
* Returns the genre name; if no genre is present in the tag String::null
|
||||
* will be returned.
|
||||
*/
|
||||
virtual String genre() const;
|
||||
|
||||
/*!
|
||||
* Returns the year; if there is no year set, this will return 0.
|
||||
*/
|
||||
virtual uint year() const;
|
||||
|
||||
/*!
|
||||
* Returns the track number; if there is no track number set, this will
|
||||
* return 0.
|
||||
*/
|
||||
virtual uint track() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setTitle(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the artist to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setArtist(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the album to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setAlbum(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the comment to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setComment(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the genre to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setGenre(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the year to i. If s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setYear(uint i);
|
||||
|
||||
/*!
|
||||
* Sets the track to i. If s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setTrack(uint i);
|
||||
|
||||
private:
|
||||
class TagPrivate;
|
||||
TagPrivate *e;
|
||||
};
|
||||
|
||||
private:
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -66,7 +66,7 @@ class FileRef::FileRefPrivate
|
||||
public:
|
||||
FileRefPrivate(File *f) : file(f) {}
|
||||
|
||||
RefCountPtr<File> file;
|
||||
TAGLIB_SHARED_PTR<File> file;
|
||||
|
||||
static List<const FileTypeResolver *> fileTypeResolvers;
|
||||
};
|
||||
|
@ -278,7 +278,7 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class FileRefPrivate;
|
||||
RefCountPtr<FileRefPrivate> d;
|
||||
TAGLIB_SHARED_PTR<FileRefPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace TagLib
|
||||
|
@ -66,7 +66,7 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class CoverArtPrivate;
|
||||
RefCountPtr<CoverArtPrivate> d;
|
||||
TAGLIB_SHARED_PTR<CoverArtPrivate> d;
|
||||
};
|
||||
|
||||
typedef List<CoverArt> CoverArtList;
|
||||
|
@ -27,19 +27,44 @@
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "mp4item.h"
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later
|
||||
# define SPRINTF sprintf_s
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
String format(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args,fmt);
|
||||
|
||||
char buf[256];
|
||||
|
||||
#if defined(HAVE_SNPRINTF)
|
||||
|
||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
|
||||
#elif defined(HAVE_SPRINTF_S)
|
||||
|
||||
vsprintf_s(buf, fmt, args);
|
||||
|
||||
#else
|
||||
# define SPRINTF sprintf
|
||||
|
||||
// Be careful. May cause a buffer overflow.
|
||||
vsprintf(buf, fmt, args);
|
||||
|
||||
#endif
|
||||
|
||||
using namespace TagLib;
|
||||
va_end(args);
|
||||
|
||||
return String(buf);
|
||||
}
|
||||
}
|
||||
|
||||
class MP4::Item::ItemPrivate
|
||||
{
|
||||
@ -242,37 +267,31 @@ String
|
||||
MP4::Item::toString() const
|
||||
{
|
||||
StringList desc;
|
||||
char tmp[256];
|
||||
switch (d->type) {
|
||||
case TypeBool:
|
||||
return d->m_bool ? "true" : "false";
|
||||
case TypeInt:
|
||||
SPRINTF(tmp, "%d", d->m_int);
|
||||
return tmp;
|
||||
return format("%d", d->m_int);
|
||||
case TypeIntPair:
|
||||
SPRINTF(tmp, "%d/%d", d->m_intPair.first, d->m_intPair.second);
|
||||
return tmp;
|
||||
return format("%d/%d", d->m_intPair.first, d->m_intPair.second);
|
||||
case TypeByte:
|
||||
SPRINTF(tmp, "%d", d->m_byte);
|
||||
return tmp;
|
||||
return format("%d", d->m_byte);
|
||||
case TypeUInt:
|
||||
SPRINTF(tmp, "%u", d->m_uint);
|
||||
return tmp;
|
||||
return format("%u", d->m_uint);
|
||||
case TypeLongLong:
|
||||
SPRINTF(tmp, "%lld", d->m_longlong);
|
||||
return tmp;
|
||||
return format("%lld", d->m_longlong);
|
||||
case TypeStringList:
|
||||
return d->m_stringList.toString(" / ");
|
||||
case TypeByteVectorList:
|
||||
for(TagLib::uint i = 0; i < d->m_byteVectorList.size(); i++) {
|
||||
SPRINTF(tmp, "[%d bytes of data]", static_cast<int>(d->m_byteVectorList[i].size()));
|
||||
desc.append(tmp);
|
||||
desc.append(format(
|
||||
"[%d bytes of data]", static_cast<int>(d->m_byteVectorList[i].size())));
|
||||
}
|
||||
return desc.toString(", ");
|
||||
case TypeCoverArtList:
|
||||
for(TagLib::uint i = 0; i < d->m_coverArtList.size(); i++) {
|
||||
SPRINTF(tmp, "[%d bytes of data]", static_cast<int>(d->m_coverArtList[i].data().size()));
|
||||
desc.append(tmp);
|
||||
desc.append(format(
|
||||
"[%d bytes of data]", static_cast<int>(d->m_coverArtList[i].data().size())));
|
||||
}
|
||||
return desc.toString(", ");
|
||||
case TypeUndefined:
|
||||
|
@ -97,7 +97,7 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class ItemPrivate;
|
||||
RefCountPtr<ItemPrivate> d;
|
||||
TAGLIB_SHARED_PTR<ItemPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ namespace TagLib {
|
||||
void parse(const ByteVector &data);
|
||||
|
||||
class HeaderPrivate;
|
||||
RefCountPtr<HeaderPrivate> d;
|
||||
TAGLIB_SHARED_PTR<HeaderPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
//
|
||||
// http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp
|
||||
|
||||
#define DATA(x) (&(x->data->data[0]))
|
||||
#define DATA(x) (&(*(x->data))[0])
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
@ -307,43 +307,17 @@ inline void appendNumber(ByteVector &v, T value)
|
||||
::memcpy(v.data() + offset, reinterpret_cast<const char *>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
class DataPrivate
|
||||
{
|
||||
public:
|
||||
DataPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
DataPrivate(const std::vector<char> &v, size_t o, size_t l)
|
||||
: data(v.begin() + o, v.begin() + o + l)
|
||||
{
|
||||
}
|
||||
|
||||
// A char* can be an iterator.
|
||||
DataPrivate(const char *begin, const char *end)
|
||||
: data(begin, end)
|
||||
{
|
||||
}
|
||||
|
||||
DataPrivate(size_t l, char c)
|
||||
: data(l, c)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
class ByteVector::ByteVectorPrivate
|
||||
{
|
||||
public:
|
||||
ByteVectorPrivate()
|
||||
: data(new DataPrivate())
|
||||
: data(new std::vector<char>())
|
||||
, offset(0)
|
||||
, length(0)
|
||||
{
|
||||
}
|
||||
|
||||
ByteVectorPrivate(RefCountPtr<ByteVectorPrivate> d, size_t o, size_t l)
|
||||
ByteVectorPrivate(TAGLIB_SHARED_PTR<ByteVectorPrivate> d, size_t o, size_t l)
|
||||
: data(d->data)
|
||||
, offset(d->offset + o)
|
||||
, length(l)
|
||||
@ -351,21 +325,21 @@ public:
|
||||
}
|
||||
|
||||
ByteVectorPrivate(const std::vector<char> &v, size_t o, size_t l)
|
||||
: data(new DataPrivate(v, o, l))
|
||||
: data(new std::vector<char>(v.begin() + o, v.begin() + o + l))
|
||||
, offset(0)
|
||||
, length(l)
|
||||
{
|
||||
}
|
||||
|
||||
ByteVectorPrivate(size_t l, char c)
|
||||
: data(new DataPrivate(l, c))
|
||||
: data(new std::vector<char>(l, c))
|
||||
, offset(0)
|
||||
, length(l)
|
||||
{
|
||||
}
|
||||
|
||||
ByteVectorPrivate(const char *s, size_t l)
|
||||
: data(new DataPrivate(s, s + l))
|
||||
: data(new std::vector<char>(s, s + l))
|
||||
, offset(0)
|
||||
, length(l)
|
||||
{
|
||||
@ -374,7 +348,7 @@ public:
|
||||
void detach()
|
||||
{
|
||||
if(!data.unique()) {
|
||||
data.reset(new DataPrivate(data->data, offset, length));
|
||||
data.reset(new std::vector<char>(data->begin() + offset, data->begin() + offset + length));
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
@ -391,7 +365,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
RefCountPtr<DataPrivate> data;
|
||||
TAGLIB_SHARED_PTR<std::vector<char> > data;
|
||||
size_t offset;
|
||||
size_t length;
|
||||
};
|
||||
@ -701,7 +675,7 @@ size_t ByteVector::size() const
|
||||
ByteVector &ByteVector::resize(size_t size, char padding)
|
||||
{
|
||||
detach();
|
||||
d->data->data.resize(d->offset + size, padding);
|
||||
d->data->resize(d->offset + size, padding);
|
||||
d->length = size;
|
||||
|
||||
return *this;
|
||||
@ -709,45 +683,45 @@ ByteVector &ByteVector::resize(size_t size, char padding)
|
||||
|
||||
ByteVector::Iterator ByteVector::begin()
|
||||
{
|
||||
return d->data->data.begin() + d->offset;
|
||||
return d->data->begin() + d->offset;
|
||||
}
|
||||
|
||||
ByteVector::ConstIterator ByteVector::begin() const
|
||||
{
|
||||
return d->data->data.begin() + d->offset;
|
||||
return d->data->begin() + d->offset;
|
||||
}
|
||||
|
||||
ByteVector::Iterator ByteVector::end()
|
||||
{
|
||||
return d->data->data.begin() + d->offset + d->length;
|
||||
return d->data->begin() + d->offset + d->length;
|
||||
}
|
||||
|
||||
ByteVector::ConstIterator ByteVector::end() const
|
||||
{
|
||||
return d->data->data.begin() + d->offset + d->length;
|
||||
return d->data->begin() + d->offset + d->length;
|
||||
}
|
||||
|
||||
ByteVector::ReverseIterator ByteVector::rbegin()
|
||||
{
|
||||
std::vector<char> &v = d->data->data;
|
||||
std::vector<char> &v = *(d->data);
|
||||
return v.rbegin() + (v.size() - (d->offset + d->length));
|
||||
}
|
||||
|
||||
ByteVector::ConstReverseIterator ByteVector::rbegin() const
|
||||
{
|
||||
std::vector<char> &v = d->data->data;
|
||||
std::vector<char> &v = *(d->data);
|
||||
return v.rbegin() + (v.size() - (d->offset + d->length));
|
||||
}
|
||||
|
||||
ByteVector::ReverseIterator ByteVector::rend()
|
||||
{
|
||||
std::vector<char> &v = d->data->data;
|
||||
std::vector<char> &v = *(d->data);
|
||||
return v.rbegin() + (v.size() - d->offset);
|
||||
}
|
||||
|
||||
ByteVector::ConstReverseIterator ByteVector::rend() const
|
||||
{
|
||||
std::vector<char> &v = d->data->data;
|
||||
std::vector<char> &v = *(d->data);
|
||||
return v.rbegin() + (v.size() - d->offset);
|
||||
}
|
||||
|
||||
@ -822,13 +796,13 @@ long long ByteVector::toInt64BE(size_t offset) const
|
||||
|
||||
const char &ByteVector::operator[](size_t index) const
|
||||
{
|
||||
return d->data->data[d->offset + index];
|
||||
return DATA(d)[d->offset + index];
|
||||
}
|
||||
|
||||
char &ByteVector::operator[](size_t index)
|
||||
{
|
||||
detach();
|
||||
return d->data->data[d->offset + index];
|
||||
return DATA(d)[d->offset + index];
|
||||
}
|
||||
|
||||
bool ByteVector::operator==(const ByteVector &v) const
|
||||
@ -930,7 +904,7 @@ void ByteVector::detach()
|
||||
d->detach();
|
||||
|
||||
if(!d.unique())
|
||||
d.reset(new ByteVectorPrivate(d->data->data, d->offset, d->length));
|
||||
d.reset(new ByteVectorPrivate(*(d->data), d->offset, d->length));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -531,7 +531,7 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class ByteVectorPrivate;
|
||||
RefCountPtr<ByteVectorPrivate> d;
|
||||
TAGLIB_SHARED_PTR<ByteVectorPrivate> d;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -305,7 +305,7 @@ namespace TagLib {
|
||||
private:
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
template <class TP> class ListPrivate;
|
||||
RefCountPtr<ListPrivate<T> > d;
|
||||
TAGLIB_SHARED_PTR<ListPrivate<T> > d;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -219,7 +219,7 @@ namespace TagLib {
|
||||
private:
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
template <class KeyP, class TP> class MapPrivate;
|
||||
RefCountPtr<MapPrivate<Key, T> > d;
|
||||
TAGLIB_SHARED_PTR<MapPrivate<Key, T> > d;
|
||||
|
||||
#endif
|
||||
};
|
||||
|
@ -37,19 +37,33 @@
|
||||
#elif defined(HAVE_BOOST_SHARED_PTR)
|
||||
# include <boost/shared_ptr.hpp>
|
||||
#else
|
||||
# ifdef __APPLE__
|
||||
# include <libkern/OSAtomic.h>
|
||||
# define TAGLIB_ATOMIC_MAC
|
||||
# elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
|
||||
# define TAGLIB_ATOMIC_WIN
|
||||
# elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 401) \
|
||||
&& (defined(__i386__) || defined(__i486__) || defined(__i586__) || \
|
||||
defined(__i686__) || defined(__x86_64) || defined(__ia64)) \
|
||||
&& !defined(__INTEL_COMPILER)
|
||||
# define TAGLIB_ATOMIC_GCC
|
||||
# elif defined(__ia64) && defined(__INTEL_COMPILER)
|
||||
# include <ia64intrin.h>
|
||||
# define TAGLIB_ATOMIC_GCC
|
||||
# include <algorithm>
|
||||
# if defined(HAVE_GCC_ATOMIC)
|
||||
# define TAGLIB_ATOMIC_INT int
|
||||
# define TAGLIB_ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
|
||||
# define TAGLIB_ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
|
||||
# elif defined(HAVE_WIN_ATOMIC)
|
||||
# if !defined(NOMINMAX)
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# define TAGLIB_ATOMIC_INT long
|
||||
# define TAGLIB_ATOMIC_INC(x) InterlockedIncrement(&x)
|
||||
# define TAGLIB_ATOMIC_DEC(x) InterlockedDecrement(&x)
|
||||
# elif defined(HAVE_MAC_ATOMIC)
|
||||
# include <libkern/OSAtomic.h>
|
||||
# define TAGLIB_ATOMIC_INT int32_t
|
||||
# define TAGLIB_ATOMIC_INC(x) OSAtomicIncrement32Barrier(&x)
|
||||
# define TAGLIB_ATOMIC_DEC(x) OSAtomicDecrement32Barrier(&x)
|
||||
# elif defined(HAVE_IA64_ATOMIC)
|
||||
# include <ia64intrin.h>
|
||||
# define TAGLIB_ATOMIC_INT int
|
||||
# define TAGLIB_ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
|
||||
# define TAGLIB_ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
|
||||
# else
|
||||
# define TAGLIB_ATOMIC_INT int
|
||||
# define TAGLIB_ATOMIC_INC(x) (++x)
|
||||
# define TAGLIB_ATOMIC_DEC(x) (--x)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@ -63,55 +77,18 @@
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
#if defined(HAVE_STD_SHARED_PTR) || defined(HAVE_TR1_SHARED_PTR) || defined(HAVE_BOOST_SHARED_PTR)
|
||||
#if defined(HAVE_STD_SHARED_PTR) || defined(HAVE_TR1_SHARED_PTR)
|
||||
|
||||
#define TAGLIB_SHARED_PTR std::tr1::shared_ptr
|
||||
|
||||
# if defined(SUPPORT_TEMPLATE_ALIAS)
|
||||
#elif defined(HAVE_BOOST_SHARED_PTR)
|
||||
|
||||
// Defines RefCountPtr<T> as an alias of shared_ptr<T>
|
||||
// if shared_ptr<T> and the template alias are both available.
|
||||
|
||||
# if defined(HAVE_STD_SHARED_PTR) || defined(HAVE_TR1_SHARED_PTR)
|
||||
|
||||
template <typename T>
|
||||
using RefCountPtr = std::tr1::shared_ptr<T>;
|
||||
|
||||
# else
|
||||
|
||||
template <typename T>
|
||||
using RefCountPtr = boost::shared_ptr<T>;
|
||||
|
||||
# endif
|
||||
|
||||
# else
|
||||
|
||||
// Defines RefCountPtr<T> as a derived class of shared_ptr<T>.
|
||||
// if shared_ptr<T> is available but the template alias is not.
|
||||
|
||||
# if defined(HAVE_STD_SHARED_PTR) || defined(HAVE_TR1_SHARED_PTR)
|
||||
|
||||
template <typename T>
|
||||
class RefCountPtr : public std::tr1::shared_ptr<T>
|
||||
{
|
||||
public:
|
||||
explicit RefCountPtr(T *p) : std::tr1::shared_ptr<T>(p) {}
|
||||
};
|
||||
|
||||
# else
|
||||
|
||||
template <typename T>
|
||||
class RefCountPtr : public boost::shared_ptr<T>
|
||||
{
|
||||
public:
|
||||
explicit RefCountPtr(T *p) : boost::shared_ptr<T>(p) {}
|
||||
};
|
||||
|
||||
# endif
|
||||
|
||||
# endif
|
||||
#define TAGLIB_SHARED_PTR boost::shared_ptr
|
||||
|
||||
#else // HAVE_*_SHARED_PTR
|
||||
|
||||
// Implements RefCountPtr<T> if shared_ptr<T> is not available.
|
||||
// Self-implements RefCountPtr<T> if shared_ptr<T> is not available.
|
||||
// I STRONGLY RECOMMEND using standard shared_ptr<T> rather than this class.
|
||||
|
||||
template<typename T>
|
||||
class RefCountPtr
|
||||
@ -134,12 +111,12 @@ namespace TagLib {
|
||||
|
||||
void addref()
|
||||
{
|
||||
increment(&count);
|
||||
TAGLIB_ATOMIC_INC(count);
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
if(decrement(&count) == 0) {
|
||||
if(TAGLIB_ATOMIC_DEC(count) == 0) {
|
||||
dispose();
|
||||
delete this;
|
||||
}
|
||||
@ -153,33 +130,7 @@ namespace TagLib {
|
||||
virtual void dispose() = 0;
|
||||
|
||||
private:
|
||||
# if defined(TAGLIB_ATOMIC_MAC)
|
||||
typedef volatile int32_t counter_t;
|
||||
|
||||
inline static void increment(counter_t *c) { OSAtomicIncrement32Barrier(c); }
|
||||
inline static counter_t decrement(counter_t *c) { return OSAtomicDecrement32Barrier(c); }
|
||||
|
||||
# elif defined(TAGLIB_ATOMIC_WIN)
|
||||
typedef volatile long counter_t;
|
||||
|
||||
inline static void increment(counter_t *c) { InterlockedIncrement(c); }
|
||||
inline static counter_t decrement(counter_t *c) { return InterlockedDecrement(c); }
|
||||
|
||||
# elif defined(TAGLIB_ATOMIC_GCC)
|
||||
typedef volatile int counter_t;
|
||||
|
||||
inline static void increment(counter_t *c) { __sync_add_and_fetch(c, 1); }
|
||||
inline static counter_t decrement(counter_t *c) { return __sync_sub_and_fetch(c, 1); }
|
||||
|
||||
# else
|
||||
typedef uint counter_t;
|
||||
|
||||
inline static void increment(counter_t *c) { ++(*c) }
|
||||
inline static counter_t decrement(counter_t *c) { return --(*c); }
|
||||
|
||||
# endif
|
||||
|
||||
counter_t count;
|
||||
volatile TAGLIB_ATOMIC_INT count;
|
||||
};
|
||||
|
||||
// Counter impl class. Provides a dynamic deleter.
|
||||
@ -208,31 +159,52 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
public:
|
||||
explicit RefCountPtr()
|
||||
: counter(0)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
explicit RefCountPtr(U *p)
|
||||
: counter(new CounterImpl<U>(p))
|
||||
{
|
||||
}
|
||||
|
||||
RefCountPtr(const RefCountPtr &x)
|
||||
RefCountPtr(const RefCountPtr<T> &x)
|
||||
: counter(x.counter)
|
||||
{
|
||||
counter = x.counter;
|
||||
counter->addref();
|
||||
if(counter)
|
||||
counter->addref();
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
RefCountPtr(const RefCountPtr<U> &x)
|
||||
: counter(reinterpret_cast<CounterBase*>(x.counter))
|
||||
{
|
||||
if(counter)
|
||||
counter->addref();
|
||||
}
|
||||
|
||||
~RefCountPtr()
|
||||
{
|
||||
counter->release();
|
||||
if(counter)
|
||||
counter->release();
|
||||
}
|
||||
|
||||
T *get() const
|
||||
{
|
||||
return static_cast<CounterImpl<T>*>(counter)->get();
|
||||
if(counter)
|
||||
return static_cast<CounterImpl<T>*>(counter)->get();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
long use_count() const
|
||||
{
|
||||
return counter->use_count();
|
||||
if(counter)
|
||||
return counter->use_count();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool unique() const
|
||||
@ -244,20 +216,44 @@ namespace TagLib {
|
||||
void reset(U *p)
|
||||
{
|
||||
if(get() != p)
|
||||
{
|
||||
counter->release();
|
||||
counter = new CounterImpl<U>(p);
|
||||
}
|
||||
RefCountPtr<T>(p).swap(*this);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
RefCountPtr<T>().swap(*this);
|
||||
}
|
||||
|
||||
void swap(RefCountPtr<T> &x)
|
||||
{
|
||||
std::swap(counter, x.counter);
|
||||
}
|
||||
|
||||
RefCountPtr<T> &operator=(const RefCountPtr<T> &x)
|
||||
{
|
||||
if(get() != x.get())
|
||||
{
|
||||
counter->release();
|
||||
if(get() != x.get()) {
|
||||
if(counter)
|
||||
counter->release();
|
||||
|
||||
counter = x.counter;
|
||||
counter->addref();
|
||||
|
||||
if(counter)
|
||||
counter->addref();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
RefCountPtr<T> &operator=(const RefCountPtr<U> &x)
|
||||
{
|
||||
if(get() != x.get()) {
|
||||
if(counter)
|
||||
counter->release();
|
||||
|
||||
counter = reinterpret_cast<CounterBase*>(x.counter);
|
||||
|
||||
if(counter)
|
||||
counter->addref();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -288,9 +284,19 @@ namespace TagLib {
|
||||
}
|
||||
|
||||
private:
|
||||
CounterBase *counter;
|
||||
mutable CounterBase *counter;
|
||||
|
||||
template <typename U> friend class RefCountPtr;
|
||||
};
|
||||
|
||||
# define TAGLIB_SHARED_PTR TagLib::RefCountPtr
|
||||
|
||||
template <typename T>
|
||||
void swap(RefCountPtr<T> &a, RefCountPtr<T> &b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
#endif // HAVE_*_SHARED_PTR
|
||||
}
|
||||
#endif // DO_NOT_DOCUMENT
|
||||
|
@ -523,7 +523,7 @@ namespace TagLib {
|
||||
static const Type WCharByteOrder;
|
||||
|
||||
class StringPrivate;
|
||||
RefCountPtr<StringPrivate> d;
|
||||
TAGLIB_SHARED_PTR<StringPrivate> d;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -30,6 +30,7 @@ SET(test_runner_SRCS
|
||||
main.cpp
|
||||
test_list.cpp
|
||||
test_map.cpp
|
||||
test_smartptr.cpp
|
||||
test_mpeg.cpp
|
||||
test_synchdata.cpp
|
||||
test_trueaudio.cpp
|
||||
|
200
tests/test_smartptr.cpp
Normal file
200
tests/test_smartptr.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
#include <trefcountptr.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include "utils.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
bool baseDestructorCalled;
|
||||
bool derivedDestructorCalled;
|
||||
bool incompleteDestructorCalled;
|
||||
|
||||
class TestSmartptr : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestSmartptr);
|
||||
CPPUNIT_TEST(testSharedptrBasic);
|
||||
CPPUNIT_TEST(testDerivedClass);
|
||||
CPPUNIT_TEST(testIncompleteClass);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
private:
|
||||
template<class T>
|
||||
void ck( const T* v1, T v2 ) { CPPUNIT_ASSERT( *v1 == v2 ); }
|
||||
|
||||
public:
|
||||
void testSharedptrBasic()
|
||||
{
|
||||
int * ip = new int;
|
||||
TAGLIB_SHARED_PTR<int> cp ( ip );
|
||||
CPPUNIT_ASSERT( ip == cp.get() );
|
||||
CPPUNIT_ASSERT( cp.use_count() == 1 );
|
||||
|
||||
*cp = 54321;
|
||||
CPPUNIT_ASSERT( *cp == 54321 );
|
||||
CPPUNIT_ASSERT( *ip == 54321 );
|
||||
ck( static_cast<int*>(cp.get()), 54321 );
|
||||
ck( static_cast<int*>(ip), *cp );
|
||||
|
||||
TAGLIB_SHARED_PTR<int> cp2 ( cp );
|
||||
CPPUNIT_ASSERT( ip == cp2.get() );
|
||||
CPPUNIT_ASSERT( cp.use_count() == 2 );
|
||||
CPPUNIT_ASSERT( cp2.use_count() == 2 );
|
||||
|
||||
CPPUNIT_ASSERT( *cp == 54321 );
|
||||
CPPUNIT_ASSERT( *cp2 == 54321 );
|
||||
ck( static_cast<int*>(cp2.get()), 54321 );
|
||||
ck( static_cast<int*>(ip), *cp2 );
|
||||
|
||||
TAGLIB_SHARED_PTR<int> cp3 ( cp );
|
||||
CPPUNIT_ASSERT( cp.use_count() == 3 );
|
||||
CPPUNIT_ASSERT( cp2.use_count() == 3 );
|
||||
CPPUNIT_ASSERT( cp3.use_count() == 3 );
|
||||
cp.reset();
|
||||
CPPUNIT_ASSERT( cp2.use_count() == 2 );
|
||||
CPPUNIT_ASSERT( cp3.use_count() == 2 );
|
||||
cp.reset( new int );
|
||||
*cp = 98765;
|
||||
CPPUNIT_ASSERT( *cp == 98765 );
|
||||
*cp3 = 87654;
|
||||
CPPUNIT_ASSERT( *cp3 == 87654 );
|
||||
CPPUNIT_ASSERT( *cp2 == 87654 );
|
||||
cp.swap( cp3 );
|
||||
CPPUNIT_ASSERT( *cp == 87654 );
|
||||
CPPUNIT_ASSERT( *cp2 == 87654 );
|
||||
CPPUNIT_ASSERT( *cp3 == 98765 );
|
||||
cp.swap( cp3 );
|
||||
CPPUNIT_ASSERT( *cp == 98765 );
|
||||
CPPUNIT_ASSERT( *cp2 == 87654 );
|
||||
CPPUNIT_ASSERT( *cp3 == 87654 );
|
||||
cp2 = cp2;
|
||||
CPPUNIT_ASSERT( cp2.use_count() == 2 );
|
||||
CPPUNIT_ASSERT( *cp2 == 87654 );
|
||||
cp = cp2;
|
||||
CPPUNIT_ASSERT( cp2.use_count() == 3 );
|
||||
CPPUNIT_ASSERT( *cp2 == 87654 );
|
||||
CPPUNIT_ASSERT( cp.use_count() == 3 );
|
||||
CPPUNIT_ASSERT( *cp == 87654 );
|
||||
|
||||
TAGLIB_SHARED_PTR<int> cp4;
|
||||
swap( cp2, cp4 );
|
||||
CPPUNIT_ASSERT( cp4.use_count() == 3 );
|
||||
CPPUNIT_ASSERT( *cp4 == 87654 );
|
||||
CPPUNIT_ASSERT( cp2.get() == 0 );
|
||||
|
||||
std::set< TAGLIB_SHARED_PTR<int> > scp;
|
||||
scp.insert(cp4);
|
||||
CPPUNIT_ASSERT( scp.find(cp4) != scp.end() );
|
||||
CPPUNIT_ASSERT( scp.find(cp4) == scp.find( TAGLIB_SHARED_PTR<int>(cp4) ) );
|
||||
}
|
||||
|
||||
private:
|
||||
class DummyBase
|
||||
{
|
||||
public:
|
||||
DummyBase(int x) : value(x)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~DummyBase()
|
||||
{
|
||||
baseDestructorCalled = true;
|
||||
}
|
||||
|
||||
int getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
int value;
|
||||
};
|
||||
|
||||
class DummyDerived : public DummyBase
|
||||
{
|
||||
public:
|
||||
DummyDerived(int x) : DummyBase(x)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~DummyDerived()
|
||||
{
|
||||
derivedDestructorCalled = true;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
void testDerivedClass()
|
||||
{
|
||||
baseDestructorCalled = false;
|
||||
derivedDestructorCalled = false;
|
||||
|
||||
{
|
||||
TAGLIB_SHARED_PTR<DummyBase> p1(new DummyDerived(100));
|
||||
CPPUNIT_ASSERT(p1->getValue() == 100);
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(baseDestructorCalled);
|
||||
CPPUNIT_ASSERT(derivedDestructorCalled);
|
||||
|
||||
baseDestructorCalled = false;
|
||||
derivedDestructorCalled = false;
|
||||
|
||||
{
|
||||
TAGLIB_SHARED_PTR<DummyDerived> p1(new DummyDerived(100));
|
||||
TAGLIB_SHARED_PTR<DummyBase> p2 = p1;
|
||||
|
||||
CPPUNIT_ASSERT(p1->getValue() == 100);
|
||||
CPPUNIT_ASSERT(p2->getValue() == 100);
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(baseDestructorCalled);
|
||||
CPPUNIT_ASSERT(derivedDestructorCalled);
|
||||
|
||||
baseDestructorCalled = false;
|
||||
derivedDestructorCalled = false;
|
||||
|
||||
{
|
||||
TAGLIB_SHARED_PTR<DummyDerived> p1;
|
||||
TAGLIB_SHARED_PTR<DummyBase> p2;
|
||||
|
||||
p1.reset(new DummyDerived(100));
|
||||
p2 = p1;
|
||||
|
||||
CPPUNIT_ASSERT(p1->getValue() == 100);
|
||||
CPPUNIT_ASSERT(p2->getValue() == 100);
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT(baseDestructorCalled);
|
||||
CPPUNIT_ASSERT(derivedDestructorCalled);
|
||||
}
|
||||
|
||||
private:
|
||||
class DummyIncomplete;
|
||||
TAGLIB_SHARED_PTR<DummyIncomplete> pincomplete;
|
||||
|
||||
class DummyIncomplete
|
||||
{
|
||||
public:
|
||||
~DummyIncomplete()
|
||||
{
|
||||
incompleteDestructorCalled = true;
|
||||
}
|
||||
|
||||
int getValue() const { return 100; }
|
||||
};
|
||||
|
||||
public:
|
||||
void testIncompleteClass()
|
||||
{
|
||||
incompleteDestructorCalled = false;
|
||||
|
||||
pincomplete.reset(new DummyIncomplete());
|
||||
CPPUNIT_ASSERT(pincomplete->getValue() == 100);
|
||||
|
||||
pincomplete.reset();
|
||||
CPPUNIT_ASSERT(incompleteDestructorCalled);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestSmartptr);
|
Reference in New Issue
Block a user