/// <summary> /// Add a key/data pair to the database. /// </summary> /// <param name="key">The key.</param> /// <param name="data">The data.</param> public void Add(byte[] key, byte[] data) { if (_file == null) { throw new ObjectDisposedException(GetType().Name); } var bytes = new byte[8]; Cdb.PackInt32((UInt32)key.Length, bytes, 0); Cdb.PackInt32((UInt32)data.Length, bytes, 4); // Write the record: keylen, datalen, key, data. _file.Write(bytes, 0, bytes.Length); _file.Write(key, 0, key.Length); _file.Write(data, 0, data.Length); // Add hash and offset to the in-memory list: UInt32 hash = Cdb.Hash(key); _hashInfo.Add(new HashPos(hash, _pos)); _hashSize[hash & 255] += 1; // one more entry // Update the file position pointer: AdvancePos(8); // key and data length AdvancePos((UInt32)key.Length); AdvancePos((UInt32)data.Length); }
private int _loop; // num of hash slots searched under current key /// <summary> /// Open a constant database from the given file. /// </summary> /// <param name="filePath">Path to the CDB file.</param> public CdbFile(string filePath) { _cdbFile = File.OpenRead(filePath); _heads = new UInt32[256 * 2]; var bytes = new byte[2048]; CdbRead(bytes, bytes.Length); for (int i = 0, offset = 0; i < 256; i++) { UInt32 pos = Cdb.UnpackInt32(bytes, offset); offset += 4; UInt32 len = Cdb.UnpackInt32(bytes, offset); offset += 4; _heads[i << 1] = pos; _heads[(i << 1) + 1] = len; } _loop = 0; }
/// <summary> /// Find the next record stored under the given <paramref name="key"/>. /// </summary> /// <returns>The next record stored under the given key, /// or <c>null</c> if no more records can be found.</returns> public byte[] FindNext(byte[] key) { // We change object state in here, so nobody must intervene! lock (_syncLock) { if (_cdbFile == null) { throw new ObjectDisposedException(GetType().Name); } // Initialize: locate hash table and entry in it: if (_loop == 0) { UInt32 u = Cdb.Hash(key); // Get hash table len&pos for this key: UInt32 slot = u & 255; _hslots = _heads[(slot << 1) + 1]; if (_hslots == 0) { return(null); } _hpos = _heads[slot << 1]; // Remember this key's hash: _khash = u; // Locate the slot in the table at _hpos: u >>= 8; u %= _hslots; u <<= 3; _kpos = _hpos + u; } // Search: iterate all hash slots for the given key: var bytes = new byte[8]; while (_loop < _hslots) { // Read the entry for this key from the hash slot CdbRead(bytes, 8, _kpos); UInt32 h = Cdb.UnpackInt32(bytes, 0); UInt32 pos = Cdb.UnpackInt32(bytes, 4); if (pos == 0) { break; } // Advance loop counter and key position. // Wrap key position if at end of hash table. _loop += 1; _kpos += 8; if (_kpos == (_hpos + (_hslots << 3))) { _kpos = _hpos; } // Different hash? Probe next slot... if (h != _khash) { continue; } // Jump to the record and see if key matches: CdbRead(bytes, 8, pos); UInt32 klen = Cdb.UnpackInt32(bytes, 0); if (klen != key.Length) { continue; } if (!CdbMatch(key)) { continue; } UInt32 dlen = Cdb.UnpackInt32(bytes, 4); // Keys match: fetch the data var data = new byte[dlen]; CdbRead(data, data.Length); return(data); } return(null); // No such key } }
private void Finish() { // Find the start of each hash table var tableStart = new UInt32[256]; UInt32 start = 0; for (int i = 0; i < 256; i++) { start += _hashSize[i]; tableStart[i] = start; } // Create a new hash info table in order by hash table var table = new HashPos[_hashInfo.Count]; foreach (var hp in _hashInfo) { table[--tableStart[hp.Hash & 255]] = hp; } var eightBytes = new byte[8]; var fixedTable = new byte[2048]; // Append each of the hash tables to the end of the file. // Along the way, build the fixed table (to be written later). for (int i = 0; i < 256; i++) { UInt32 len = _hashSize[i] * 2; // len of i-th hash table // Remember pos and len of i-th hash table in fixed table: Cdb.PackInt32(_pos, fixedTable, i * 8); Cdb.PackInt32(len, fixedTable, i * 8 + 4); // Build the i-th hash table: UInt32 tableIndex = tableStart[i]; var hashTable = new HashPos[len]; for (uint u = 0; u < _hashSize[i]; u++) { HashPos hp = table[tableIndex++]; // Locate a free entry in the hash table: UInt32 where = (hp.Hash >> 8) % len; while (hashTable[where].Pos != 0) { if (++where == len) { where = 0; // wrap around } } hashTable[where] = hp; // and store the (hash,pos) pair } // Append to the end of the CDB file: for (uint u = 0; u < len; u++) { Cdb.PackInt32(hashTable[u].Hash, eightBytes, 0); Cdb.PackInt32(hashTable[u].Pos, eightBytes, 4); _file.Write(eightBytes, 0, 8); AdvancePos(8); } } // Rewind to the start of the file and write the fixed-size table: _file.Seek(0, SeekOrigin.Begin); _file.Write(fixedTable, 0, fixedTable.Length); }