* 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.
5 * Created by Jens Alfke on 8/31/09.
6 * Copyright 2009 Jens Alfke. All rights reserved.
7 * BSD-Licensed: See the file "LICENSE.txt" for details.
11 #include "VersionDictionary.h"
21 class DictionaryFileHeader :public Chunk {
23 DictionaryFileHeader()
24 :Chunk(sizeof(DictionaryFileHeader),kChunkType)
26 memcpy(&magicNumber, &kMagicNumber, sizeof(magicNumber));
30 return type()==kChunkType && memcmp(&magicNumber,&kMagicNumber,sizeof(magicNumber))==0;
33 uint8_t magicNumber[8];
35 static const uint16_t kChunkType = 4;
36 static const uint8_t kMagicNumber[8];
39 const uint8_t DictionaryFileHeader::kMagicNumber[8] = {0x4A, 0x54, 0xF1, 0x1E, 'h', 'A', 's', 'H'};
48 _current( new OverlayDictionary(&Dictionary::kEmpty) )
52 Ottoman::Ottoman (const char *filename, bool writeable)
53 :_writeable(writeable),
54 _filename(strdup(filename)),
58 _file = new File(_filename, writeable ?O_RDWR :O_RDONLY);
59 _lastVersion = new VersionDictionary(_file);
61 _current = new OverlayDictionary(_lastVersion);
72 bool Ottoman::needsSync() const {
73 return _file && (!_lastVersion->isLatestVersion() || !_file->hasPath(_filename));
76 bool Ottoman::sync() {
79 File *curFile = _file;
80 if (!curFile->hasPath(_filename))
81 curFile = new File(_filename, _writeable ?O_RDWR :O_RDONLY);
83 VersionDictionary *newest = new VersionDictionary(curFile);
84 bool changed = (newest->generation() != _lastVersion->generation());
86 if (newest->generation() < _lastVersion->generation())
87 throw File::Error("Versions got lost in file update");
90 _current->rebase(newest);
93 _lastVersion = newest;
96 if (_file != curFile) {
107 ChunkIterator* Ottoman::chunkIterator() const {
108 return _file ?new ChunkIterator(_file, sizeof(DictionaryFileHeader)) :NULL;
111 bool Ottoman::scavenge (bool repair) {
115 const off_t actualEOF = _file->length();
116 fprintf(stderr, "Ottoman::scavenge: Scanning %s (%llu / 0x%llX bytes) ...\n",
117 _filename, actualEOF, actualEOF);
118 _file->setPosition(0);
119 ChunkIterator it(_file, 0);
120 const DictionaryFileHeader *header = (const DictionaryFileHeader *) it.chunk();
121 if (!header || !header->valid()) {
122 fprintf(stderr, "Ottoman::scavenge: No valid file header at all!\n");
126 // Scan through all the chunks:
127 FilePosition lastValidTrailer = 0;
128 FilePosition validEOF = 0;
133 const Chunk *chunk = it.chunk();
135 uint16_t type = it.chunk()->type();
136 if (type != lastType) {
138 printf("%6d\n", count);
139 fprintf(stderr, "Ottoman::scavenge: at 0x%08X: type %u ... ",
140 it.position(), type);
147 case KeyValueChunk::kChunkType:
148 ((const KeyValueChunk*)chunk)->validate();
150 case Index::kChunkType:
151 ((const Index*)chunk)->validateEntries(_file);
153 case VersionDictionary::kChunkType: {
154 fprintf(stderr, "Found dictionary trailer at %u!\n", it.position());
156 // Validate by instantiating the VersionDictionary:
157 VersionDictionary tempDict(_file, it.position());
158 // If constructor succeeded, it's valid, so remember it:
159 lastValidTrailer = it.position();
160 validEOF = lastValidTrailer + chunk->size();
161 fprintf(stderr, "Ottoman::scavenge: Valid dictionary trailer at %X--%X\n",
162 lastValidTrailer, validEOF);
168 fprintf(stderr, "%6d\n", count);
169 } catch (File::Error &err) {
170 fprintf(stderr, "Ottoman::scavenge caught File::Error(%i,\"%s\") at pos %llX\n",
171 err.code, err.message, _file->position());
172 // Keep going; we can recover the dictionary(ies) before the bad one.
175 if (lastValidTrailer == 0) {
176 fprintf(stderr, "Ottoman::scavenge: No valid dictionaries found!\n");
178 } else if (validEOF < actualEOF) {
179 fprintf(stderr, "Ottoman::scavenge: Need to truncate to 0x%X (0x%llX bytes)\n",
180 lastValidTrailer, actualEOF-lastValidTrailer);
182 _file->setLength(validEOF);
188 fprintf(stderr, "Ottoman::scavenge: File is OK!\n");
197 // low-level write. Does not lock or check for conflicts!
198 void Ottoman::_append (File *dstFile) {
199 VersionDictionary *lastVersion = _lastVersion;
201 lastVersion = new VersionDictionary(dstFile);
202 VersionDictionary *saved = lastVersion->_appendAndOpen(_current->overlay(),
204 _current->baseReplaced());
205 // (don't delete _lastVersion: saved->_previousVersion now points to it.)
206 _lastVersion = saved;
207 _current->revertTo(_lastVersion);
211 bool Ottoman::save() {
214 if (_current && _current->isChanged()) {
215 File::Lock lock(_file);
217 return false; // conflict!
223 File* Ottoman::_writeTo (const char *dstFileName, bool overwriteAllowed) {
224 int mode = O_RDWR | O_CREAT | O_TRUNC;
225 if (!overwriteAllowed)
227 File *dstFile = new File(dstFileName, mode, true);
229 dstFile->write(DictionaryFileHeader());
233 File::unlink(dstFileName);
239 void Ottoman::saveAs (const char *dstFileName, bool overwriteAllowed) {
240 File *dstFile = _writeTo(dstFileName, overwriteAllowed);
242 _filename = strdup(dstFileName);
247 bool Ottoman::saveAndCompact() {
250 char tempFileName[1024];
251 #ifdef _DARWIN_C_SOURCE
252 ::strlcpy(tempFileName, _filename, sizeof(tempFileName)-1);
253 ::strlcat(tempFileName, "~", sizeof(tempFileName));
255 ::strncpy(tempFileName, _filename, sizeof(tempFileName)-1);
256 ::strncat(tempFileName, "~", sizeof(tempFileName));
260 // Check for conflict in existing file:
261 File::Lock lock(_file);
264 tempFile = _writeTo(tempFileName, false);
265 File::rename(tempFileName, _filename);