First official checkin.
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)");
133 // Read & verify trailer:
134 VersionDictionary::Trailer trailer;
135 _file->read(trailer);
136 _trailerPosition = trailerPos;
137 _count = trailer.count;
138 _indexPositions = trailer.indexPositions;
140 if (trailer.magicNumber1 != VersionDictionary::Trailer::kMagicNumber1
141 || trailer.magicNumber2 != VersionDictionary::Trailer::kMagicNumber2)
142 throw File::Error(ERANGE, "No trailer found in file (invalid magic numbers)");\
146 _file->mapRegion(0, _trailerPosition+sizeof(trailer));
149 for (int i=0; i<256; i++) {
150 const Index *index = _index(i);
157 bool VersionDictionary::isLatestVersion() const {
158 return _file->length() <= _trailerPosition+sizeof(VersionDictionary::Trailer);
162 /* Append addDict to baseDict, writing the results into dstFile (which is usually the same
163 as baseDict->file().) If 'replace' is true, ignore the old contents of baseDict.
164 Returns the position of the new trailer. */
165 FilePosition VersionDictionary::_append (const VersionDictionary *baseDict,
166 const Dictionary *addDict,
170 File *srcFile = baseDict->_file;
171 bool incremental = !replace && dstFile==srcFile;
172 Index* newIndex[256];
175 // Work out the needed capacity for each Index bucket:
176 int newCounts[256] = {0};
178 for (int i=0; i<256; i++) {
179 const Index *oldIndex = baseDict->_index(i);
181 newCounts[i] = oldIndex->count();
184 Dictionary::Iterator *it = addDict->iterate();
186 newCounts[it->key().hash >> 24]++;
189 // Allocate new Indexes, of sufficient capacity, for each growing bucket:
190 for (int i=0; i<256; i++) {
191 const Index *oldIndex = baseDict->_index(i);
192 if (newCounts[i] && (!incremental || !oldIndex || newCounts[i] > oldIndex->count())) {
193 newIndex[i] = Index::create(newCounts[i]);
194 if (incremental && oldIndex)
195 newIndex[i]->copyFrom(srcFile, oldIndex);
201 // Lock the file now, seek to the end, and make sure it's been prepared with a header,
202 // since FilePositions of 0 and 1 are reserved.
203 File::Lock lock(dstFile);
204 const FilePosition startPos = dstFile->setPositionToEnd();
206 throw File::Error(ERANGE, "Cannot write VersionDictionary to empty file");
208 // For safety's sake, make sure the old file hasn't been destroyed:
209 FilePosition oldEOF = baseDict->_trailerPosition;
211 oldEOF += sizeof(VersionDictionary::Trailer);
212 if (srcFile->length() < oldEOF)
213 throw File::Error(ERANGE, "File has been truncated since being read");
216 FilePosition pos = startPos;
221 else if (dstFile == srcFile)
222 newCount = baseDict->_count;
224 // Write out the surviving pre-existing entries from the old file:
226 for (VersionDictionary::Iterator it(baseDict); it; ++it) {
228 if (!addDict->contains(key)) {
229 int bucket = key.hash >> 24;
230 int size = KeyValueChunk::write(dstFile,pos, key, it.value());
231 newIndex[bucket]->put(srcFile, key, pos, startPos);
238 // Now write the items from the new dict:
239 Dictionary::Iterator *it = addDict->iterate();
242 Blob value=it->value();
243 int bucket = key.hash >> 24;
244 Index *index = newIndex[bucket];
248 int size = KeyValueChunk::write(dstFile,pos, key, value);
249 if (index->put(srcFile, key, pos, startPos))
252 } else if (incremental) {
253 // NULL value is a deleted-entry marker used by OverlayDictionary
254 if (index->remove(srcFile, key, startPos))
260 // Write out the new indexes:
261 IndexPositions newIndexPositions;
263 newIndexPositions = baseDict->_indexPositions;
265 memset(&newIndexPositions, 0, sizeof(newIndexPositions));
267 pos += Chunk::writePadding(dstFile);
268 for (int i=0; i<256; i++) {
270 Index *index = newIndex[i];
271 newIndexPositions[i] = pos;
272 pos += dstFile->write(index, index->size());
277 // Flush everything out to disk, with maximum paranoia, before writing the trailer.
278 // Since scavenging corrupt files looks for trailers, we don't want to append a
279 // trailer until we're certain that all of the real data is safely on-disk.
280 dstFile->flushDisk();
282 // Write the trailer:
283 FilePosition newTrailerPosition = pos;
284 VersionDictionary::Trailer trailer(newCount,
286 baseDict->_trailerPosition,
287 baseDict->generation() + 1);
288 pos += dstFile->write(trailer);
290 // Just a mild flush here; flushDisk() is very expensive and can be disruptive to
291 // real-time I/O in other apps, so it's bad to call it too often.
293 assert(pos==dstFile->position());
295 return newTrailerPosition;
298 // If something goes wrong, try to back out everything we wrote:
300 dstFile->setLength(startPos);
308 #pragma mark TESTING-ONLY:
310 VersionDictionary* VersionDictionary::_appendAndOpen (const Dictionary *addDict,
314 FilePosition nextVersionPos = _append(this, addDict, dstFile, replace);
315 VersionDictionary *nextVersion = new VersionDictionary(dstFile, nextVersionPos);
316 nextVersion->_previousVersion = this;
320 VersionDictionary* VersionDictionary::create (File *file, const Dictionary *srcDict) {
321 return VersionDictionary(file)._appendAndOpen(srcDict, file, true);
326 #pragma mark ITERATOR:
328 Dictionary::Iterator* VersionDictionary::iterate() const {
329 return new VersionDictionary::Iterator(this);
332 VersionDictionary::Iterator::Iterator (const VersionDictionary *file)
340 VersionDictionary::Iterator::~Iterator() {
344 bool VersionDictionary::Iterator::hasValue() const {return _iter && _iter->hasValue();}
345 Key VersionDictionary::Iterator::key() const {return _iter->key();}
346 Blob VersionDictionary::Iterator::value() const {return _iter->value();}
348 bool VersionDictionary::Iterator::next() {
357 bool VersionDictionary::Iterator::nextIndex() {
358 while (++_bucket < 256) {
359 const Index *index = _file->_index(_bucket);
361 _iter = new Index::Iterator(_file->_file, index);
374 #pragma mark CHANGE ITERATOR:
376 VersionDictionary::ChangeIterator* VersionDictionary::iterateChanges() const {
377 return new VersionDictionary::ChangeIterator(this);
380 VersionDictionary::ChangeIterator::ChangeIterator (const VersionDictionary *file)
388 VersionDictionary::ChangeIterator::~ChangeIterator() {
392 bool VersionDictionary::ChangeIterator::hasValue() const {return _iter && _iter->hasValue();}
393 Key VersionDictionary::ChangeIterator::key() const {return _iter->key();}
394 Blob VersionDictionary::ChangeIterator::value() const {return _iter->value();}
396 bool VersionDictionary::ChangeIterator::next() {
397 const VersionDictionary::Trailer *trailer = _file->_trailer();
399 // Check if current iterator has a value that's from this version:
400 if (_iter && _iter->hasValue()) {
401 if (((Index::Iterator*)_iter)->valuePosition() > trailer->previousTrailerPosition)
403 else if (_iter->next())
406 // If not, go to next Index:
412 bool VersionDictionary::ChangeIterator::nextIndex() {
414 const VersionDictionary::Trailer *trailer = _file->_trailer();
415 while (++_bucket < 256) {
416 const Index *index = _file->_index(_bucket);
418 // Skip indexes that weren't updated in this version:
419 if (trailer->indexPositions[_bucket] > trailer->previousTrailerPosition) {
420 _iter = new Index::Iterator(_file->_file, index);