/* * 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) 2012 Digia Plc and/or its subsidiary(-ies). */ #include "CLucene/StdHeader.h" #include "RAMDirectory.h" #include "Lock.h" #include "Directory.h" #include "FSDirectory.h" #include "CLucene/index/IndexReader.h" #include "CLucene/util/VoidMap.h" #include "CLucene/util/Misc.h" #include "CLucene/debug/condition.h" CL_NS_USE(util) CL_NS_DEF(store) RAMFile::RAMFile() { length = 0; lastModified = Misc::currentTimeMillis(); } RAMFile::~RAMFile() { } RAMDirectory::RAMLock::RAMLock(const QString& name, RAMDirectory* dir) : directory(dir) { fname = name; } RAMDirectory::RAMLock::~RAMLock() { directory = NULL; } QString RAMDirectory::RAMLock::toString() const { return QLatin1String("LockFile@RAM"); } bool RAMDirectory::RAMLock::isLocked() { return directory->fileExists(fname); } bool RAMDirectory::RAMLock::obtain() { SCOPED_LOCK_MUTEX(directory->files_mutex); if (!directory->fileExists(fname)) { IndexOutput* tmp = directory->createOutput(fname); tmp->close(); _CLDELETE(tmp); return true; } return false; } void RAMDirectory::RAMLock::release() { directory->deleteFile(fname); } RAMIndexOutput::~RAMIndexOutput() { if (deleteFile) _CLDELETE(file); file = NULL; } RAMIndexOutput::RAMIndexOutput(RAMFile* f) : file(f) { pointer = 0; deleteFile = false; } RAMIndexOutput::RAMIndexOutput() : file(_CLNEW RAMFile) { pointer = 0; deleteFile = true; } void RAMIndexOutput::writeTo(IndexOutput* out) { flush(); int64_t end = file->length; int64_t pos = 0; int32_t p = 0; while (pos < end) { int32_t length = CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE; int64_t nextPos = pos + length; if (nextPos > end) { // at the last buffer length = (int32_t)(end - pos); } out->writeBytes((uint8_t*)file->buffers[p++], length); pos = nextPos; } } void RAMIndexOutput::reset() { seek(_ILONGLONG(0)); file->length = _ILONGLONG(0); } void RAMIndexOutput::flushBuffer(const uint8_t* src, const int32_t len) { uint8_t* b = NULL; int32_t bufferPos = 0; while (bufferPos != len) { uint32_t bufferNumber = pointer/CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE; int32_t bufferOffset = pointer%CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE; int32_t bytesInBuffer = CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE - bufferOffset; int32_t remainInSrcBuffer = len - bufferPos; int32_t bytesToCopy = bytesInBuffer >= remainInSrcBuffer ? remainInSrcBuffer : bytesInBuffer; if (bufferNumber == file->buffers.size()){ b = _CL_NEWARRAY(uint8_t, CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE); file->buffers.push_back( b ); }else{ b = file->buffers[bufferNumber]; } memcpy(b+bufferOffset, src+bufferPos, bytesToCopy * sizeof(uint8_t)); bufferPos += bytesToCopy; pointer += bytesToCopy; } if (pointer > file->length) file->length = pointer; file->lastModified = Misc::currentTimeMillis(); } void RAMIndexOutput::close() { BufferedIndexOutput::close(); } /** Random-at methods */ void RAMIndexOutput::seek(const int64_t pos) { BufferedIndexOutput::seek(pos); pointer = (int32_t)pos; } int64_t RAMIndexOutput::length() { return file->length; } RAMIndexInput::RAMIndexInput(RAMFile* f) : file(f) { pointer = 0; _length = f->length; } RAMIndexInput::RAMIndexInput(const RAMIndexInput& other) : BufferedIndexInput(other) { file = other.file; pointer = other.pointer; _length = other._length; } RAMIndexInput::~RAMIndexInput() { RAMIndexInput::close(); } IndexInput* RAMIndexInput::clone() const { return _CLNEW RAMIndexInput(*this); } int64_t RAMIndexInput::length() { return _length; } QString RAMIndexInput::getDirectoryType() const { return RAMDirectory::DirectoryType(); } void RAMIndexInput::readInternal(uint8_t* dest, const int32_t len) { const int64_t bytesAvailable = file->length - pointer; int64_t remainder = len <= bytesAvailable ? len : bytesAvailable; int32_t start = pointer; int32_t destOffset = 0; while (remainder != 0) { int32_t bufferNumber = start / CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE; int32_t bufferOffset = start % CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE; int32_t bytesInBuffer = CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE - bufferOffset; /* The buffer's entire length (bufferLength) is defined by IndexInput.h ** as int32_t, so obviously the number of bytes in a given segment of the ** buffer won't exceed the the capacity of int32_t. Therefore, the ** int64_t->int32_t cast on the next line is safe. */ int32_t bytesToCopy = bytesInBuffer >= remainder ? static_cast(remainder) : bytesInBuffer; uint8_t* b = file->buffers[bufferNumber]; memcpy(dest + destOffset, b + bufferOffset, bytesToCopy * sizeof(uint8_t)); destOffset += bytesToCopy; start += bytesToCopy; remainder -= bytesToCopy; pointer += bytesToCopy; } } void RAMIndexInput::close() { BufferedIndexInput::close(); } void RAMIndexInput::seekInternal(const int64_t pos) { CND_PRECONDITION(pos >= 0 && pos < this->_length, "Seeking out of range") pointer = (int32_t)pos; } // #pragma mark -- RAMDirectory QStringList RAMDirectory::list() const { SCOPED_LOCK_MUTEX(files_mutex); QStringList names; FileMap::const_iterator itr; for (itr = files.begin(); itr != files.end(); ++itr) names.push_back(itr->first); return names; } RAMDirectory::RAMDirectory() : Directory() , files(false, true) { } RAMDirectory::~RAMDirectory() { //todo: should call close directory? } void RAMDirectory::_copyFromDir(Directory* dir, bool closeDir) { QStringList names = dir->list(); uint8_t buf[CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE]; foreach (const QString& name, names) { if (!CL_NS(index)::IndexReader::isLuceneFile(name)) continue; // make place on ram disk IndexOutput* os = createOutput(name); // read current file IndexInput* is = dir->openInput(name); // and copy to ram disk //todo: this could be a problem when copying from big indexes... int64_t readCount = 0; int64_t len = is->length(); while (readCount < len) { int32_t toRead = CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE; if ((readCount + toRead) > len) toRead = int32_t(len - readCount); is->readBytes(buf, toRead); os->writeBytes(buf, toRead); readCount += toRead; } // graceful cleanup is->close(); _CLDELETE(is); os->close(); _CLDELETE(os); } if (closeDir) dir->close(); } RAMDirectory::RAMDirectory(Directory* dir) : Directory() , files(false, true) { _copyFromDir(dir, false); } RAMDirectory::RAMDirectory(const QString& dir) : Directory() , files(false, true) { Directory* fsdir = FSDirectory::getDirectory(dir, false); try { _copyFromDir(fsdir, false); } _CLFINALLY ( fsdir->close(); _CLDECDELETE(fsdir); ); } bool RAMDirectory::fileExists(const QString& name) const { SCOPED_LOCK_MUTEX(files_mutex); return files.exists(name); } int64_t RAMDirectory::fileModified(const QString& name) const { SCOPED_LOCK_MUTEX(files_mutex); const RAMFile* f = files.get(name); return f->lastModified; } int64_t RAMDirectory::fileLength(const QString& name) const { SCOPED_LOCK_MUTEX(files_mutex); RAMFile* f = files.get(name); return f->length; } IndexInput* RAMDirectory::openInput(const QString& name) { SCOPED_LOCK_MUTEX(files_mutex); RAMFile* file = files.get(name); if (file == NULL) { _CLTHROWA(CL_ERR_IO, // DSR:PROPOSED: Better error checking. "[RAMDirectory::open] The requested file does not exist."); } return _CLNEW RAMIndexInput(file); } void RAMDirectory::close() { SCOPED_LOCK_MUTEX(files_mutex); files.clear(); } bool RAMDirectory::doDeleteFile(const QString& name) { SCOPED_LOCK_MUTEX(files_mutex); files.remove(name); return true; } void RAMDirectory::renameFile(const QString& from, const QString& to) { SCOPED_LOCK_MUTEX(files_mutex); FileMap::iterator itr = files.find(from); /* DSR:CL_BUG_LEAK: ** If a file named $to already existed, its old value was leaked. ** My inclination would be to prevent this implicit deletion with an ** exception, but it happens routinely in CLucene's internals (e.g., during ** IndexWriter.addIndexes with the file named 'segments'). */ if (files.exists(to)) files.remove(to); if (itr == files.end()) { char tmp[1024]; _snprintf(tmp, 1024, "cannot rename %s, file does not exist", from.toLocal8Bit().constData()); _CLTHROWT(CL_ERR_IO, tmp); } CND_PRECONDITION(itr != files.end(), "itr == files.end()") RAMFile* file = itr->second; files.removeitr(itr, true, true); files.put(to, file); } void RAMDirectory::touchFile(const QString& name) { RAMFile* file = NULL; { SCOPED_LOCK_MUTEX(files_mutex); file = files.get(name); } uint64_t ts1 = file->lastModified; uint64_t ts2 = Misc::currentTimeMillis(); //make sure that the time has actually changed while (ts1 == ts2) { _LUCENE_SLEEP(1); ts2 = Misc::currentTimeMillis(); }; file->lastModified = ts2; } IndexOutput* RAMDirectory::createOutput(const QString& name) { /* Check the $files VoidMap to see if there was a previous file named ** $name. If so, delete the old RAMFile object, but reuse the existing ** char buffer ($n) that holds the filename. If not, duplicate the ** supplied filename buffer ($name) and pass ownership of that memory ($n) ** to $files. */ SCOPED_LOCK_MUTEX(files_mutex); QString n = files.getKey(name); if (!n.isEmpty()) { RAMFile* rf = files.get(name); _CLDELETE(rf); } else { n = name; } RAMFile* file = _CLNEW RAMFile(); #ifdef _DEBUG file->filename = n; #endif files[n] = file; return _CLNEW RAMIndexOutput(file); } LuceneLock* RAMDirectory::makeLock(const QString& name) { return _CLNEW RAMLock(name, this); } QString RAMDirectory::toString() const { return QLatin1String("RAMDirectory"); } CL_NS_END