// inserts a key/value into an existing space // if necessary, splits the space into two (used/free) // NOTE - only call this method from inside a lock {} block private void InsertIntoDisk(Hash key, byte[] value, DiskEntry entry) { InitStream(); if (_stream.Position != entry.Offset) { _stream.Seek(entry.Offset, SeekOrigin.Begin); } _stream.Write(value, 0, value.Length); _stream.Flush(); var diff = entry.Length - value.Length; if (diff > 0) { var free = new DiskEntry() { Offset = (uint)(entry.Offset + value.Length), Length = (uint)diff }; _freeSpace.Add(free); } _entries[key] = new DiskEntry() { Offset = entry.Offset, Length = (uint)value.Length }; RequestUpdate(key); }
public void SetValue(Hash key, byte[] value) { Throw.If(!_ready, "not ready"); if (value == null || value.Length == 0) { Remove(key); return; } if (ContainsKey(key)) { DiskEntry entry; lock (_lock) { entry = _entries[key]; // check if we can fit this in the previous occupied space if (entry.Length >= value.Length) { InsertIntoDisk(key, value, entry); return; } else { // we cant fit it into the previous space, so mark the previous space as free _freeSpace.Add(entry); } } } int freeSpaceIndex = -1; uint min = uint.MaxValue; lock (_lock) { // search for the minimum free space that can fit this new entry for (int i = 0; i < _freeSpace.Count; i++) { var entry = _freeSpace[i]; if (entry.Length >= value.Length && entry.Length < min) { min = entry.Length; freeSpaceIndex = i; } } } if (freeSpaceIndex >= 0) { lock (_lock) { var entry = _freeSpace[freeSpaceIndex]; _freeSpace.RemoveAt(freeSpaceIndex); InsertIntoDisk(key, value, entry); } } else // nothing found, we need to alloc more disk space here { lock (_lock) { InitStream(); _stream.Seek(0, SeekOrigin.End); var entry = new DiskEntry() { Length = (uint)value.Length, Offset = (uint)_stream.Position }; InsertIntoDisk(key, value, entry); } } }
public void ReadBlocks() { lock (_lock) { InitStream(); _entries.Clear(); _freeSpace.Clear(); if (_stream.Length > 0) { _stream.Seek(0, SeekOrigin.Begin); using (var reader = new BinaryReader(_stream, Encoding.ASCII, true)) { //uint nextOffset = (uint)(_stream.Length - _header.Length); uint blockOffset = reader.ReadUInt32(); _lastBlockOffset = blockOffset; while (blockOffset > 0) { _stream.Seek(blockOffset, SeekOrigin.Begin); var temp = reader.ReadBytes(_header.Length); Throw.If(!temp.SequenceEqual(_header), "header mark expected"); var entryCount = reader.ReadUInt32(); while (entryCount > 0) { var hash = reader.ReadHash(); uint location = reader.ReadUInt32(); uint size; switch (DataSize) { case KeyStoreDataSize.Small: size = reader.ReadByte(); break; case KeyStoreDataSize.Medium: size = reader.ReadUInt16(); break; case KeyStoreDataSize.Large: size = reader.ReadUInt24(); break; case KeyStoreDataSize.Huge: size = reader.ReadUInt32(); break; default: throw new Exception("unsupported data size"); } if (!_entries.ContainsKey(hash)) { _entries[hash] = new DiskEntry() { Length = size, Offset = location }; } entryCount--; } blockOffset = reader.ReadUInt32(); } } } else { this._lastBlockOffset = 0; } } }