此文已由作者郑博授权网易云社区发布。
欢迎访问,了解更多网易技术产品运营经验
#endif // _WIN32}class FileStream::FileStreamPrivate{public: FileStreamPrivate(const FileName &fileName) : file(InvalidFileHandle) , name(fileName) , readOnly(true) { } FileHandle file; FileNameHandle name; bool readOnly;};// public membersFileStream::FileStream(FileName fileName, bool openReadOnly) : d(new FileStreamPrivate(fileName)){ // First try with read / write mode, if that fails, fall back to read only. if(!openReadOnly) d->file = openFile(fileName, false); if(d->file != InvalidFileHandle) d->readOnly = false; else d->file = openFile(fileName, true); if(d->file == InvalidFileHandle) {# ifdef _WIN32 debug("Could not open file " + fileName.toString());# else debug("Could not open file " + String(static_cast(d->name)));# endif }}FileStream::~FileStream(){ if(isOpen()) closeFile(d->file); delete d;}FileName FileStream::name() const{ return d->name;}ByteVector FileStream::readBlock(ulong length){ if(!isOpen()) { debug("FileStream::readBlock() -- invalid file."); return ByteVector::null; } if(length == 0) return ByteVector::null; const ulong streamLength = static_cast(FileStream::length()); if(length > bufferSize() && length > streamLength) length = streamLength; ByteVector buffer(static_cast(length)); const size_t count = readFile(d->file, buffer); buffer.resize(static_cast(count)); return buffer;}void FileStream::writeBlock(const ByteVector &data){ if(!isOpen()) { debug("FileStream::writeBlock() -- invalid file."); return; } if(readOnly()) { debug("FileStream::writeBlock() -- read only file."); return; } writeFile(d->file, data);}void FileStream::insert(const ByteVector &data, ulong start, ulong replace){ if(!isOpen()) { debug("FileStream::insert() -- invalid file."); return; } if(readOnly()) { debug("FileStream::insert() -- read only file."); return; } if(data.size() == replace) { seek(start); writeBlock(data); return; } else if(data.size() < replace) { 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 // and avoid TagLib's high level API for rendering just copying parts of // the file that don't contain tag data. // // Now I'll explain the steps in this ugliness: // First, make sure that we're working with a buffer that is longer than // the *differnce* in the tag sizes. We want to avoid overwriting parts // that aren't yet in memory, so this is necessary. ulong bufferLength = bufferSize(); while(data.size() - replace > bufferLength) bufferLength += bufferSize(); // Set where to start the reading and writing. long readPosition = start + replace; long writePosition = start; ByteVector buffer = data; ByteVector aboutToOverwrite(static_cast(bufferLength)); while(true) { // Seek to the current read position and read the data that we're about // to overwrite. Appropriately increment the readPosition. seek(readPosition); 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(bytesRead < bufferLength) clear(); // Seek to the write position and write our buffer. Increment the // writePosition. seek(writePosition); 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; }}void FileStream::removeBlock(ulong start, ulong length){ if(!isOpen()) { debug("FileStream::removeBlock() -- invalid file."); return; } ulong bufferLength = bufferSize(); long readPosition = start + length; long writePosition = start; ByteVector buffer(static_cast(bufferLength)); for(size_t bytesRead = -1; bytesRead != 0;) { seek(readPosition); bytesRead = readFile(d->file, buffer); readPosition += bytesRead; // 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(bytesRead < buffer.size()) { clear(); buffer.resize(bytesRead); } seek(writePosition); writeFile(d->file, buffer); writePosition += bytesRead; } truncate(writePosition);}bool FileStream::readOnly() const{ return d->readOnly;}bool FileStream::isOpen() const{ return (d->file != InvalidFileHandle);}void FileStream::seek(long offset, Position p){ if(!isOpen()) { debug("FileStream::seek() -- invalid file."); return; }#ifdef _WIN32 DWORD whence; switch(p) { case Beginning: whence = FILE_BEGIN; break; case Current: whence = FILE_CURRENT; break; case End: whence = FILE_END; break; default: debug("FileStream::seek() -- Invalid Position value."); return; }#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) LARGE_INTEGER distance = { 0 }; distance.QuadPart = offset; if (!SetFilePointerEx(d->file, distance, NULL, whence))#else SetLastError(NO_ERROR); SetFilePointer(d->file, offset, NULL, whence); const int lastError = GetLastError(); if(lastError != NO_ERROR && lastError != ERROR_NEGATIVE_SEEK)#endif debug("FileStream::seek() -- Failed to set the file pointer.");#else int whence; switch(p) { case Beginning: whence = SEEK_SET; break; case Current: whence = SEEK_CUR; break; case End: whence = SEEK_END; break; default: debug("FileStream::seek() -- Invalid Position value."); return; } fseek(d->file, offset, whence);#endif}void FileStream::clear(){#ifdef _WIN32 // NOP#else clearerr(d->file);#endif}long FileStream::tell() const{#ifdef _WIN32#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) LARGE_INTEGER distance = { 0 }; LARGE_INTEGER position = { 0 }; if (SetFilePointerEx(d->file, distance, &position, FILE_CURRENT)) { return static_cast(position.QuadPart); }#else SetLastError(NO_ERROR); const DWORD position = SetFilePointer(d->file, 0, NULL, FILE_CURRENT); if(GetLastError() == NO_ERROR) { return static_cast(position); }#endif else { debug("FileStream::tell() -- Failed to get the file pointer."); return 0; }#else return ftell(d->file);#endif}long FileStream::length(){ if(!isOpen()) { debug("FileStream::length() -- invalid file."); return 0; }#ifdef _WIN32#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) LARGE_INTEGER fileSize = { 0 }; if (GetFileSizeEx(d->file, &fileSize)) { return static_cast(fileSize.QuadPart); }#else SetLastError(NO_ERROR); const DWORD fileSize = GetFileSize(d->file, NULL); if (GetLastError() == NO_ERROR) { return static_cast(fileSize); }#endif else { debug("FileStream::length() -- Failed to get the file size."); return 0; }#else const long curpos = tell(); seek(0, End); const long endpos = tell(); seek(curpos, Beginning); return endpos;#endif}// protected membersvoid FileStream::truncate(long length){#ifdef _WIN32 const long currentPos = tell(); seek(length); SetLastError(NO_ERROR); SetEndOfFile(d->file); if(GetLastError() != NO_ERROR) { debug("FileStream::truncate() -- Failed to truncate the file."); } seek(currentPos);#else const int error = ftruncate(fileno(d->file), length); if(error != 0) { debug("FileStream::truncate() -- Coundn't truncate the file."); }#endif}TagLib::uint FileStream::bufferSize(){ return 1024;}
为了便于调试,还需要修改taglib\toolkit\tdebuglistener.cpp,以便在调试直接在Output窗口输出调试信息,完整代码如下:
/*************************************************************************** copyright : (C) 2013 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************//*************************************************************************** * 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 "tdebuglistener.h"#include #include #ifdef _WIN32# include #endifusing namespace TagLib;namespace{ class DefaultListener : public DebugListener { public: virtual void printMessage(const String &msg) {#ifdef _WIN32 const wstring wstr = msg.toWString();#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) OutputDebugStringW(wstr.c_str());#else const int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL); if(len != 0) { std::vector buf(len); WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &buf[0], len, NULL, NULL); std::cerr << std::string(&buf[0]); }#endif#else std::cerr << msg;#endif } }; DefaultListener defaultListener;}namespace TagLib{ DebugListener *debugListener = &defaultListener; DebugListener::DebugListener() { } DebugListener::~DebugListener() { } void setDebugListener(DebugListener *listener) { if(listener) debugListener = listener; else debugListener = &defaultListener; }}
最后,编译吧,骚年!!!
更多网易技术、产品、运营经验分享请。
相关文章:
【推荐】 【推荐】