此文已由作者郑博授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验
#endif // _WIN32 } class FileStream::FileStreamPrivate { public: FileStreamPrivate(const FileName &fileName) : file(InvalidFileHandle) , name(fileName) , readOnly(true) { } FileHandle file; FileNameHandle name; bool readOnly; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// FileStream::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 members //////////////////////////////////////////////////////////////////////////////// void 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 oolkit debuglistener.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 #endif using 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; } }
最后,编译吧,骚年!!!
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 Docker容器的原理与实践 (下)
【推荐】 Shadowsocks原理详解(上篇)