Merge branch 'taglib2' into tonumber4-merge

Conflicts:
	ConfigureChecks.cmake
	config-taglib.h.cmake
This commit is contained in:
Tsuda Kageyu
2013-05-01 17:44:02 +09:00
29 changed files with 2612 additions and 213 deletions

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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;
};
}

View File

@ -231,7 +231,7 @@ namespace TagLib
#endif
private:
class PicturePrivate;
RefCountPtr<PicturePrivate> d;
TAGLIB_SHARED_PTR<PicturePrivate> d;
};
}
}

View 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
View 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
View 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
View 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
View 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

View 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;
}

View 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

View 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

View 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);
}

View 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

View File

@ -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;
};

View File

@ -278,7 +278,7 @@ namespace TagLib {
private:
class FileRefPrivate;
RefCountPtr<FileRefPrivate> d;
TAGLIB_SHARED_PTR<FileRefPrivate> d;
};
} // namespace TagLib

View File

@ -66,7 +66,7 @@ namespace TagLib {
private:
class CoverArtPrivate;
RefCountPtr<CoverArtPrivate> d;
TAGLIB_SHARED_PTR<CoverArtPrivate> d;
};
typedef List<CoverArt> CoverArtList;

View File

@ -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:

View File

@ -97,7 +97,7 @@ namespace TagLib {
private:
class ItemPrivate;
RefCountPtr<ItemPrivate> d;
TAGLIB_SHARED_PTR<ItemPrivate> d;
};
}

View File

@ -181,7 +181,7 @@ namespace TagLib {
void parse(const ByteVector &data);
class HeaderPrivate;
RefCountPtr<HeaderPrivate> d;
TAGLIB_SHARED_PTR<HeaderPrivate> d;
};
}
}

View File

@ -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));
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -531,7 +531,7 @@ namespace TagLib {
private:
class ByteVectorPrivate;
RefCountPtr<ByteVectorPrivate> d;
TAGLIB_SHARED_PTR<ByteVectorPrivate> d;
};
/*!

View File

@ -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
};

View File

@ -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
};

View File

@ -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

View File

@ -523,7 +523,7 @@ namespace TagLib {
static const Type WCharByteOrder;
class StringPrivate;
RefCountPtr<StringPrivate> d;
TAGLIB_SHARED_PTR<StringPrivate> d;
};
/*!

View File

@ -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
View 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);