Merge pull request #206 from TsudaKageyu/smartptrfile

Rewrote File, FileRef and FileStream to use smart pointers
This commit is contained in:
Tsuda Kageyu 2013-05-19 06:17:19 -07:00
commit 0a4756b527
8 changed files with 349 additions and 299 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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