jbm: it compiles... but bombs in unit tests
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"
20 # define UINT32_MAX (4294967295U)
25 // Trailer is stored in file; all numbers are little-endian.
26 class VersionDictionary::Trailer :public Chunk {
28 LittleEndian<uint32_t> magicNumber1;
29 LittleEndian<uint32_t> count;
30 VersionDictionary::IndexPositions indexPositions;
31 LittleEndian<FilePosition> previousTrailerPosition;
32 LittleEndian<uint32_t> generation;
33 LittleEndianDouble timestamp;
34 LittleEndian<uint32_t> magicNumber2;
37 :Chunk(sizeof(Trailer), kChunkType)
41 VersionDictionary::IndexPositions indexPos,
42 FilePosition previousTrailerPos,
44 :Chunk(sizeof(Trailer), kChunkType),
45 magicNumber1(kMagicNumber1),
47 indexPositions(indexPos),
48 previousTrailerPosition(previousTrailerPos),
50 timestamp(::time(NULL)),
51 magicNumber2(kMagicNumber2)
54 static const uint32_t kMagicNumber1 = 0xfe83b1cd;
55 static const uint32_t kMagicNumber2 = 0xff84b2c1;
60 VersionDictionary::VersionDictionary (File *file)
63 _previousTrailerPosition(0),
68 if (file->length() > sizeof(Trailer))
72 VersionDictionary::VersionDictionary (File *file, FilePosition trailerPosition)
74 _trailerPosition(trailerPosition),
75 _previousTrailerPosition(0),
80 _read(trailerPosition);
83 int VersionDictionary::count() const {
87 const VersionDictionary::Trailer* VersionDictionary::_trailer() const {
88 if (_trailerPosition > 0)
89 return (const VersionDictionary::Trailer*) _file->mappedPosition(_trailerPosition);
91 return NULL; // Only happens in the empty-file case
94 const Index* VersionDictionary::_index (int i) const {
95 if (_indexPositions[i] > 0)
96 return (const Index*) _file->mappedPosition(_indexPositions[i]);
101 int VersionDictionary::generation() const {
102 const VersionDictionary::Trailer *trailer = _trailer();
103 return trailer ? (int)trailer->generation : -1;
106 time_t VersionDictionary::timestamp() const {
107 const VersionDictionary::Trailer *trailer = _trailer();
108 return trailer ? (time_t)trailer->timestamp : 0;
111 const VersionDictionary* VersionDictionary::previousVersion() const {
112 if (!_previousVersion)
113 if (_previousTrailerPosition > 0)
114 _previousVersion = new VersionDictionary(_file, _previousTrailerPosition);
115 return _previousVersion;
119 Blob VersionDictionary::get (Key key) const {
120 const Index *index = _index(key.hash >> 24);
121 return index ?index->get(_file, key) :Blob();
124 void VersionDictionary::_read (FilePosition trailerPos) {
127 if (trailerPos > 0) {
128 _file->setPosition(trailerPos);
130 // Determine position of trailer, at EOF:
131 off_t pos = _file->setPositionToEnd(sizeof(VersionDictionary::Trailer));
132 if (pos < 0 || (pos & 0x03) || pos > UINT32_MAX)
133 throw File::Error(ERANGE, "No trailer found in file (wrong EOF)");
134 trailerPos = (FilePosition)pos;
137 // Read & verify trailer:
138 VersionDictionary::Trailer trailer;
139 _file->read(trailer);
140 _trailerPosition = trailerPos;
141 _previousTrailerPosition = trailer.previousTrailerPosition;
142 _count = trailer.count;
143 _indexPositions = trailer.indexPositions;
145 if (trailer.magicNumber1 != VersionDictionary::Trailer::kMagicNumber1
146 || trailer.magicNumber2 != VersionDictionary::Trailer::kMagicNumber2)
147 throw File::Error("No trailer found in file (invalid magic numbers)");
148 if (_previousTrailerPosition >= _trailerPosition)
149 throw File::Error("Bad VersionDictionary trailer (illegal previousTrailerPosition)");
152 _file->mapRegion(0, _trailerPosition+sizeof(trailer));
155 for (int i=0; i<256; i++) {
156 if (_indexPositions[i] > 0)
157 if (_indexPositions[i] < _previousTrailerPosition || _indexPositions[i] >= _trailerPosition)
158 throw File::Error("Bad VersionDictionary trailer (illegal index position)");
159 const Index *index = _index(i);
166 bool VersionDictionary::isLatestVersion() const {
167 return _file->length() <= _trailerPosition+sizeof(VersionDictionary::Trailer);
171 /* Append addDict to baseDict, writing the results into dstFile (which is usually the same
172 as baseDict->file().) If 'replace' is true, ignore the old contents of baseDict.
173 Returns the position of the new trailer. */
174 FilePosition VersionDictionary::_append (const VersionDictionary *baseDict,
175 const Dictionary *addDict,
179 File *srcFile = baseDict->_file;
180 bool incremental = !replace && dstFile==srcFile;
181 Index* newIndex[256];
184 // Work out the needed capacity for each Index bucket:
185 int newCounts[256] = {0};
187 for (int i=0; i<256; i++) {
188 const Index *oldIndex = baseDict->_index(i);
190 newCounts[i] = oldIndex->count();
193 Dictionary::Iterator *it = addDict->iterate();
195 newCounts[it->key().hash >> 24]++;
198 // Allocate new Indexes, of sufficient capacity, for each growing bucket:
199 for (int i=0; i<256; i++) {
200 const Index *oldIndex = baseDict->_index(i);
201 if (newCounts[i] && (!incremental || !oldIndex || newCounts[i] > oldIndex->count())) {
202 newIndex[i] = Index::create(newCounts[i]);
203 if (incremental && oldIndex)
204 newIndex[i]->copyFrom(srcFile, oldIndex);
210 // Lock the file now, seek to the end, and make sure it's been prepared with a header,
211 // since FilePositions of 0 and 1 are reserved.
212 File::Lock lock(dstFile);
213 const FilePosition startPos = (FilePosition) dstFile->setPositionToEnd();
215 throw File::Error(ERANGE, "Cannot write VersionDictionary to empty file");
217 // For safety's sake, make sure the old file hasn't been destroyed:
218 FilePosition oldEOF = baseDict->_trailerPosition;
220 oldEOF += sizeof(VersionDictionary::Trailer);
221 if (srcFile->length() < oldEOF)
222 throw File::Error(ERANGE, "File has been truncated since being read");
225 FilePosition pos = startPos;
230 else if (dstFile == srcFile)
231 newCount = baseDict->_count;
233 // Write out the surviving pre-existing entries from the old file:
235 for (VersionDictionary::Iterator it(baseDict); it; ++it) {
237 if (!addDict->contains(key)) {
238 int bucket = key.hash >> 24;
239 int size = KeyValueChunk::write(dstFile,pos, key, it.value());
240 newIndex[bucket]->put(srcFile, key, pos, startPos);
247 // Now write the items from the new dict:
248 Dictionary::Iterator *it = addDict->iterate();
251 Blob value=it->value();
252 int bucket = key.hash >> 24;
253 Index *index = newIndex[bucket];
257 int size = KeyValueChunk::write(dstFile,pos, key, value);
258 if (index->put(srcFile, key, pos, startPos))
261 } else if (incremental) {
262 // NULL value is a deleted-entry marker used by OverlayDictionary
263 if (index->remove(srcFile, key, startPos))
269 // Write out the new indexes:
270 IndexPositions newIndexPositions;
272 newIndexPositions = baseDict->_indexPositions;
274 memset(&newIndexPositions, 0, sizeof(newIndexPositions));
276 pos += Chunk::writePadding(dstFile);
277 for (int i=0; i<256; i++) {
279 Index *index = newIndex[i];
280 newIndexPositions[i] = pos;
281 pos += dstFile->write(index, index->size());
286 // Flush everything out to disk, with maximum paranoia, before writing the trailer.
287 // Since scavenging corrupt files looks for trailers, we don't want to append a
288 // trailer until we're certain that all of the real data is safely on-disk.
289 dstFile->flushDisk();
291 // Write the trailer:
292 FilePosition newTrailerPosition = pos;
293 VersionDictionary::Trailer trailer(newCount,
295 baseDict->_trailerPosition,
296 baseDict->generation() + 1);
297 pos += dstFile->write(trailer);
299 // Just a mild flush here; flushDisk() is very expensive and can be disruptive to
300 // real-time I/O in other apps, so it's bad to call it too often.
302 assert(pos==dstFile->position());
304 return newTrailerPosition;
307 // If something goes wrong, try to back out everything we wrote:
309 dstFile->setLength(startPos);
317 #pragma mark TESTING-ONLY:
319 VersionDictionary* VersionDictionary::_appendAndOpen (const Dictionary *addDict,
323 FilePosition nextVersionPos = _append(this, addDict, dstFile, replace);
324 VersionDictionary *nextVersion = new VersionDictionary(dstFile, nextVersionPos);
325 nextVersion->_previousVersion = this;
329 VersionDictionary* VersionDictionary::create (File *file, const Dictionary *srcDict) {
330 return VersionDictionary(file)._appendAndOpen(srcDict, file, true);
335 #pragma mark ITERATOR:
337 Dictionary::Iterator* VersionDictionary::iterate() const {
338 return new VersionDictionary::Iterator(this);
341 VersionDictionary::Iterator::Iterator (const VersionDictionary *file)
349 VersionDictionary::Iterator::~Iterator() {
353 bool VersionDictionary::Iterator::hasValue() const {return _iter && _iter->hasValue();}
354 Key VersionDictionary::Iterator::key() const {return _iter->key();}
355 Blob VersionDictionary::Iterator::value() const {return _iter->value();}
357 bool VersionDictionary::Iterator::next() {
366 bool VersionDictionary::Iterator::nextIndex() {
367 while (++_bucket < 256) {
368 const Index *index = _file->_index(_bucket);
370 _iter = new Index::Iterator(_file->_file, index);
383 #pragma mark CHANGE ITERATOR:
385 VersionDictionary::ChangeIterator* VersionDictionary::iterateChanges() const {
386 return new VersionDictionary::ChangeIterator(this);
389 VersionDictionary::ChangeIterator::ChangeIterator (const VersionDictionary *file)
397 VersionDictionary::ChangeIterator::~ChangeIterator() {
401 bool VersionDictionary::ChangeIterator::hasValue() const {return _iter && _iter->hasValue();}
402 Key VersionDictionary::ChangeIterator::key() const {return _iter->key();}
403 Blob VersionDictionary::ChangeIterator::value() const {return _iter->value();}
405 bool VersionDictionary::ChangeIterator::next() {
406 const VersionDictionary::Trailer *trailer = _file->_trailer();
408 // Check if current iterator has a value that's from this version:
409 if (_iter && _iter->hasValue()) {
410 if (((Index::Iterator*)_iter)->valuePosition() > trailer->previousTrailerPosition)
412 else if (_iter->next())
415 // If not, go to next Index:
421 bool VersionDictionary::ChangeIterator::nextIndex() {
423 const VersionDictionary::Trailer *trailer = _file->_trailer();
424 while (++_bucket < 256) {
425 const Index *index = _file->_index(_bucket);
427 // Skip indexes that weren't updated in this version:
428 if (trailer->indexPositions[_bucket] > trailer->previousTrailerPosition) {
429 _iter = new Index::Iterator(_file->_file, index);