jbm: it compiles... but bombs in unit tests
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'};
47 _current( new OverlayDictionary(&Dictionary::kEmpty) )
51 Ottoman::Ottoman (const char *filename, bool writeable)
52 :_writeable(writeable),
53 _filename(strdup(filename)),
57 _file = new File(_filename, writeable ?O_RDWR :O_RDONLY);
58 _lastVersion = new VersionDictionary(_file);
60 _current = new OverlayDictionary(_lastVersion);
71 bool Ottoman::needsSync() const {
72 return _file && (!_lastVersion->isLatestVersion() || !_file->hasPath(_filename));
75 bool Ottoman::sync() {
78 File *curFile = _file;
79 if (!curFile->hasPath(_filename))
80 curFile = new File(_filename, _writeable ?O_RDWR :O_RDONLY);
82 VersionDictionary *newest = new VersionDictionary(curFile);
83 bool changed = (newest->generation() != _lastVersion->generation());
85 if (newest->generation() < _lastVersion->generation())
86 throw File::Error("Versions got lost in file update");
89 _current->rebase(newest);
92 _lastVersion = newest;
95 if (_file != curFile) {
103 ChunkIterator* Ottoman::chunkIterator() const {
104 return _file ?new ChunkIterator(_file, sizeof(DictionaryFileHeader)) :NULL;
107 bool Ottoman::scavenge (bool repair) {
111 const off_t actualEOF = _file->length();
112 fprintf(stderr, "Ottoman::scavenge: Scanning %s (%llu / 0x%llX bytes) ...\n",
113 _filename, actualEOF, actualEOF);
114 _file->setPosition(0);
115 ChunkIterator it(_file, 0);
116 const DictionaryFileHeader *header = (const DictionaryFileHeader *) it.chunk();
117 if (!header || !header->valid()) {
118 fprintf(stderr, "Ottoman::scavenge: No valid file header at all!\n");
122 // Scan through all the chunks:
123 FilePosition lastValidTrailer = 0;
124 FilePosition validEOF = 0;
129 const Chunk *chunk = it.chunk();
131 uint16_t type = it.chunk()->type();
132 if (type != lastType) {
134 printf("%6d\n", count);
135 fprintf(stderr, "Ottoman::scavenge: at 0x%08X: type %u ... ",
136 it.position(), type);
143 case KeyValueChunk::kChunkType:
144 ((const KeyValueChunk*)chunk)->validate();
146 case Index::kChunkType:
147 ((const Index*)chunk)->validateEntries(_file);
149 case VersionDictionary::kChunkType: {
150 fprintf(stderr, "Found dictionary trailer at %u!\n", it.position());
152 // Validate by instantiating the VersionDictionary:
153 VersionDictionary tempDict(_file, it.position());
154 // If constructor succeeded, it's valid, so remember it:
155 lastValidTrailer = it.position();
156 validEOF = lastValidTrailer + chunk->size();
157 fprintf(stderr, "Ottoman::scavenge: Valid dictionary trailer at %X--%X\n",
158 lastValidTrailer, validEOF);
164 fprintf(stderr, "%6d\n", count);
165 } catch (File::Error &err) {
166 fprintf(stderr, "Ottoman::scavenge caught File::Error(%i,\"%s\") at pos %llX\n",
167 err.code, err.message, _file->position());
168 // Keep going; we can recover the dictionary(ies) before the bad one.
171 if (lastValidTrailer == 0) {
172 fprintf(stderr, "Ottoman::scavenge: No valid dictionaries found!\n");
174 } else if (validEOF < actualEOF) {
175 fprintf(stderr, "Ottoman::scavenge: Need to truncate to 0x%X (0x%llX bytes)\n",
176 lastValidTrailer, actualEOF-lastValidTrailer);
178 _file->setLength(validEOF);
184 fprintf(stderr, "Ottoman::scavenge: File is OK!\n");
193 // low-level write. Does not lock or check for conflicts!
194 void Ottoman::_append (File *dstFile) {
195 VersionDictionary *lastVersion = _lastVersion;
197 lastVersion = new VersionDictionary(dstFile);
198 VersionDictionary *saved = lastVersion->_appendAndOpen(_current->overlay(),
200 _current->baseReplaced());
201 // (don't delete _lastVersion: saved->_previousVersion now points to it.)
202 _lastVersion = saved;
203 _current->revertTo(_lastVersion);
206 bool Ottoman::save() {
209 if (_current && _current->isChanged()) {
210 File::Lock lock(_file);
212 return false; // conflict!
218 File* Ottoman::_writeTo (const char *dstFileName, bool overwriteAllowed) {
219 int mode = O_RDWR | O_CREAT | O_TRUNC;
220 if (!overwriteAllowed)
222 File *dstFile = new File(dstFileName, mode, true);
224 dstFile->write(DictionaryFileHeader());
228 File::unlink(dstFileName);
234 void Ottoman::saveAs (const char *dstFileName, bool overwriteAllowed) {
235 File *dstFile = _writeTo(dstFileName, overwriteAllowed);
237 _filename = strdup(dstFileName);
241 bool Ottoman::saveAndCompact() {
244 char tempFileName[1024];
245 ::strncpy(tempFileName, _filename, sizeof(tempFileName)-1);
246 ::strncat(tempFileName, "~", sizeof(tempFileName));
249 // Check for conflict in existing file:
250 File::Lock lock(_file);
253 tempFile = _writeTo(tempFileName, false);
254 File::rename(tempFileName, _filename);