| /*------------------------------------------------------------------------------ |
| * Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team |
| * |
| * Distributable under the terms of either the Apache License (Version 2.0) or |
| * the GNU Lesser General Public License, as specified in the COPYING file. |
| * |
| * Changes are Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
| ------------------------------------------------------------------------------*/ |
| #include "CLucene/StdHeader.h" |
| #include "CompoundFile.h" |
| #include "CLucene/util/Misc.h" |
| |
| CL_NS_USE(store) |
| CL_NS_USE(util) |
| CL_NS_DEF(index) |
| |
| CompoundFileReader::CSIndexInput::CSIndexInput(CL_NS(store)::IndexInput* base, |
| const int64_t fileOffset, const int64_t length) |
| { |
| this->base = base; |
| this->fileOffset = fileOffset; |
| this->_length = length; |
| } |
| |
| void CompoundFileReader::CSIndexInput::readInternal(uint8_t* b, const int32_t len) |
| { |
| SCOPED_LOCK_MUTEX(base->THIS_LOCK) |
| |
| int64_t start = getFilePointer(); |
| if(start + len > _length) |
| _CLTHROWA(CL_ERR_IO, "read past EOF"); |
| base->seek(fileOffset + start); |
| base->readBytes(b, len); |
| } |
| |
| CompoundFileReader::CSIndexInput::~CSIndexInput() |
| { |
| } |
| |
| IndexInput* CompoundFileReader::CSIndexInput::clone() const |
| { |
| return _CLNEW CSIndexInput(*this); |
| } |
| |
| CompoundFileReader::CSIndexInput::CSIndexInput(const CSIndexInput& clone) |
| : BufferedIndexInput(clone) |
| { |
| this->base = clone.base; //no need to clone this.. |
| this->fileOffset = clone.fileOffset; |
| this->_length = clone._length; |
| } |
| |
| void CompoundFileReader::CSIndexInput::close() |
| { |
| } |
| |
| CompoundFileReader::CompoundFileReader(Directory* dir, const QString& name) |
| : entries(false, true) |
| { |
| directory = dir; |
| fileName = name; |
| |
| bool success = false; |
| try { |
| stream = dir->openInput(name); |
| |
| // read the directory and init files |
| int32_t count = stream->readVInt(); |
| FileEntry* entry = NULL; |
| TCHAR tid[CL_MAX_PATH]; |
| for (int32_t i = 0; i < count; i++) { |
| int64_t offset = stream->readLong(); |
| int32_t read = stream->readString(tid, CL_MAX_PATH); |
| QString aid(QString::fromWCharArray(tid, read)); |
| |
| // set length of the previous entry |
| if (entry != NULL) |
| entry->length = offset - entry->offset; |
| |
| entry = _CLNEW FileEntry(offset); |
| entries.put(aid, entry); |
| } |
| |
| // set the length of the final entry |
| if (entry != NULL) |
| entry->length = stream->length() - entry->offset; |
| success = true; |
| } _CLFINALLY ( |
| if (!success && (stream != NULL)) { |
| try { |
| stream->close(); |
| _CLDELETE(stream); |
| } catch (CLuceneError& err) { |
| if (err.number() != CL_ERR_IO) |
| throw err; |
| } |
| } |
| ) |
| } |
| |
| CompoundFileReader::~CompoundFileReader() |
| { |
| close(); |
| } |
| |
| Directory* CompoundFileReader::getDirectory() |
| { |
| return directory; |
| } |
| |
| QString CompoundFileReader::getName() const |
| { |
| return fileName; |
| } |
| |
| void CompoundFileReader::close() |
| { |
| SCOPED_LOCK_MUTEX(THIS_LOCK) |
| |
| if (stream != NULL) { |
| entries.clear(); |
| stream->close(); |
| _CLDELETE(stream); |
| } |
| } |
| |
| IndexInput* CompoundFileReader::openInput(const QString& id) |
| { |
| SCOPED_LOCK_MUTEX(THIS_LOCK) |
| |
| if (stream == NULL) |
| _CLTHROWA(CL_ERR_IO, "Stream closed"); |
| |
| const FileEntry* entry = entries.get(id); |
| if (entry == NULL) { |
| char buf[CL_MAX_PATH + 30]; |
| strcpy(buf,"No sub-file with id "); |
| strncat(buf, id.toLocal8Bit().constData(), CL_MAX_PATH); |
| strcat(buf, " found"); |
| _CLTHROWA(CL_ERR_IO,buf); |
| } |
| return _CLNEW CSIndexInput(stream, entry->offset, entry->length); |
| } |
| |
| QStringList CompoundFileReader::list() const |
| { |
| // for (EntriesType::const_iterator i=entries.begin();i!=entries.end();i++){ |
| // names->push_back(i->first); |
| // ++i; |
| // } |
| |
| QStringList names; |
| EntriesType::const_iterator itr; |
| // TODO: verify this, see old code above ??? |
| for (itr = entries.begin(); itr != entries.end(); ++itr) |
| names.push_back(itr->first); |
| |
| return names; |
| } |
| |
| bool CompoundFileReader::fileExists(const QString& name) const |
| { |
| return entries.exists(name); |
| } |
| |
| int64_t CompoundFileReader::fileModified(const QString& name) const |
| { |
| return directory->fileModified(fileName); |
| } |
| |
| void CompoundFileReader::touchFile(const QString& name) |
| { |
| directory->touchFile(fileName); |
| } |
| |
| bool CompoundFileReader::doDeleteFile(const QString& name) |
| { |
| _CLTHROWA(CL_ERR_UnsupportedOperation, |
| "UnsupportedOperationException: CompoundFileReader::doDeleteFile"); |
| } |
| |
| void CompoundFileReader::renameFile(const QString& from, const QString& to) |
| { |
| _CLTHROWA(CL_ERR_UnsupportedOperation, |
| "UnsupportedOperationException: CompoundFileReader::renameFile"); |
| } |
| |
| int64_t CompoundFileReader::fileLength(const QString& name) const |
| { |
| FileEntry* e = entries.get(name); |
| if (e == NULL) { |
| char buf[CL_MAX_PATH + 30]; |
| strcpy(buf,"File "); |
| strncat(buf, name.toLocal8Bit().constData(), CL_MAX_PATH); |
| strcat(buf," does not exist"); |
| _CLTHROWA(CL_ERR_IO,buf); |
| } |
| return e->length; |
| } |
| |
| IndexOutput* CompoundFileReader::createOutput(const QString& name) |
| { |
| _CLTHROWA(CL_ERR_UnsupportedOperation, |
| "UnsupportedOperationException: CompoundFileReader::createOutput"); |
| } |
| |
| LuceneLock* CompoundFileReader::makeLock(const QString& name) |
| { |
| _CLTHROWA(CL_ERR_UnsupportedOperation, |
| "UnsupportedOperationException: CompoundFileReader::makeLock"); |
| } |
| |
| QString CompoundFileReader::toString() const |
| { |
| QString ret(QLatin1String("CompoundFileReader@")); |
| return ret.append(fileName); |
| } |
| |
| CompoundFileWriter::CompoundFileWriter(Directory* dir, const QString& name) |
| : ids(false) |
| , entries(true) |
| { |
| if (dir == NULL) |
| _CLTHROWA(CL_ERR_NullPointer, "directory cannot be null"); |
| |
| if (name.isEmpty()) |
| _CLTHROWA(CL_ERR_NullPointer, "name cannot be null"); |
| |
| merged = false; |
| directory = dir; |
| fileName = name; |
| } |
| |
| CompoundFileWriter::~CompoundFileWriter() |
| { |
| } |
| |
| Directory* CompoundFileWriter::getDirectory() |
| { |
| return directory; |
| } |
| |
| /** Returns the name of the compound file. */ |
| QString CompoundFileWriter::getName() const |
| { |
| return fileName; |
| } |
| |
| void CompoundFileWriter::addFile(const QString& file) |
| { |
| if (merged) |
| _CLTHROWA(CL_ERR_IO, "Can't add extensions after merge has been called"); |
| |
| if (file.isEmpty()) |
| _CLTHROWA(CL_ERR_NullPointer, "file cannot be null"); |
| |
| if (ids.find(file) != ids.end()) { |
| char buf[CL_MAX_PATH + 30]; |
| strcpy(buf, "File "); |
| strncat(buf, file.toLocal8Bit().constData(), CL_MAX_PATH); |
| strcat(buf," already added"); |
| _CLTHROWA(CL_ERR_IO,buf); |
| } |
| ids.insert(file); |
| entries.push_back(_CLNEW WriterFileEntry(file)); |
| } |
| |
| void CompoundFileWriter::close() |
| { |
| if (merged) |
| _CLTHROWA(CL_ERR_IO, "Merge already performed"); |
| |
| if (entries.size() == 0) // isEmpty() |
| _CLTHROWA(CL_ERR_IO, "No entries to merge have been defined"); |
| |
| merged = true; |
| |
| // open the compound stream |
| IndexOutput* os = NULL; |
| try { |
| os = directory->createOutput(fileName); |
| |
| // Write the number of entries |
| os->writeVInt(entries.size()); |
| |
| // Write the directory with all offsets at 0. |
| // Remember the positions of directory entries so that we can |
| // adjust the offsets later |
| { //msvc6 for scope fix |
| TCHAR tfile[CL_MAX_PATH]; |
| for (CLLinkedList<WriterFileEntry*>::iterator i = entries.begin(); |
| i != entries.end(); i++) { |
| WriterFileEntry* fe = *i; |
| fe->directoryOffset = os->getFilePointer(); |
| os->writeLong(0); // for now |
| tfile[fe->file.toWCharArray(tfile)] = '\0'; |
| os->writeString(tfile, _tcslen(tfile)); |
| } |
| } |
| |
| // Open the files and copy their data into the stream. |
| // Remember the locations of each file's data section. |
| { //msvc6 for scope fix |
| int32_t bufferLength = 1024; |
| uint8_t buffer[1024]; |
| for (CLLinkedList<WriterFileEntry*>::iterator i = entries.begin(); |
| i != entries.end(); i++) { |
| WriterFileEntry* fe = *i; |
| fe->dataOffset = os->getFilePointer(); |
| copyFile(fe, os, buffer, bufferLength); |
| } |
| } |
| |
| { //msvc6 for scope fix |
| // Write the data offsets into the directory of the compound stream |
| for (CLLinkedList<WriterFileEntry*>::iterator i = entries.begin(); |
| i != entries.end(); i++) { |
| WriterFileEntry* fe = *i; |
| os->seek(fe->directoryOffset); |
| os->writeLong(fe->dataOffset); |
| } |
| } |
| |
| |
| } _CLFINALLY ( |
| if (os != NULL) { |
| try { |
| os->close(); |
| _CLDELETE(os); |
| } catch (...) { } |
| } |
| ); |
| } |
| |
| void CompoundFileWriter::copyFile(WriterFileEntry* source, IndexOutput* os, |
| uint8_t* buffer, int32_t bufferLength) |
| { |
| IndexInput* is = NULL; |
| try { |
| int64_t startPtr = os->getFilePointer(); |
| |
| is = directory->openInput(source->file); |
| int64_t length = is->length(); |
| int64_t remainder = length; |
| int32_t chunk = bufferLength; |
| |
| while(remainder > 0) { |
| int32_t len = (int32_t)min((int64_t)chunk, remainder); |
| is->readBytes(buffer, len); |
| os->writeBytes(buffer, len); |
| remainder -= len; |
| } |
| |
| // Verify that remainder is 0 |
| if (remainder != 0) { |
| TCHAR buf[CL_MAX_PATH+100]; |
| _sntprintf(buf, CL_MAX_PATH + 100, _T("Non-zero remainder length ") |
| _T("after copying: %d (id: %s, length: %d, buffer size: %d)"), |
| remainder, source->file.toLocal8Bit().constData(), length, chunk); |
| _CLTHROWT(CL_ERR_IO, buf); |
| } |
| |
| // Verify that the output length diff is equal to original file |
| int64_t endPtr = os->getFilePointer(); |
| int64_t diff = endPtr - startPtr; |
| if (diff != length) { |
| TCHAR buf[100]; |
| _sntprintf(buf, 100, _T("Difference in the output file offsets %d ") |
| _T("does not match the original file length %d"), diff, length); |
| _CLTHROWT(CL_ERR_IO,buf); |
| } |
| } _CLFINALLY ( |
| if (is != NULL) { |
| is->close(); |
| _CLDELETE(is); |
| } |
| ); |
| } |
| |
| CL_NS_END |