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