/// <summary> /// Adds a key to the constant database. /// </summary> /// <param name="key">The key to add to the database.</param> /// <param name="data">The data associated with this key.</param> /// <exception cref="System.IO.IOException">If an error occurs adding the key /// to the database.</exception> public void Add(byte[] key, byte[] data) { /* Write out the key length. */ CdbUtils.WriteLittleEndianInt32(fileStream, key.Length); /* Write out the data length. */ CdbUtils.WriteLittleEndianInt32(fileStream, data.Length); /* Write out the key. */ fileStream.Write(key, 0, key.Length); /* Write out the data. */ fileStream.Write(data, 0, data.Length); /* Add the hash pointer to our list. */ int hash = CdbUtils.Hash(key); hashPointers.Add(new CdbHashPointer(hash, currentKeyPos)); /* Add this item to the count. */ tableCount[hash & 0xff]++; /* Update the file position pointer. */ PosPlus(8); PosPlus(key.Length); PosPlus(data.Length); }
/* Returns the next data element in the CDB file. */ private CdbEntry NextElement() { try { /* Read the key and value lengths. */ int klen = CdbUtils.ReadLittleEndianInt32(cdbStream); pos += 4; int dlen = CdbUtils.ReadLittleEndianInt32(cdbStream); pos += 4; /* Read the key. */ byte[] key = new byte[klen]; for (int off = 0; off < klen;) // below { int count = cdbStream.Read(key, off, klen - off); if (count == -1) { throw new ArgumentException("invalid cdb format"); } off += count; } pos += klen; /* Read the data. */ byte[] data = new byte[dlen]; for (int off = 0; off < dlen;) // below { int count = cdbStream.Read(data, off, dlen - off); if (count == -1) { throw new ArgumentException("invalid cdb format"); } off += count; } pos += dlen; /* Return a CdbElement with the key and data. */ return(new CdbEntry(key, data)); } catch (IOException) { throw new ArgumentException("invalid cdb format"); } }
/// <summary> /// Finds the next record stored under the given key. /// </summary> /// <param name="key"> The key to search for. </param> /// <returns> The next record store under the given key, or /// <code>null</code> if no record with that key could be found. </returns> public byte[] FindNext(byte[] key) { lock (this) { /* There are no keys if we could not read the slot table. */ if (slotTable == null) { return(null); } /* Locate the hash entry if we have not yet done so. */ if (numberOfHashSlotsSearched == 0) { /* Get the hash value for the key. */ int u = CdbUtils.Hash(key); /* Unpack the information for this record. */ int slot = u & 255; numberOfHashSlots = slotTable[(slot << 1) + 1]; if (numberOfHashSlots == 0) { return(null); } currentKeyHashTablePos = slotTable[slot << 1]; /* Store the hash value. */ currentKeyHashValue = u; /* Locate the slot containing this key. */ u = (int)((uint)u >> 8); u %= numberOfHashSlots; u <<= 3; currentKeyPos = currentKeyHashTablePos + u; } /* Search all of the hash slots for this key. */ try { while (numberOfHashSlotsSearched < numberOfHashSlots) { /* Read the entry for this key from the hash slot. */ fileStream.Seek(currentKeyPos, SeekOrigin.Begin); int h = fileStream.ReadByte() | (fileStream.ReadByte() << 8) | (fileStream.ReadByte() << 16) | (fileStream.ReadByte() << 24); int pos = fileStream.ReadByte() | (fileStream.ReadByte() << 8) | (fileStream.ReadByte() << 16) | (fileStream.ReadByte() << 24); if (pos == 0) { return(null); } /* Advance the loop count and key position. Wrap the * key position around to the beginning of the hash slot * if we are at the end of the table. */ numberOfHashSlotsSearched += 1; currentKeyPos += 8; if (currentKeyPos == (currentKeyHashTablePos + (numberOfHashSlots << 3))) { currentKeyPos = currentKeyHashTablePos; } /* Ignore this entry if the hash values do not match. */ if (h != currentKeyHashValue) { continue; } /* Get the length of the key and data in this hash slot * entry. */ fileStream.Seek(pos, SeekOrigin.Begin); int klen = fileStream.ReadByte() | (fileStream.ReadByte() << 8) | (fileStream.ReadByte() << 16) | (fileStream.ReadByte() << 24); if (klen != key.Length) { continue; } int dlen = fileStream.ReadByte() | (fileStream.ReadByte() << 8) | (fileStream.ReadByte() << 16) | (fileStream.ReadByte() << 24); /* Read the key stored in this entry and compare it to * the key we were given. */ bool match = true; byte[] k = new byte[klen]; fileStream.Read(k, 0, k.Length); for (int i = 0; i < k.Length; i++) { if (k[i] != key[i]) { match = false; break; } } /* No match; check the next slot. */ if (!match) { continue; } /* The keys match, return the data. */ byte[] d = new byte[dlen]; fileStream.Read(d, 0, d.Length); return(d); } } catch (IOException) { return(null); } /* No more data values for this key. */ return(null); } }
/// <summary> /// Finalizes the constant database. /// </summary> /// <exception cref="System.IO.IOException">If an error occurs closing out the /// database.</exception> public void Finish() { if (fileStream == null) { return; } /* Find the start of each hash table. */ int curEntry = 0; for (int i = 0; i < 256; i++) { curEntry += tableCount[i]; tableStart[i] = curEntry; } /* Create a new hash pointer list in order by hash table. */ CdbHashPointer[] slotPointers = new CdbHashPointer[hashPointers.Count]; foreach (CdbHashPointer hp in hashPointers) { slotPointers[--tableStart[hp.HashValue & 0xff]] = hp; } /* Write out each of the hash tables, building the slot table in * the process. */ byte[] slotTable = new byte[2048]; for (int i = 0; i < 256; i++) { /* Get the length of the hashtable. */ int len = tableCount[i] * 2; /* Store the position of this table in the slot table. */ slotTable[(i * 8) + 0] = unchecked ((byte)(currentKeyPos & 0xff)); slotTable[(i * 8) + 1] = unchecked ((byte)(((int)((uint)currentKeyPos >> 8)) & 0xff)); slotTable[(i * 8) + 2] = unchecked ((byte)(((int)((uint)currentKeyPos >> 16)) & 0xff)); slotTable[(i * 8) + 3] = unchecked ((byte)(((int)((uint)currentKeyPos >> 24)) & 0xff)); slotTable[(i * 8) + 4 + 0] = unchecked ((byte)(len & 0xff)); slotTable[(i * 8) + 4 + 1] = unchecked ((byte)(((int)((uint)len >> 8)) & 0xff)); slotTable[(i * 8) + 4 + 2] = unchecked ((byte)(((int)((uint)len >> 16)) & 0xff)); slotTable[(i * 8) + 4 + 3] = unchecked ((byte)(((int)((uint)len >> 24)) & 0xff)); /* Build the hash table. */ int curSlotPointer = tableStart[i]; CdbHashPointer[] hashTable = new CdbHashPointer[len]; for (int u = 0; u < tableCount[i]; u++) { /* Get the hash pointer. */ CdbHashPointer hp = slotPointers[curSlotPointer++]; /* Locate a free space in the hash table. */ int @where = ((int)((uint)hp.HashValue >> 8)) % len; while (hashTable[@where] != null) { if (++@where == len) { @where = 0; } } /* Store the hash pointer. */ hashTable[@where] = hp; } /* Write out the hash table. */ for (int u = 0; u < len; u++) { CdbHashPointer hp = hashTable[u]; if (hp != null) { CdbUtils.WriteLittleEndianInt32(fileStream, hashTable[u].HashValue); CdbUtils.WriteLittleEndianInt32(fileStream, hashTable[u].EntryPos); } else { CdbUtils.WriteLittleEndianInt32(fileStream, 0); CdbUtils.WriteLittleEndianInt32(fileStream, 0); } PosPlus(8); } } /* Seek back to the beginning of the file and write out the * slot table. */ fileStream.Seek(0, SeekOrigin.Begin); fileStream.Write(slotTable, 0, slotTable.Length); /* Close the file. */ fileStream.Dispose(); fileStream = null; }