Oops, forgot to set _previousTrailerPosition, which made the previousVersion() accessor not work. Fixed. Also added some sanity-checking of its value.
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"
21 // Trailer is stored in file; all numbers are little-endian.
22 class VersionDictionary::Trailer :public Chunk {
24 LittleEndian<uint32_t> magicNumber1;
25 LittleEndian<uint32_t> count;
26 VersionDictionary::IndexPositions indexPositions;
27 LittleEndian<FilePosition> previousTrailerPosition;
28 LittleEndian<uint32_t> generation;
29 LittleEndianDouble timestamp;
30 LittleEndian<uint32_t> magicNumber2;
33 :Chunk(sizeof(Trailer), kChunkType)
37 VersionDictionary::IndexPositions indexPos,
38 FilePosition previousTrailerPos,
40 :Chunk(sizeof(Trailer), kChunkType),
41 magicNumber1(kMagicNumber1),
43 indexPositions(indexPos),
44 previousTrailerPosition(previousTrailerPos),
46 timestamp(::time(NULL)),
47 magicNumber2(kMagicNumber2)
50 static const uint32_t kMagicNumber1 = 0xfe83b1cd;
51 static const uint32_t kMagicNumber2 = 0xff84b2c1;
56 VersionDictionary::VersionDictionary (File *file)
59 _previousTrailerPosition(0),
64 if (file->length() > sizeof(Trailer))
68 VersionDictionary::VersionDictionary (File *file, FilePosition trailerPosition)
70 _trailerPosition(trailerPosition),
71 _previousTrailerPosition(0),
76 _read(trailerPosition);
79 int VersionDictionary::count() const {
83 const VersionDictionary::Trailer* VersionDictionary::_trailer() const {
84 if (_trailerPosition > 0)
85 return (const VersionDictionary::Trailer*) _file->mappedPosition(_trailerPosition);
87 return NULL; // Only happens in the empty-file case
90 const Index* VersionDictionary::_index (int i) const {
91 if (_indexPositions[i] > 0)
92 return (const Index*) _file->mappedPosition(_indexPositions[i]);
97 int VersionDictionary::generation() const {
98 const VersionDictionary::Trailer *trailer = _trailer();
99 return trailer ? (int)trailer->generation : -1;
102 time_t VersionDictionary::timestamp() const {
103 const VersionDictionary::Trailer *trailer = _trailer();
104 return trailer ? (time_t)trailer->timestamp : 0;
107 const VersionDictionary* VersionDictionary::previousVersion() const {
108 if (!_previousVersion)
109 if (_previousTrailerPosition > 0)
110 _previousVersion = new VersionDictionary(_file, _previousTrailerPosition);
111 return _previousVersion;
115 Blob VersionDictionary::get (Key key) const {
116 const Index *index = _index(key.hash >> 24);
117 return index ?index->get(_file, key) :Blob();
120 void VersionDictionary::_read (FilePosition trailerPos) {
123 if (trailerPos > 0) {
124 _file->setPosition(trailerPos);
126 // Determine position of trailer, at EOF:
127 off_t pos = _file->setPositionToEnd(sizeof(VersionDictionary::Trailer));
128 if (pos < 0 || (pos & 0x03) || pos > UINT32_MAX)
129 throw File::Error(ERANGE, "No trailer found in file (wrong EOF)");
130 trailerPos = (FilePosition)pos;
133 // Read & verify trailer:
134 VersionDictionary::Trailer trailer;
135 _file->read(trailer);
136 _trailerPosition = trailerPos;
137 _previousTrailerPosition = trailer.previousTrailerPosition;
138 _count = trailer.count;
139 _indexPositions = trailer.indexPositions;
141 if (trailer.magicNumber1 != VersionDictionary::Trailer::kMagicNumber1
142 || trailer.magicNumber2 != VersionDictionary::Trailer::kMagicNumber2)
143 throw File::Error("No trailer found in file (invalid magic numbers)");
144 if (_previousTrailerPosition >= _trailerPosition)
145 throw File::Error("Bad VersionDictionary trailer (illegal previousTrailerPosition)");
148 _file->mapRegion(0, _trailerPosition+sizeof(trailer));
151 for (int i=0; i<256; i++) {
152 if (_indexPositions[i] > 0)
153 if (_indexPositions[i] < _previousTrailerPosition || _indexPositions[i] >= _trailerPosition)
154 throw File::Error("Bad VersionDictionary trailer (illegal index position)");
155 const Index *index = _index(i);
162 bool VersionDictionary::isLatestVersion() const {
163 return _file->length() <= _trailerPosition+sizeof(VersionDictionary::Trailer);
167 /* Append addDict to baseDict, writing the results into dstFile (which is usually the same
168 as baseDict->file().) If 'replace' is true, ignore the old contents of baseDict.
169 Returns the position of the new trailer. */
170 FilePosition VersionDictionary::_append (const VersionDictionary *baseDict,
171 const Dictionary *addDict,
175 File *srcFile = baseDict->_file;
176 bool incremental = !replace && dstFile==srcFile;
177 Index* newIndex[256];
180 // Work out the needed capacity for each Index bucket:
181 int newCounts[256] = {0};
183 for (int i=0; i<256; i++) {
184 const Index *oldIndex = baseDict->_index(i);
186 newCounts[i] = oldIndex->count();
189 Dictionary::Iterator *it = addDict->iterate();
191 newCounts[it->key().hash >> 24]++;
194 // Allocate new Indexes, of sufficient capacity, for each growing bucket:
195 for (int i=0; i<256; i++) {
196 const Index *oldIndex = baseDict->_index(i);
197 if (newCounts[i] && (!incremental || !oldIndex || newCounts[i] > oldIndex->count())) {
198 newIndex[i] = Index::create(newCounts[i]);
199 if (incremental && oldIndex)
200 newIndex[i]->copyFrom(srcFile, oldIndex);
206 // Lock the file now, seek to the end, and make sure it's been prepared with a header,
207 // since FilePositions of 0 and 1 are reserved.
208 File::Lock lock(dstFile);
209 const FilePosition startPos = (FilePosition) dstFile->setPositionToEnd();
211 throw File::Error(ERANGE, "Cannot write VersionDictionary to empty file");
213 // For safety's sake, make sure the old file hasn't been destroyed:
214 FilePosition oldEOF = baseDict->_trailerPosition;
216 oldEOF += sizeof(VersionDictionary::Trailer);
217 if (srcFile->length() < oldEOF)
218 throw File::Error(ERANGE, "File has been truncated since being read");
221 FilePosition pos = startPos;
226 else if (dstFile == srcFile)
227 newCount = baseDict->_count;
229 // Write out the surviving pre-existing entries from the old file:
231 for (VersionDictionary::Iterator it(baseDict); it; ++it) {
233 if (!addDict->contains(key)) {
234 int bucket = key.hash >> 24;
235 int size = KeyValueChunk::write(dstFile,pos, key, it.value());
236 newIndex[bucket]->put(srcFile, key, pos, startPos);
243 // Now write the items from the new dict:
244 Dictionary::Iterator *it = addDict->iterate();
247 Blob value=it->value();
248 int bucket = key.hash >> 24;
249 Index *index = newIndex[bucket];
253 int size = KeyValueChunk::write(dstFile,pos, key, value);
254 if (index->put(srcFile, key, pos, startPos))
257 } else if (incremental) {
258 // NULL value is a deleted-entry marker used by OverlayDictionary
259 if (index->remove(srcFile, key, startPos))
265 // Write out the new indexes:
266 IndexPositions newIndexPositions;
268 newIndexPositions = baseDict->_indexPositions;
270 memset(&newIndexPositions, 0, sizeof(newIndexPositions));
272 pos += Chunk::writePadding(dstFile);
273 for (int i=0; i<256; i++) {
275 Index *index = newIndex[i];
276 newIndexPositions[i] = pos;
277 pos += dstFile->write(index, index->size());
282 // Flush everything out to disk, with maximum paranoia, before writing the trailer.
283 // Since scavenging corrupt files looks for trailers, we don't want to append a
284 // trailer until we're certain that all of the real data is safely on-disk.
285 dstFile->flushDisk();
287 // Write the trailer:
288 FilePosition newTrailerPosition = pos;
289 VersionDictionary::Trailer trailer(newCount,
291 baseDict->_trailerPosition,
292 baseDict->generation() + 1);
293 pos += dstFile->write(trailer);
295 // Just a mild flush here; flushDisk() is very expensive and can be disruptive to
296 // real-time I/O in other apps, so it's bad to call it too often.
298 assert(pos==dstFile->position());
300 return newTrailerPosition;
303 // If something goes wrong, try to back out everything we wrote:
305 dstFile->setLength(startPos);
313 #pragma mark TESTING-ONLY:
315 VersionDictionary* VersionDictionary::_appendAndOpen (const Dictionary *addDict,
319 FilePosition nextVersionPos = _append(this, addDict, dstFile, replace);
320 VersionDictionary *nextVersion = new VersionDictionary(dstFile, nextVersionPos);
321 nextVersion->_previousVersion = this;
325 VersionDictionary* VersionDictionary::create (File *file, const Dictionary *srcDict) {
326 return VersionDictionary(file)._appendAndOpen(srcDict, file, true);
331 #pragma mark ITERATOR:
333 Dictionary::Iterator* VersionDictionary::iterate() const {
334 return new VersionDictionary::Iterator(this);
337 VersionDictionary::Iterator::Iterator (const VersionDictionary *file)
345 VersionDictionary::Iterator::~Iterator() {
349 bool VersionDictionary::Iterator::hasValue() const {return _iter && _iter->hasValue();}
350 Key VersionDictionary::Iterator::key() const {return _iter->key();}
351 Blob VersionDictionary::Iterator::value() const {return _iter->value();}
353 bool VersionDictionary::Iterator::next() {
362 bool VersionDictionary::Iterator::nextIndex() {
363 while (++_bucket < 256) {
364 const Index *index = _file->_index(_bucket);
366 _iter = new Index::Iterator(_file->_file, index);
379 #pragma mark CHANGE ITERATOR:
381 VersionDictionary::ChangeIterator* VersionDictionary::iterateChanges() const {
382 return new VersionDictionary::ChangeIterator(this);
385 VersionDictionary::ChangeIterator::ChangeIterator (const VersionDictionary *file)
393 VersionDictionary::ChangeIterator::~ChangeIterator() {
397 bool VersionDictionary::ChangeIterator::hasValue() const {return _iter && _iter->hasValue();}
398 Key VersionDictionary::ChangeIterator::key() const {return _iter->key();}
399 Blob VersionDictionary::ChangeIterator::value() const {return _iter->value();}
401 bool VersionDictionary::ChangeIterator::next() {
402 const VersionDictionary::Trailer *trailer = _file->_trailer();
404 // Check if current iterator has a value that's from this version:
405 if (_iter && _iter->hasValue()) {
406 if (((Index::Iterator*)_iter)->valuePosition() > trailer->previousTrailerPosition)
408 else if (_iter->next())
411 // If not, go to next Index:
417 bool VersionDictionary::ChangeIterator::nextIndex() {
419 const VersionDictionary::Trailer *trailer = _file->_trailer();
420 while (++_bucket < 256) {
421 const Index *index = _file->_index(_bucket);
423 // Skip indexes that weren't updated in this version:
424 if (trailer->indexPositions[_bucket] > trailer->previousTrailerPosition) {
425 _iter = new Index::Iterator(_file->_file, index);