mirror of
https://github.com/taglib/taglib.git
synced 2025-06-04 01:28:21 -04:00
Merge pull request #206 from TsudaKageyu/smartptrfile
Rewrote File, FileRef and FileStream to use smart pointers
This commit is contained in:
commit
0a4756b527
@ -47,12 +47,12 @@ static bool stringManagementEnabled = true;
|
||||
|
||||
void taglib_set_strings_unicode(BOOL unicode)
|
||||
{
|
||||
unicodeStrings = bool(unicode);
|
||||
unicodeStrings = (unicode != 0);
|
||||
}
|
||||
|
||||
void taglib_set_string_management_enabled(BOOL management)
|
||||
{
|
||||
stringManagementEnabled = bool(management);
|
||||
stringManagementEnabled = (management != 0);
|
||||
}
|
||||
|
||||
void taglib_free(void* pointer)
|
||||
@ -66,32 +66,32 @@ void taglib_free(void* pointer)
|
||||
|
||||
TagLib_File *taglib_file_new(const char *filename)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
{
|
||||
switch(type) {
|
||||
case TagLib_File_MPEG:
|
||||
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MPEG::File(filename)));
|
||||
case TagLib_File_OggVorbis:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Vorbis::File(filename)));
|
||||
case TagLib_File_FLAC:
|
||||
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new FLAC::File(filename)));
|
||||
case TagLib_File_MPC:
|
||||
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MPC::File(filename)));
|
||||
case TagLib_File_OggFlac:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::FLAC::File(filename)));
|
||||
case TagLib_File_WavPack:
|
||||
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new WavPack::File(filename)));
|
||||
case TagLib_File_Speex:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Speex::File(filename)));
|
||||
case TagLib_File_TrueAudio:
|
||||
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new TrueAudio::File(filename)));
|
||||
case TagLib_File_MP4:
|
||||
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MP4::File(filename)));
|
||||
case TagLib_File_ASF:
|
||||
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new ASF::File(filename)));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -101,29 +101,29 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
|
||||
void taglib_file_free(TagLib_File *file)
|
||||
{
|
||||
delete reinterpret_cast<File *>(file);
|
||||
delete reinterpret_cast<FileRef *>(file);
|
||||
}
|
||||
|
||||
BOOL taglib_file_is_valid(const TagLib_File *file)
|
||||
{
|
||||
return reinterpret_cast<const File *>(file)->isValid();
|
||||
return reinterpret_cast<const FileRef *>(file)->isValid();
|
||||
}
|
||||
|
||||
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
|
||||
{
|
||||
const File *f = reinterpret_cast<const File *>(file);
|
||||
const FileRef *f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<TagLib_Tag *>(f->tag());
|
||||
}
|
||||
|
||||
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
|
||||
{
|
||||
const File *f = reinterpret_cast<const File *>(file);
|
||||
const FileRef *f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
|
||||
}
|
||||
|
||||
BOOL taglib_file_save(TagLib_File *file)
|
||||
{
|
||||
return reinterpret_cast<File *>(file)->save();
|
||||
return reinterpret_cast<FileRef *>(file)->save();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -59,24 +59,130 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
typedef List<const FileRef::FileTypeResolver *> ResolverList;
|
||||
typedef ResolverList::ConstIterator ResolverConstIterator;
|
||||
|
||||
ResolverList fileTypeResolvers;
|
||||
|
||||
RefCountPtr<File> create(
|
||||
FileName fileName,
|
||||
bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
RefCountPtr<File> file;
|
||||
for(ResolverConstIterator it = fileTypeResolvers.begin(); it != fileTypeResolvers.end(); ++it)
|
||||
{
|
||||
file.reset((*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
|
||||
String ext;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Avoids direct conversion from FileName to String
|
||||
// because String can't decode strings in local encodings properly.
|
||||
|
||||
if(!fileName.wstr().empty()) {
|
||||
const wchar_t *pext = PathFindExtensionW(fileName.wstr().c_str());
|
||||
if(*pext == L'.')
|
||||
ext = String(pext + 1).upper();
|
||||
}
|
||||
else {
|
||||
const char *pext = PathFindExtensionA(fileName.str().c_str());
|
||||
if(*pext == '.')
|
||||
ext = String(pext + 1).upper();
|
||||
}
|
||||
#else
|
||||
{
|
||||
String s = fileName;
|
||||
const size_t pos = s.rfind(".");
|
||||
if(pos != String::npos)
|
||||
ext = s.substr(pos + 1).upper();
|
||||
}
|
||||
#endif
|
||||
|
||||
// If this list is updated, the method defaultFileExtensions() should also be
|
||||
// updated. However at some point that list should be created at the same time
|
||||
// that a default file type resolver is created.
|
||||
|
||||
if(!ext.isEmpty()) {
|
||||
if(ext == "MP3")
|
||||
file.reset(new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "OGG")
|
||||
file.reset(new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
file.reset(new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
if(!file->isValid())
|
||||
file.reset(new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
}
|
||||
else if(ext == "FLAC")
|
||||
file.reset(new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "MPC")
|
||||
file.reset(new MPC::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "WV")
|
||||
file.reset(new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "SPX")
|
||||
file.reset(new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "OPUS")
|
||||
file.reset(new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "TTA")
|
||||
file.reset(new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
|
||||
file.reset(new MP4::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "WMA" || ext == "ASF")
|
||||
file.reset(new ASF::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "AIF" || ext == "AIFF")
|
||||
file.reset(new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "WAV")
|
||||
file.reset(new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "APE")
|
||||
file.reset(new APE::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
file.reset(new Mod::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "S3M")
|
||||
file.reset(new S3M::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "IT")
|
||||
file.reset(new IT::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
else if(ext == "XM")
|
||||
file.reset(new XM::File(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
class FileRef::FileRefPrivate
|
||||
{
|
||||
public:
|
||||
FileRefPrivate(File *f) : file(f) {}
|
||||
FileRefPrivate()
|
||||
: file()
|
||||
{
|
||||
}
|
||||
|
||||
FileRefPrivate(File *f)
|
||||
: file(f)
|
||||
{
|
||||
}
|
||||
|
||||
FileRefPrivate(RefCountPtr<File> f)
|
||||
: file(f)
|
||||
{
|
||||
}
|
||||
|
||||
RefCountPtr<File> file;
|
||||
|
||||
static List<const FileTypeResolver *> fileTypeResolvers;
|
||||
};
|
||||
|
||||
List<const FileRef::FileTypeResolver *> FileRef::FileRefPrivate::fileTypeResolvers;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FileRef::FileRef()
|
||||
: d(new FileRefPrivate(0))
|
||||
: d(new FileRefPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
@ -118,6 +224,36 @@ Tag *FileRef::tag() const
|
||||
return d->file->tag();
|
||||
}
|
||||
|
||||
PropertyMap FileRef::properties() const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::properties() - Called without a valid file.");
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
return d->file->properties();
|
||||
}
|
||||
|
||||
void FileRef::removeUnsupportedProperties(const StringList& properties)
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::removeUnsupportedProperties() - Called without a valid file.");
|
||||
return;
|
||||
}
|
||||
|
||||
d->file->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap FileRef::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::setProperties() - Called without a valid file.");
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
return d->file->setProperties(properties);
|
||||
}
|
||||
|
||||
AudioProperties *FileRef::audioProperties() const
|
||||
{
|
||||
if(isNull()) {
|
||||
@ -143,7 +279,7 @@ bool FileRef::save()
|
||||
|
||||
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
|
||||
{
|
||||
FileRefPrivate::fileTypeResolvers.prepend(resolver);
|
||||
fileTypeResolvers.prepend(resolver);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@ -182,9 +318,14 @@ StringList FileRef::defaultFileExtensions()
|
||||
return l;
|
||||
}
|
||||
|
||||
bool FileRef::isValid() const
|
||||
{
|
||||
return (d->file && d->file->isValid());
|
||||
}
|
||||
|
||||
bool FileRef::isNull() const
|
||||
{
|
||||
return !d->file || !d->file->isValid();
|
||||
return !isValid();
|
||||
}
|
||||
|
||||
FileRef &FileRef::operator=(const FileRef &ref)
|
||||
@ -212,93 +353,3 @@ bool FileRef::operator!=(const FileRef &ref) const
|
||||
{
|
||||
return ref.d->file != d->file;
|
||||
}
|
||||
|
||||
File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) // static
|
||||
{
|
||||
|
||||
List<const FileTypeResolver *>::ConstIterator it = FileRefPrivate::fileTypeResolvers.begin();
|
||||
|
||||
for(; it != FileRefPrivate::fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
|
||||
String ext;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Avoids direct conversion from FileName to String
|
||||
// because String can't decode strings in local encodings properly.
|
||||
|
||||
if(!fileName.wstr().empty()) {
|
||||
const wchar_t *pext = PathFindExtensionW(fileName.wstr().c_str());
|
||||
if(*pext == L'.')
|
||||
ext = String(pext + 1).upper();
|
||||
}
|
||||
else {
|
||||
const char *pext = PathFindExtensionA(fileName.str().c_str());
|
||||
if(*pext == '.')
|
||||
ext = String(pext + 1).upper();
|
||||
}
|
||||
#else
|
||||
{
|
||||
String s = fileName;
|
||||
const size_t pos = s.rfind(".");
|
||||
if(pos != String::npos)
|
||||
ext = s.substr(pos + 1).upper();
|
||||
}
|
||||
#endif
|
||||
|
||||
// If this list is updated, the method defaultFileExtensions() should also be
|
||||
// updated. However at some point that list should be created at the same time
|
||||
// that a default file type resolver is created.
|
||||
|
||||
if(!ext.isEmpty()) {
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if (file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
if(ext == "FLAC")
|
||||
return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "MPC")
|
||||
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WV")
|
||||
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "SPX")
|
||||
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OPUS")
|
||||
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "TTA")
|
||||
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
|
||||
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WMA" || ext == "ASF")
|
||||
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIF" || ext == "AIFF")
|
||||
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WAV")
|
||||
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "S3M")
|
||||
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "IT")
|
||||
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -26,10 +26,10 @@
|
||||
#ifndef TAGLIB_FILEREF_H
|
||||
#define TAGLIB_FILEREF_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
#include "tstringlist.h"
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
#if _WIN32
|
||||
@ -172,6 +172,41 @@ namespace TagLib {
|
||||
*/
|
||||
Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Exports the tags of the file as dictionary mapping (human readable) tag
|
||||
* names (uppercase Strings) to StringLists of tag values. Calls the according
|
||||
* specialization in the File subclasses.
|
||||
* For each metadata object of the file that could not be parsed into the PropertyMap
|
||||
* format, the returend map's unsupportedData() list will contain one entry identifying
|
||||
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
|
||||
* to remove (a subset of) them.
|
||||
* For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
|
||||
* tag) only the most "modern" one will be exported (ID3v2 in this case).
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties, or a subset of them, from the file's metadata.
|
||||
* The parameter \a properties must contain only entries from
|
||||
* properties().unsupportedData().
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
|
||||
/*!
|
||||
* Sets the tags of this File to those specified in \a properties. Calls the
|
||||
* according specialization method in the subclasses of File to do the translation
|
||||
* into the format-specific details.
|
||||
* If some value(s) could not be written imported to the specific metadata format,
|
||||
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
|
||||
* indicating that no problems occured.
|
||||
* With file types that support several tag formats (for instance, MP3 files can have
|
||||
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
|
||||
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
|
||||
* be taken into account for the return value of this function.
|
||||
* See the documentation of the subclass implementations for detailed descriptions.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Returns the audio properties for this FileRef. If no audio properties
|
||||
* were read then this will returns a null pointer.
|
||||
@ -232,8 +267,17 @@ namespace TagLib {
|
||||
*/
|
||||
static StringList defaultFileExtensions();
|
||||
|
||||
/*!
|
||||
* Returns true if the file is open and readable.
|
||||
*
|
||||
* \note Just a negative of isNull().
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/*!
|
||||
* Returns true if the file (and as such other pointers) are null.
|
||||
*
|
||||
* \note Just a negative of isValid().
|
||||
*/
|
||||
bool isNull() const;
|
||||
|
||||
@ -264,22 +308,6 @@ namespace TagLib {
|
||||
*/
|
||||
bool operator!=(const FileRef &ref) const;
|
||||
|
||||
/*!
|
||||
* A simple implementation of file type guessing. If \a readAudioProperties
|
||||
* is true then the audio properties will be read using
|
||||
* \a audioPropertiesStyle. If \a readAudioProperties is false then
|
||||
* \a audioPropertiesStyle will be ignored.
|
||||
*
|
||||
* \note You generally shouldn't use this method, but instead the constructor
|
||||
* directly.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
static File *create(FileName fileName,
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
|
||||
private:
|
||||
class FileRefPrivate;
|
||||
RefCountPtr<FileRefPrivate> d;
|
||||
|
@ -28,96 +28,76 @@
|
||||
#include "tstring.h"
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
#else
|
||||
# include <stdio.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef R_OK
|
||||
# define R_OK 4
|
||||
#endif
|
||||
#ifndef W_OK
|
||||
# define W_OK 2
|
||||
#endif
|
||||
|
||||
#include "asffile.h"
|
||||
#include "mpegfile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "flacfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "mpcfile.h"
|
||||
#include "mp4file.h"
|
||||
#include "wavpackfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#include "apefile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "itfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "mp4file.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class File::FilePrivate
|
||||
class File::FilePrivateBase
|
||||
{
|
||||
public:
|
||||
FilePrivate(IOStream *stream, bool owner);
|
||||
FilePrivateBase()
|
||||
: valid(true)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~FilePrivateBase()
|
||||
{
|
||||
}
|
||||
|
||||
virtual IOStream *stream() const = 0;
|
||||
|
||||
IOStream *stream;
|
||||
bool streamOwner;
|
||||
bool valid;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static const size_t bufferSize = 8192;
|
||||
|
||||
#else
|
||||
|
||||
static const size_t bufferSize = 1024;
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
File::FilePrivate::FilePrivate(IOStream *stream, bool owner) :
|
||||
stream(stream),
|
||||
streamOwner(owner),
|
||||
valid(true)
|
||||
// FilePrivate implementation which takes ownership of the stream.
|
||||
|
||||
class File::ManagedFilePrivate : public File::FilePrivateBase
|
||||
{
|
||||
}
|
||||
public:
|
||||
ManagedFilePrivate(IOStream *stream)
|
||||
: p(stream)
|
||||
{
|
||||
}
|
||||
|
||||
virtual IOStream *stream() const
|
||||
{
|
||||
return p.get();
|
||||
}
|
||||
|
||||
private:
|
||||
NonRefCountPtr<IOStream> p;
|
||||
};
|
||||
|
||||
// FilePrivate implementation which doesn't take ownership of the stream.
|
||||
|
||||
class File::UnmanagedFilePrivate : public File::FilePrivateBase
|
||||
{
|
||||
public:
|
||||
UnmanagedFilePrivate(IOStream *stream)
|
||||
: p(stream)
|
||||
{
|
||||
}
|
||||
|
||||
virtual IOStream *stream() const
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
private:
|
||||
IOStream *p;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
File::File(FileName fileName)
|
||||
{
|
||||
IOStream *stream = new FileStream(fileName);
|
||||
d = new FilePrivate(stream, true);
|
||||
}
|
||||
|
||||
File::File(IOStream *stream)
|
||||
{
|
||||
d = new FilePrivate(stream, false);
|
||||
}
|
||||
|
||||
File::~File()
|
||||
{
|
||||
if(d->stream && d->streamOwner)
|
||||
delete d->stream;
|
||||
delete d;
|
||||
}
|
||||
|
||||
FileName File::name() const
|
||||
{
|
||||
return d->stream->name();
|
||||
return d->stream()->name();
|
||||
}
|
||||
|
||||
PropertyMap File::properties() const
|
||||
@ -137,17 +117,17 @@ PropertyMap File::setProperties(const PropertyMap &properties)
|
||||
|
||||
ByteVector File::readBlock(size_t length)
|
||||
{
|
||||
return d->stream->readBlock(length);
|
||||
return d->stream()->readBlock(length);
|
||||
}
|
||||
|
||||
void File::writeBlock(const ByteVector &data)
|
||||
{
|
||||
d->stream->writeBlock(data);
|
||||
d->stream()->writeBlock(data);
|
||||
}
|
||||
|
||||
offset_t File::find(const ByteVector &pattern, offset_t fromOffset, const ByteVector &before)
|
||||
{
|
||||
if(!d->stream || pattern.size() > d->bufferSize)
|
||||
if(!d->stream() || pattern.size() > bufferSize())
|
||||
return -1;
|
||||
|
||||
// The position in the file that the current buffer starts at.
|
||||
@ -196,20 +176,20 @@ offset_t File::find(const ByteVector &pattern, offset_t fromOffset, const ByteVe
|
||||
// (1) previous partial match
|
||||
|
||||
if(previousPartialMatch != ByteVector::npos
|
||||
&& d->bufferSize > previousPartialMatch)
|
||||
&& bufferSize() > previousPartialMatch)
|
||||
{
|
||||
const size_t patternOffset = (d->bufferSize - previousPartialMatch);
|
||||
const size_t patternOffset = (bufferSize() - previousPartialMatch);
|
||||
if(buffer.containsAt(pattern, 0, patternOffset)) {
|
||||
seek(originalPosition);
|
||||
return bufferOffset - d->bufferSize + previousPartialMatch;
|
||||
return bufferOffset - bufferSize() + previousPartialMatch;
|
||||
}
|
||||
}
|
||||
|
||||
if(!before.isNull()
|
||||
&& beforePreviousPartialMatch != ByteVector::npos
|
||||
&& d->bufferSize > beforePreviousPartialMatch)
|
||||
&& bufferSize() > beforePreviousPartialMatch)
|
||||
{
|
||||
const size_t beforeOffset = (d->bufferSize - beforePreviousPartialMatch);
|
||||
const size_t beforeOffset = (bufferSize() - beforePreviousPartialMatch);
|
||||
if(buffer.containsAt(before, 0, beforeOffset)) {
|
||||
seek(originalPosition);
|
||||
return -1;
|
||||
@ -236,7 +216,7 @@ offset_t File::find(const ByteVector &pattern, offset_t fromOffset, const ByteVe
|
||||
if(!before.isNull())
|
||||
beforePreviousPartialMatch = buffer.endsWithPartialMatch(before);
|
||||
|
||||
bufferOffset += d->bufferSize;
|
||||
bufferOffset += bufferSize();
|
||||
}
|
||||
|
||||
// Since we hit the end of the file, reset the status before continuing.
|
||||
@ -251,21 +231,9 @@ offset_t File::find(const ByteVector &pattern, offset_t fromOffset, const ByteVe
|
||||
|
||||
offset_t File::rfind(const ByteVector &pattern, offset_t fromOffset, const ByteVector &before)
|
||||
{
|
||||
if(!d->stream || pattern.size() > d->bufferSize)
|
||||
if(!d->stream() || pattern.size() > bufferSize())
|
||||
return -1;
|
||||
|
||||
// The position in the file that the current buffer starts at.
|
||||
|
||||
ByteVector buffer;
|
||||
|
||||
// These variables are used to keep track of a partial match that happens at
|
||||
// the end of a buffer.
|
||||
|
||||
/*
|
||||
int previousPartialMatch = -1;
|
||||
int beforePreviousPartialMatch = -1;
|
||||
*/
|
||||
|
||||
// Save the location of the current read pointer. We will restore the
|
||||
// position using seek() before all returns.
|
||||
|
||||
@ -275,17 +243,21 @@ offset_t File::rfind(const ByteVector &pattern, offset_t fromOffset, const ByteV
|
||||
|
||||
offset_t bufferOffset;
|
||||
if(fromOffset == 0) {
|
||||
seek(-1 * int(d->bufferSize), End);
|
||||
seek(-1 * int(bufferSize()), End);
|
||||
bufferOffset = tell();
|
||||
}
|
||||
else {
|
||||
seek(fromOffset + -1 * int(d->bufferSize), Beginning);
|
||||
seek(fromOffset + -1 * int(bufferSize()), Beginning);
|
||||
bufferOffset = tell();
|
||||
}
|
||||
|
||||
// See the notes in find() for an explanation of this algorithm.
|
||||
|
||||
for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
|
||||
while(true)
|
||||
{
|
||||
ByteVector buffer = readBlock(bufferSize());
|
||||
if(buffer.isEmpty())
|
||||
break;
|
||||
|
||||
// TODO: (1) previous partial match
|
||||
|
||||
@ -304,7 +276,7 @@ offset_t File::rfind(const ByteVector &pattern, offset_t fromOffset, const ByteV
|
||||
|
||||
// TODO: (3) partial match
|
||||
|
||||
bufferOffset -= d->bufferSize;
|
||||
bufferOffset -= bufferSize();
|
||||
seek(bufferOffset);
|
||||
}
|
||||
|
||||
@ -319,22 +291,22 @@ offset_t File::rfind(const ByteVector &pattern, offset_t fromOffset, const ByteV
|
||||
|
||||
void File::insert(const ByteVector &data, offset_t start, size_t replace)
|
||||
{
|
||||
d->stream->insert(data, start, replace);
|
||||
d->stream()->insert(data, start, replace);
|
||||
}
|
||||
|
||||
void File::removeBlock(offset_t start, size_t length)
|
||||
{
|
||||
d->stream->removeBlock(start, length);
|
||||
d->stream()->removeBlock(start, length);
|
||||
}
|
||||
|
||||
bool File::readOnly() const
|
||||
{
|
||||
return d->stream->readOnly();
|
||||
return d->stream()->readOnly();
|
||||
}
|
||||
|
||||
bool File::isOpen() const
|
||||
{
|
||||
return d->stream->isOpen();
|
||||
return d->stream()->isOpen();
|
||||
}
|
||||
|
||||
bool File::isValid() const
|
||||
@ -344,57 +316,27 @@ bool File::isValid() const
|
||||
|
||||
void File::seek(offset_t offset, Position p)
|
||||
{
|
||||
d->stream->seek(offset, IOStream::Position(p));
|
||||
d->stream()->seek(offset, IOStream::Position(p));
|
||||
}
|
||||
|
||||
void File::truncate(offset_t length)
|
||||
{
|
||||
d->stream->truncate(length);
|
||||
d->stream()->truncate(length);
|
||||
}
|
||||
|
||||
void File::clear()
|
||||
{
|
||||
d->stream->clear();
|
||||
d->stream()->clear();
|
||||
}
|
||||
|
||||
offset_t File::tell() const
|
||||
{
|
||||
return d->stream->tell();
|
||||
return d->stream()->tell();
|
||||
}
|
||||
|
||||
offset_t File::length()
|
||||
{
|
||||
return d->stream->length();
|
||||
}
|
||||
|
||||
bool File::isReadable(const char *file)
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later
|
||||
|
||||
return _access_s(file, R_OK) == 0;
|
||||
|
||||
#else
|
||||
|
||||
return access(file, R_OK) == 0;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool File::isWritable(const char *file)
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later
|
||||
|
||||
return _access_s(file, W_OK) == 0;
|
||||
|
||||
#else
|
||||
|
||||
return access(file, W_OK) == 0;
|
||||
|
||||
#endif
|
||||
|
||||
return d->stream()->length();
|
||||
}
|
||||
|
||||
String File::toString() const
|
||||
@ -415,9 +357,19 @@ String File::toString() const
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
File::File(const FileName &fileName)
|
||||
: d(new ManagedFilePrivate(new FileStream(fileName)))
|
||||
{
|
||||
}
|
||||
|
||||
File::File(IOStream *stream)
|
||||
: d(new UnmanagedFilePrivate(stream))
|
||||
{
|
||||
}
|
||||
|
||||
size_t File::bufferSize()
|
||||
{
|
||||
return FilePrivate::bufferSize;
|
||||
return FileStream::bufferSize();
|
||||
}
|
||||
|
||||
void File::setValid(bool valid)
|
||||
|
@ -235,21 +235,6 @@ namespace TagLib {
|
||||
*/
|
||||
offset_t length();
|
||||
|
||||
/*!
|
||||
* Returns true if \a file can be opened for reading. If the file does not
|
||||
* exist, this will return false.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
static bool isReadable(const char *file);
|
||||
|
||||
/*!
|
||||
* Returns true if \a file can be opened for writing.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
static bool isWritable(const char *name);
|
||||
|
||||
/*!
|
||||
* Returns description of the audio file and its tags.
|
||||
*/
|
||||
@ -257,13 +242,12 @@ namespace TagLib {
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Construct a File object and opens the \a file. \a file should be a
|
||||
* be a C-string in the local file system encoding.
|
||||
* Construct a File object and opens the file specified by \a fileName.
|
||||
*
|
||||
* \note Constructor is protected since this class should only be
|
||||
* instantiated through subclasses.
|
||||
*/
|
||||
File(FileName file);
|
||||
File(const FileName &fileName);
|
||||
|
||||
/*!
|
||||
* Construct a File object and use the \a stream instance.
|
||||
@ -297,8 +281,10 @@ namespace TagLib {
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
class FilePrivateBase;
|
||||
class ManagedFilePrivate;
|
||||
class UnmanagedFilePrivate;
|
||||
NonRefCountPtr<FilePrivateBase> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -43,8 +43,9 @@ namespace
|
||||
// Using Win32 native API instead of standard C file I/O to reduce the resource consumption.
|
||||
|
||||
typedef FileName FileNameHandle;
|
||||
|
||||
typedef HANDLE FileHandle;
|
||||
|
||||
const size_t BufferSize = 8192;
|
||||
const FileHandle InvalidFileHandle = INVALID_HANDLE_VALUE;
|
||||
|
||||
inline FileHandle openFile(const FileName &path, bool readOnly)
|
||||
@ -120,6 +121,8 @@ namespace
|
||||
};
|
||||
|
||||
typedef FILE* FileHandle;
|
||||
|
||||
const size_t BufferSize = 1024;
|
||||
const FileHandle InvalidFileHandle = 0;
|
||||
|
||||
inline FileHandle openFile(const FileName &path, bool readOnly)
|
||||
@ -160,8 +163,6 @@ public:
|
||||
FileNameHandle name;
|
||||
bool readOnly;
|
||||
offset_t size;
|
||||
|
||||
static const uint bufferSize = 1024;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -195,8 +196,6 @@ FileStream::~FileStream()
|
||||
{
|
||||
if(isOpen())
|
||||
closeFile(d->file);
|
||||
|
||||
delete d;
|
||||
}
|
||||
|
||||
FileName FileStream::name() const
|
||||
@ -215,7 +214,7 @@ ByteVector FileStream::readBlock(size_t length)
|
||||
return ByteVector::null;
|
||||
|
||||
const offset_t streamLength = FileStream::length();
|
||||
if(length > FileStreamPrivate::bufferSize && static_cast<offset_t>(length) > streamLength)
|
||||
if(length > BufferSize && static_cast<offset_t>(length) > streamLength)
|
||||
length = static_cast<size_t>(streamLength);
|
||||
|
||||
ByteVector buffer(length);
|
||||
@ -275,10 +274,10 @@ void FileStream::insert(const ByteVector &data, offset_t start, size_t replace)
|
||||
// the *differnce* in the tag sizes. We want to avoid overwriting parts
|
||||
// that aren't yet in memory, so this is necessary.
|
||||
|
||||
size_t bufferLength = FileStreamPrivate::bufferSize;
|
||||
size_t bufferLength = BufferSize;
|
||||
|
||||
while(data.size() - replace > bufferLength)
|
||||
bufferLength += FileStreamPrivate::bufferSize;
|
||||
bufferLength += BufferSize;
|
||||
|
||||
// Set where to start the reading and writing.
|
||||
|
||||
@ -333,7 +332,7 @@ void FileStream::removeBlock(offset_t start, size_t length)
|
||||
return;
|
||||
}
|
||||
|
||||
size_t bufferLength = FileStreamPrivate::bufferSize;
|
||||
size_t bufferLength = BufferSize;
|
||||
|
||||
offset_t readPosition = start + length;
|
||||
offset_t writePosition = start;
|
||||
@ -525,6 +524,11 @@ offset_t FileStream::length()
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t FileStream::bufferSize()
|
||||
{
|
||||
return BufferSize;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -135,9 +135,14 @@ namespace TagLib {
|
||||
*/
|
||||
void truncate(offset_t length);
|
||||
|
||||
/*!
|
||||
* Returns the buffer size that is used for internal buffering.
|
||||
*/
|
||||
static size_t bufferSize();
|
||||
|
||||
private:
|
||||
class FileStreamPrivate;
|
||||
FileStreamPrivate *d;
|
||||
NonRefCountPtr<FileStreamPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
string newname = copy.fileName();
|
||||
|
||||
FileRef *f = new FileRef(newname.c_str());
|
||||
CPPUNIT_ASSERT(f->isValid());
|
||||
CPPUNIT_ASSERT(!f->isNull());
|
||||
f->tag()->setArtist("test artist");
|
||||
f->tag()->setTitle("test title");
|
||||
@ -48,6 +49,7 @@ public:
|
||||
delete f;
|
||||
|
||||
f = new FileRef(newname.c_str());
|
||||
CPPUNIT_ASSERT(f->isValid());
|
||||
CPPUNIT_ASSERT(!f->isNull());
|
||||
CPPUNIT_ASSERT_EQUAL(f->tag()->artist(), String("test artist"));
|
||||
CPPUNIT_ASSERT_EQUAL(f->tag()->title(), String("test title"));
|
||||
@ -65,6 +67,7 @@ public:
|
||||
delete f;
|
||||
|
||||
f = new FileRef(newname.c_str());
|
||||
CPPUNIT_ASSERT(f->isValid());
|
||||
CPPUNIT_ASSERT(!f->isNull());
|
||||
CPPUNIT_ASSERT_EQUAL(f->tag()->artist(), String("ttest artist"));
|
||||
CPPUNIT_ASSERT_EQUAL(f->tag()->title(), String("ytest title"));
|
||||
@ -73,6 +76,27 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(f->tag()->track(), TagLib::uint(7));
|
||||
CPPUNIT_ASSERT_EQUAL(f->tag()->year(), TagLib::uint(2080));
|
||||
delete f;
|
||||
|
||||
f = new FileRef(newname.c_str());
|
||||
CPPUNIT_ASSERT(f->isValid());
|
||||
CPPUNIT_ASSERT(!f->isNull());
|
||||
PropertyMap prop = f->properties();
|
||||
CPPUNIT_ASSERT_EQUAL(prop["ARTIST"].front(), String("ttest artist"));
|
||||
CPPUNIT_ASSERT_EQUAL(prop["TITLE" ].front(), String("ytest title"));
|
||||
prop["ARTIST"].front() = "a test artist";
|
||||
prop["TITLE" ].front() = "b test title";
|
||||
f->setProperties(prop);
|
||||
f->save();
|
||||
delete f;
|
||||
|
||||
f = new FileRef(newname.c_str());
|
||||
CPPUNIT_ASSERT(f->isValid());
|
||||
CPPUNIT_ASSERT(!f->isNull());
|
||||
prop = f->properties();
|
||||
CPPUNIT_ASSERT_EQUAL(prop["ARTIST"].front(), String("a test artist"));
|
||||
CPPUNIT_ASSERT_EQUAL(prop["TITLE" ].front(), String("b test title"));
|
||||
delete f;
|
||||
|
||||
}
|
||||
|
||||
void testMusepack()
|
||||
|
Loading…
x
Reference in New Issue
Block a user