* Merged in jbm's changes for Linux compatibility, fixing Mac compatibility in the process :)
* Fixed two regressions having to do with the _previousTrailerPosition in VersionDictionary.cpp.
* Made sure RTTI is enabled in the OttomanTest target because gtest requires it.
2 * VersionDictionary.cpp
5 * Created by Jens Alfke on 8/21/09.
6 * Copyright 2009 Jens Alfke. All rights reserved.
7 * BSD-Licensed: See the file "LICENSE.txt" for details.
10 #include "VersionDictionary.h"
24 // Trailer is stored in file; all numbers are little-endian.
25 class VersionDictionary::Trailer :public Chunk {
27 LittleEndian<uint32_t> magicNumber1;
28 LittleEndian<uint32_t> count;
29 VersionDictionary::IndexPositions indexPositions;
30 LittleEndian<FilePosition> previousTrailerPosition;
31 LittleEndian<uint32_t> generation;
32 LittleEndianDouble timestamp;
33 LittleEndian<uint32_t> magicNumber2;
36 :Chunk(sizeof(Trailer), kChunkType)
40 VersionDictionary::IndexPositions indexPos,
41 FilePosition previousTrailerPos,
43 :Chunk(sizeof(Trailer), kChunkType),
44 magicNumber1(kMagicNumber1),
46 indexPositions(indexPos),
47 previousTrailerPosition(previousTrailerPos),
49 timestamp(::time(NULL)),
50 magicNumber2(kMagicNumber2)
53 static const uint32_t kMagicNumber1 = 0xfe83b1cd;
54 static const uint32_t kMagicNumber2 = 0xff84b2c1;
59 VersionDictionary::VersionDictionary (File *file)
62 _previousTrailerPosition(0),
67 if (file->length() > sizeof(Trailer))
71 VersionDictionary::VersionDictionary (File *file, FilePosition trailerPosition)
73 _trailerPosition(trailerPosition),
74 _previousTrailerPosition(0),
79 _read(trailerPosition);
82 int VersionDictionary::count() const {
86 const VersionDictionary::Trailer* VersionDictionary::_trailer() const {
87 if (_trailerPosition > 0)
88 return (const VersionDictionary::Trailer*) _file->mappedPosition(_trailerPosition);
90 return NULL; // Only happens in the empty-file case
93 const Index* VersionDictionary::_index (int i) const {
94 if (_indexPositions[i] > 0)
95 return (const Index*) _file->mappedPosition(_indexPositions[i]);
100 int VersionDictionary::generation() const {
101 const VersionDictionary::Trailer *trailer = _trailer();
102 return trailer ? (int)trailer->generation : -1;
105 time_t VersionDictionary::timestamp() const {
106 const VersionDictionary::Trailer *trailer = _trailer();
107 return trailer ? (time_t)trailer->timestamp : 0;
110 const VersionDictionary* VersionDictionary::previousVersion() const {
111 if (!_previousVersion)
112 if (_previousTrailerPosition > 0)
113 _previousVersion = new VersionDictionary(_file, _previousTrailerPosition);
114 return _previousVersion;
118 Blob VersionDictionary::get (Key key) const {
119 const Index *index = _index(key.hash >> 24);
120 return index ?index->get(_file, key) :Blob();
123 void VersionDictionary::_read (FilePosition trailerPos) {
126 if (trailerPos > 0) {
127 _file->setPosition(trailerPos);
129 // Determine position of trailer, at EOF:
130 off_t pos = _file->setPositionToEnd(sizeof(VersionDictionary::Trailer));
131 if (pos < 0 || (pos & 0x03) || pos > UINT32_MAX)
132 throw File::Error(ERANGE, "No trailer found in file (wrong EOF)");
133 trailerPos = (FilePosition)pos;
136 // Read & verify trailer:
137 VersionDictionary::Trailer trailer;
138 _file->read(trailer);
139 _trailerPosition = trailerPos;
140 _previousTrailerPosition = trailer.previousTrailerPosition;
141 _count = trailer.count;
142 _indexPositions = trailer.indexPositions;
144 if (trailer.magicNumber1 != VersionDictionary::Trailer::kMagicNumber1
145 || trailer.magicNumber2 != VersionDictionary::Trailer::kMagicNumber2)
146 throw File::Error("No trailer found in file (invalid magic numbers)");
147 if (_previousTrailerPosition >= _trailerPosition)
148 throw File::Error("Bad VersionDictionary trailer (illegal previousTrailerPosition)");
151 _file->mapRegion(0, _trailerPosition+sizeof(trailer));
154 for (int i=0; i<256; i++) {
155 if (_indexPositions[i] >= _trailerPosition)
156 throw File::Error("Bad VersionDictionary trailer (illegal index position)");
157 const Index *index = _index(i);
164 bool VersionDictionary::isLatestVersion() const {
165 return _file->length() <= _trailerPosition+sizeof(VersionDictionary::Trailer);
169 /* Append addDict to baseDict, writing the results into dstFile (which is usually the same
170 as baseDict->file().) If 'replace' is true, ignore the old contents of baseDict.
171 Returns the position of the new trailer. */
172 FilePosition VersionDictionary::_append (const VersionDictionary *baseDict,
173 const Dictionary *addDict,
177 File *srcFile = baseDict->_file;
178 bool incremental = !replace && dstFile==srcFile;
179 Index* newIndex[256];
182 // Work out the needed capacity for each Index bucket:
183 int newCounts[256] = {0};
185 for (int i=0; i<256; i++) {
186 const Index *oldIndex = baseDict->_index(i);
188 newCounts[i] = oldIndex->count();
191 Dictionary::Iterator *it = addDict->iterate();
193 newCounts[it->key().hash >> 24]++;
196 // Allocate new Indexes, of sufficient capacity, for each growing bucket:
197 for (int i=0; i<256; i++) {
198 const Index *oldIndex = baseDict->_index(i);
199 if (newCounts[i] && (!incremental || !oldIndex || newCounts[i] > oldIndex->count())) {
200 newIndex[i] = Index::create(newCounts[i]);
201 if (incremental && oldIndex)
202 newIndex[i]->copyFrom(srcFile, oldIndex);
208 // Lock the file now, seek to the end, and make sure it's been prepared with a header,
209 // since FilePositions of 0 and 1 are reserved.
210 File::Lock lock(dstFile);
211 const FilePosition startPos = (FilePosition) dstFile->setPositionToEnd();
213 throw File::Error(ERANGE, "Cannot write VersionDictionary to empty file");
215 // For safety's sake, make sure the old file hasn't been destroyed:
216 FilePosition oldEOF = baseDict->_trailerPosition;
218 oldEOF += sizeof(VersionDictionary::Trailer);
219 if (srcFile->length() < oldEOF)
220 throw File::Error(ERANGE, "File has been truncated since being read");
223 FilePosition pos = startPos;
228 else if (dstFile == srcFile)
229 newCount = baseDict->_count;
231 // Write out the surviving pre-existing entries from the old file:
233 for (VersionDictionary::Iterator it(baseDict); it; ++it) {
235 if (!addDict->contains(key)) {
236 int bucket = key.hash >> 24;
237 int size = KeyValueChunk::write(dstFile,pos, key, it.value());
238 newIndex[bucket]->put(srcFile, key, pos, startPos);
245 // Now write the items from the new dict:
246 Dictionary::Iterator *it = addDict->iterate();
249 Blob value=it->value();
250 int bucket = key.hash >> 24;
251 Index *index = newIndex[bucket];
255 int size = KeyValueChunk::write(dstFile,pos, key, value);
256 if (index->put(srcFile, key, pos, startPos))
259 } else if (incremental) {
260 // NULL value is a deleted-entry marker used by OverlayDictionary
261 if (index->remove(srcFile, key, startPos))
267 // Write out the new indexes:
268 IndexPositions newIndexPositions;
270 newIndexPositions = baseDict->_indexPositions;
272 memset(&newIndexPositions, 0, sizeof(newIndexPositions));
274 pos += Chunk::writePadding(dstFile);
275 for (int i=0; i<256; i++) {
277 Index *index = newIndex[i];
278 newIndexPositions[i] = pos;
279 pos += dstFile->write(index, index->size());
284 // Flush everything out to disk, with maximum paranoia, before writing the trailer.
285 // Since scavenging corrupt files looks for trailers, we don't want to append a
286 // trailer until we're certain that all of the real data is safely on-disk.
287 dstFile->flushDisk();
289 // Write the trailer:
290 FilePosition previousTrailerPosition = 0;
291 if (dstFile==srcFile)
292 previousTrailerPosition = baseDict->_trailerPosition;
293 FilePosition newTrailerPosition = pos;
294 VersionDictionary::Trailer trailer(newCount,
296 previousTrailerPosition,
297 baseDict->generation() + 1);
298 pos += dstFile->write(trailer);
300 // Just a mild flush here; flushDisk() is very expensive and can be disruptive to
301 // real-time I/O in other apps, so it's bad to call it too often.
303 assert(pos==dstFile->position());
305 return newTrailerPosition;
308 // If something goes wrong, try to back out everything we wrote:
310 dstFile->setLength(startPos);
317 VersionDictionary* VersionDictionary::_appendAndOpen (const Dictionary *addDict,
321 FilePosition nextVersionPos = _append(this, addDict, dstFile, replace);
322 VersionDictionary *nextVersion = new VersionDictionary(dstFile, nextVersionPos);
323 nextVersion->_previousVersion = this;
329 #pragma mark TESTING-ONLY:
331 VersionDictionary* VersionDictionary::create (File *file, const Dictionary *srcDict) {
332 return VersionDictionary(file)._appendAndOpen(srcDict, file, true);
337 #pragma mark ITERATOR:
339 Dictionary::Iterator* VersionDictionary::iterate() const {
340 return new VersionDictionary::Iterator(this);
343 VersionDictionary::Iterator::Iterator (const VersionDictionary *file)
351 VersionDictionary::Iterator::~Iterator() {
355 bool VersionDictionary::Iterator::hasValue() const {return _iter && _iter->hasValue();}
356 Key VersionDictionary::Iterator::key() const {return _iter->key();}
357 Blob VersionDictionary::Iterator::value() const {return _iter->value();}
359 bool VersionDictionary::Iterator::next() {
368 bool VersionDictionary::Iterator::nextIndex() {
369 while (++_bucket < 256) {
370 const Index *index = _file->_index(_bucket);
372 _iter = new Index::Iterator(_file->_file, index);
385 #pragma mark CHANGE ITERATOR:
387 VersionDictionary::ChangeIterator* VersionDictionary::iterateChanges() const {
388 return new VersionDictionary::ChangeIterator(this);
391 VersionDictionary::ChangeIterator::ChangeIterator (const VersionDictionary *file)
399 VersionDictionary::ChangeIterator::~ChangeIterator() {
403 bool VersionDictionary::ChangeIterator::hasValue() const {return _iter && _iter->hasValue();}
404 Key VersionDictionary::ChangeIterator::key() const {return _iter->key();}
405 Blob VersionDictionary::ChangeIterator::value() const {return _iter->value();}
407 bool VersionDictionary::ChangeIterator::next() {
408 const VersionDictionary::Trailer *trailer = _file->_trailer();
410 // Check if current iterator has a value that's from this version:
411 if (_iter && _iter->hasValue()) {
412 if (((Index::Iterator*)_iter)->valuePosition() > trailer->previousTrailerPosition)
414 else if (_iter->next())
417 // If not, go to next Index:
423 bool VersionDictionary::ChangeIterator::nextIndex() {
425 const VersionDictionary::Trailer *trailer = _file->_trailer();
426 while (++_bucket < 256) {
427 const Index *index = _file->_index(_bucket);
429 // Skip indexes that weren't updated in this version:
430 if (trailer->indexPositions[_bucket] > trailer->previousTrailerPosition) {
431 _iter = new Index::Iterator(_file->_file, index);