Small refactoring of FileStream

This commit is contained in:
Tsuda Kageyu 2013-05-19 02:33:17 +09:00
parent 229d69864d
commit dcf11b9586
2 changed files with 106 additions and 130 deletions

View File

@ -729,9 +729,11 @@ TagLib::uint ByteVector::size() const
ByteVector &ByteVector::resize(uint size, char padding)
{
detach();
d->data->data.resize(d->offset + size, padding);
d->length = size;
if(size != d->length) {
detach();
d->data->data.resize(d->offset + size, padding);
d->length = size;
}
return *this;
}

View File

@ -51,9 +51,10 @@ namespace
typedef FileName FileNameHandle;
# define INVALID_FILE INVALID_HANDLE_VALUE
typedef HANDLE FileHandle;
const FileHandle InvalidFileHandle = INVALID_HANDLE_VALUE;
HANDLE openFile(const FileName &path, bool readOnly)
inline FileHandle openFile(const FileName &path, bool readOnly)
{
const DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
@ -62,29 +63,32 @@ namespace
else if(!path.str().empty())
return CreateFileA(path.str().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
else
return INVALID_FILE;
return InvalidFileHandle;
}
size_t fread(void *ptr, size_t size, size_t nmemb, HANDLE stream)
inline void closeFile(FileHandle file)
{
DWORD readLen;
if(ReadFile(stream, ptr, size * nmemb, &readLen, NULL))
return (readLen / size);
CloseHandle(file);
}
inline size_t readFile(FileHandle file, ByteVector &buffer)
{
DWORD length;
if(ReadFile(file, buffer.data(), static_cast<DWORD>(buffer.size()), &length, NULL))
return static_cast<size_t>(length);
else
return 0;
}
size_t fwrite(const void *ptr, size_t size, size_t nmemb, HANDLE stream)
inline size_t writeFile(FileHandle file, const ByteVector &buffer)
{
DWORD writtenLen;
if(WriteFile(stream, ptr, size * nmemb, &writtenLen, NULL))
return (writtenLen / size);
DWORD length;
if(WriteFile(file, buffer.data(), static_cast<DWORD>(buffer.size()), &length, NULL))
return static_cast<size_t>(length);
else
return 0;
}
# if _DEBUG
// Convert a string in a local encoding into a UTF-16 string.
// This function should only be used to generate an error message.
@ -111,78 +115,59 @@ namespace
}
}
# endif
#else
# define INVALID_FILE 0
struct FileNameHandle : public std::string
{
FileNameHandle(FileName name) : std::string(name) {}
operator FileName () const { return c_str(); }
};
FILE *openFile(const FileName &path, bool readOnly)
typedef FILE* FileHandle;
const FileHandle InvalidFileHandle = 0;
inline FileHandle openFile(const FileName &path, bool readOnly)
{
return fopen(path, readOnly ? "rb" : "rb+");
}
inline void closeFile(FileHandle file)
{
fclose(file);
}
inline size_t readFile(FileHandle file, ByteVector &buffer)
{
return fread(buffer.data(), sizeof(char), buffer.size(), file);
}
inline size_t writeFile(FileHandle file, const ByteVector &buffer)
{
return fwrite(buffer.data(), sizeof(char), buffer.size(), file);
}
#endif
}
class FileStream::FileStreamPrivate
{
public:
FileStreamPrivate(const FileName &fileName, bool openReadOnly);
#ifdef _WIN32
HANDLE file;
#else
FILE *file;
#endif
FileStreamPrivate(const FileName &fileName, bool openReadOnly)
: file(InvalidFileHandle)
, name(fileName)
, readOnly(openReadOnly)
, size(0)
{
}
FileHandle file;
FileNameHandle name;
bool readOnly;
ulong size;
static const uint bufferSize = 1024;
};
FileStream::FileStreamPrivate::FileStreamPrivate(const FileName &fileName, bool openReadOnly) :
file(INVALID_FILE),
name(fileName),
readOnly(true),
size(0)
{
// First try with read / write mode, if that fails, fall back to read only.
if(!openReadOnly)
file = openFile(name, false);
if(file != INVALID_FILE)
readOnly = false;
else
file = openFile(name, true);
if(file == INVALID_FILE)
{
# ifdef _WIN32
debug("Could not open file " + fileNameToString(name));
# else
debug("Could not open file " + String((const char *) name));
# endif
}
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
@ -190,21 +175,30 @@ FileStream::FileStreamPrivate::FileStreamPrivate(const FileName &fileName, bool
FileStream::FileStream(FileName file, bool openReadOnly)
: d(new FileStreamPrivate(file, openReadOnly))
{
// First try with read / write mode, if that fails, fall back to read only.
if(!openReadOnly)
d->file = openFile(file, false);
if(d->file != InvalidFileHandle)
d->readOnly = false;
else
d->file = openFile(d->name, true);
if(d->file == InvalidFileHandle)
{
# ifdef _WIN32
debug("Could not open file " + fileNameToString(d->name));
# else
debug("Could not open file " + String(static_cast<const char *>(d->name)));
# endif
}
}
FileStream::~FileStream()
{
#ifdef _WIN32
if(isOpen())
CloseHandle(d->file);
#else
if(isOpen())
fclose(d->file);
#endif
closeFile(d->file);
delete d;
}
@ -224,16 +218,16 @@ ByteVector FileStream::readBlock(ulong length)
if(length == 0)
return ByteVector::null;
if(length > FileStreamPrivate::bufferSize &&
length > ulong(FileStream::length()))
{
length = FileStream::length();
}
const ulong streamLength = static_cast<ulong>(FileStream::length());
if(length > bufferSize() && length > streamLength)
length = streamLength;
ByteVector v(static_cast<uint>(length));
const int count = fread(v.data(), sizeof(char), length, d->file);
v.resize(count);
return v;
ByteVector buffer(static_cast<uint>(length));
const size_t count = readFile(d->file, buffer);
buffer.resize(static_cast<uint>(count));
return buffer;
}
void FileStream::writeBlock(const ByteVector &data)
@ -248,7 +242,7 @@ void FileStream::writeBlock(const ByteVector &data)
return;
}
fwrite(data.data(), sizeof(char), data.size(), d->file);
writeFile(d->file, data);
}
void FileStream::insert(const ByteVector &data, ulong start, ulong replace)
@ -269,10 +263,10 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace)
return;
}
else if(data.size() < replace) {
seek(start);
writeBlock(data);
removeBlock(start + data.size(), replace - data.size());
return;
seek(start);
writeBlock(data);
removeBlock(start + data.size(), replace - data.size());
return;
}
// Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore
@ -295,64 +289,41 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace)
long readPosition = start + replace;
long writePosition = start;
ByteVector buffer;
ByteVector buffer = data;
ByteVector aboutToOverwrite(static_cast<uint>(bufferLength));
// This is basically a special case of the loop below. Here we're just
// doing the same steps as below, but since we aren't using the same buffer
// size -- instead we're using the tag size -- this has to be handled as a
// special case. We're also using File::writeBlock() just for the tag.
// That's a bit slower than using char *'s so, we're only doing it here.
seek(readPosition);
int bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
readPosition += bufferLength;
seek(writePosition);
writeBlock(data);
writePosition += data.size();
buffer = aboutToOverwrite;
// In case we've already reached the end of file...
buffer.resize(bytesRead);
// Ok, here's the main loop. We want to loop until the read fails, which
// means that we hit the end of the file.
while(!buffer.isEmpty()) {
while(true)
{
// Seek to the current read position and read the data that we're about
// to overwrite. Appropriately increment the readPosition.
seek(readPosition);
bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
const size_t bytesRead = readFile(d->file, aboutToOverwrite);
aboutToOverwrite.resize(bytesRead);
readPosition += bufferLength;
// Check to see if we just read the last block. We need to call clear()
// if we did so that the last write succeeds.
if(ulong(bytesRead) < bufferLength)
if(bytesRead < bufferLength)
clear();
// Seek to the write position and write our buffer. Increment the
// writePosition.
seek(writePosition);
fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);
writeBlock(buffer);
// We hit the end of the file.
if(bytesRead == 0)
break;
writePosition += buffer.size();
// Make the current buffer the data that we read in the beginning.
buffer = aboutToOverwrite;
// Again, we need this for the last write. We don't want to write garbage
// at the end of our file, so we need to set the buffer size to the amount
// that we actually read.
bufferLength = bytesRead;
}
}
@ -370,11 +341,9 @@ void FileStream::removeBlock(ulong start, ulong length)
ByteVector buffer(static_cast<uint>(bufferLength));
ulong bytesRead = 1;
while(bytesRead != 0) {
while(true) {
seek(readPosition);
bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file);
const size_t bytesRead = readFile(d->file, buffer);
readPosition += bytesRead;
// Check to see if we just read the last block. We need to call clear()
@ -384,9 +353,14 @@ void FileStream::removeBlock(ulong start, ulong length)
clear();
seek(writePosition);
fwrite(buffer.data(), sizeof(char), bytesRead, d->file);
writeFile(d->file, buffer);
if(bytesRead == 0)
break;
writePosition += bytesRead;
}
truncate(writePosition);
}
@ -397,7 +371,7 @@ bool FileStream::readOnly() const
bool FileStream::isOpen() const
{
return (d->file != INVALID_FILE);
return (d->file != InvalidFileHandle);
}
void FileStream::seek(long offset, Position p)