/// <summary> /// 构造B+树存储头 /// </summary> /// <returns></returns> private byte[] MakeHeader() { // 魔数前缀 | 版本 | 节点容量 | 键大小 | 根节点块序号 | 空闲块头序号 // prefix | version | node size | key size | block number of root | block number of free list head byte[] header = new byte[HeaderSize]; // 魔数前缀 StoredConstants.TreeFileHeaderPrefix.CopyTo(header, 0); // 版本 1 Byte header[StoredConstants.TreeFileHeaderPrefix.Length] = Version; // 节点容量 4 Bytes int index = StoredConstants.TreeFileHeaderPrefix.Length + 1; StoredHelper.Store(this.NodeCapacity, header, index); index += StoredConstants.IntegerLength; // 键大小 4 Bytes StoredHelper.Store(this.KeyLength, header, index); index += StoredConstants.IntegerLength; // 根节点块序号 8 Bytes StoredHelper.Store(this.RootNodeBlockNumber, header, index); index += StoredConstants.LongLength; // 空闲块头序号 8 Bytes StoredHelper.Store(this.FreeBlockHeadNumber, header, index); index += StoredConstants.LongLength; return(header); }
/// <summary> /// 读取块存储头部 /// </summary> private void ReadFileHeader() { byte[] header = new byte[this.FileHeaderSize]; this.Stream.Seek(this.SeekStart, SeekOrigin.Begin); this.Stream.Read(header, 0, this.FileHeaderSize); int index = 0; // 魔数前缀 foreach (byte b in FileHeaderPrefix) { if (header[index] != b) { throw new LinkedFileException("invalid header prefix"); } index++; } // 块的大小 this.BlockSize = StoredHelper.RetrieveInt(header, index); index += StoredConstants.IntegerLength; // 空闲列表头序号 this.FreeBlockHead = StoredHelper.RetrieveLong(header, index); index += StoredConstants.LongLength; this.IsHeaderDirty = false; if (this.BlockSize < StoredConstants.MinBlockSize) { throw new LinkedFileException("linked file block size too small " + this.BlockSize); } }
/// <summary> /// 读取块存储头部 /// </summary> private void ReadFileHeader() { // 魔数前缀长度 | 块的大小 byte[] header = new byte[FileHeaderSize]; this.Stream.Seek(this.SeekStart, SeekOrigin.Begin); this.Stream.Read(header, 0, FileHeaderSize); int index = 0; // 魔数前缀 foreach (byte b in FileHeaderPrefix) { if (header[index] != b) { throw new BlockFileException("Invalid block file header prefix."); } index++; } // 块的大小 4 Bytes this.BlockSize = StoredHelper.RetrieveInt(header, index); index += StoredConstants.IntegerLength; if (this.BlockSize < StoredConstants.MinBlockSize) { throw new BlockFileException("Block size is too small " + this.BlockSize + "."); } }
/// <summary> /// 获取指定头序号的大块数据 /// </summary> /// <param name="headBlockNumber">指定头序号</param> /// <returns>大块数据</returns> public byte[] GetChunk(long headBlockNumber) { // get the head, interpret the length LinkedFileBlockFlag blockFlag; long nextBlockNumber; byte[] block = this.ReadBlock(headBlockNumber, out blockFlag, out nextBlockNumber); // 读取块长度 int length = StoredHelper.RetrieveInt(block, 0); if (length < 0) { throw new LinkedFileException("negative length block? must be garbage: " + length); } if (blockFlag != LinkedFileBlockFlag.Head) { throw new LinkedFileException("first block not marked HEAD"); } byte[] buffer = new byte[length]; // 在第一个块中读取数据 int firstLength = this.BlockSize - StoredConstants.IntegerLength; if (firstLength > length) { firstLength = length; } Array.Copy(block, StoredConstants.IntegerLength, buffer, 0, firstLength); // 在链接的其他块中读取数据 int stored = firstLength; while (stored < length) { // get the next buffer long currentBlockNumber = nextBlockNumber; block = this.ReadBlock(currentBlockNumber, out blockFlag, out nextBlockNumber); int nextLength = this.BlockSize; if (length - stored < nextLength) { nextLength = length - stored; } Array.Copy(block, 0, buffer, stored, nextLength); stored += nextLength; } return(buffer); }
/// <summary> /// 构造存储头 /// </summary> /// <returns></returns> public byte[] MakeFileHeader() { // 魔数前缀长度 | 块的大小 byte[] header = new byte[FileHeaderSize]; // 魔数前缀 FileHeaderPrefix.CopyTo(header, 0); // 块的大小 4 Bytes StoredHelper.Store(this.BlockSize, header, FileHeaderPrefix.Length); return(header); }
/// <summary> /// 读取B+树存储头 /// </summary> private void ReadHeader() { // 魔数前缀 | 版本 | 节点容量 | 键大小 | 根节点块序号 | 空闲块头序号 // prefix | version | node size | key size | block number of root | block number of free list head byte[] header = new byte[HeaderSize]; this.Stream.Seek(this.SeekStart, SeekOrigin.Begin); this.Stream.Read(header, 0, HeaderSize); // 验证头前缀魔数 int index = 0; foreach (byte b in StoredConstants.TreeFileHeaderPrefix) { if (header[index] != b) { throw new BlockFileException("invalid header prefix"); } index++; } // 版本 this.Version = header[index]; index += 1; // 节点容量 this.NodeCapacity = StoredHelper.RetrieveInt(header, index); index += StoredConstants.IntegerLength; // 键大小 this.KeyLength = StoredHelper.RetrieveInt(header, index); index += StoredConstants.IntegerLength; // 根节点块序号 this.RootNodeBlockNumber = StoredHelper.RetrieveLong(header, index); index += StoredConstants.LongLength; // 空闲块头序号 this.FreeBlockHeadNumber = StoredHelper.RetrieveLong(header, index); index += StoredConstants.LongLength; if (this.NodeCapacity < 2) { throw new BPlusTreeException("node size must be larger than 2"); } if (this.KeyLength < 5) { throw new BPlusTreeException("Key length must be larger than 5"); } }
/// <summary> /// 在指定的块序号之后,查找新的空闲块头序号 /// </summary> /// <param name="blockNumber">指定的块序号</param> /// <returns>新的空闲块头序号</returns> private long ParseFreeBlock(long blockNumber) { int freeSize = 1 + StoredConstants.LongLength; byte[] block = new byte[freeSize]; this.BlockFile.ReadBlock(blockNumber, block, 0, freeSize); if (block[0] != (byte)BPlusTreeNodeIndicator.Free) { throw new BPlusTreeException("free block not marked free"); } long newHead = StoredHelper.RetrieveLong(block, 1); return(newHead); }
/// <summary> /// 读取块 /// </summary> /// <param name="blockNumber">块序号</param> /// <param name="blockFlag">块标记</param> /// <param name="nextBlockNumber">链接的下一个块</param> /// <returns>块数据</returns> private byte[] ReadBlock(long blockNumber, out LinkedFileBlockFlag blockFlag, out long nextBlockNumber) { byte[] fullBuffer = new byte[LinkedBlockOverhead + this.BlockSize]; this.BlockFile.ReadBlock(blockNumber, fullBuffer, 0, fullBuffer.Length); // 读取块标记 blockFlag = (LinkedFileBlockFlag)fullBuffer[0]; // 读取链接的下一个块的序号 nextBlockNumber = StoredHelper.RetrieveLong(fullBuffer, 1); // 读取数据 byte[] buffer = new byte[this.BlockSize]; Array.Copy(fullBuffer, LinkedBlockOverhead, buffer, 0, this.BlockSize); return(buffer); }
/// <summary> /// 构造存储头 /// </summary> /// <returns></returns> public byte[] MakeFileHeader() { // 魔数前缀长度 | 块的大小 | 空闲列表头序号 byte[] header = new byte[this.FileHeaderSize]; // 魔数前缀 FileHeaderPrefix.CopyTo(header, 0); int index = FileHeaderPrefix.Length; // 块的大小 StoredHelper.Store(this.BlockSize, header, index); index += StoredConstants.IntegerLength; // 空闲列表头序号 StoredHelper.Store(this.FreeBlockHead, header, index); index += StoredConstants.LongLength; return(header); }
/// <summary> /// 回收再利用指定序号的块 /// </summary> /// <param name="blockNumber">指定序号</param> public void ReclaimBlock(long blockNumber) { int freeSize = 1 + StoredConstants.LongLength; byte[] block = new byte[freeSize]; // it better not already be marked free this.BlockFile.ReadBlock(blockNumber, block, 0, 1); if (block[0] == (byte)BPlusTreeNodeIndicator.Free) { throw new BPlusTreeException("attempt to re-free free block not allowed"); } block[0] = (byte)BPlusTreeNodeIndicator.Free; // 将指定序号的块置为空闲 StoredHelper.Store(this.FreeBlockHeadNumber, block, 1); this.BlockFile.WriteBlock(blockNumber, block, 0, freeSize); this.FreeBlockHeadNumber = blockNumber; }
/// <summary> /// 写入块 /// </summary> /// <param name="blockNumber">块序号</param> /// <param name="blockFlag">块标记</param> /// <param name="fromArray">读取至此数组</param> /// <param name="startAt">读取起始点</param> /// <param name="length">读取长度</param> /// <param name="nextBlockNumber">链接的下一个块</param> private void WriteBlock(long blockNumber, LinkedFileBlockFlag blockFlag, byte[] fromArray, int startAt, int length, long nextBlockNumber) { if (this.BlockSize < length) { throw new LinkedFileException("block size too small " + this.BlockSize + "<" + length); } byte[] buffer = new byte[LinkedBlockOverhead + length]; // 写入块标记 buffer[0] = (byte)blockFlag; // 1 Byte // 写入链接的下一个块的序号 StoredHelper.Store(nextBlockNumber, buffer, 1); // 8 Bytes // 写入数据 if (fromArray != null) { Array.Copy(fromArray, startAt, buffer, LinkedBlockOverhead, length); } this.BlockFile.WriteBlock(blockNumber, buffer, 0, buffer.Length); }
/// <summary> /// 存储新的大块数据,返回头序号。 /// </summary> /// <param name="chunk">读取至此数组</param> /// <param name="startAt">读取起始点</param> /// <param name="length">读取长度</param> /// <returns>存储数据的头序号</returns> public long StoreChunk(byte[] chunk, int startAt, int length) { if (length < 0 || startAt < 0) { throw new LinkedFileException("cannot store negative length chunk (" + startAt + "," + length + ")"); } // 分配用于存储的块 并标记为头块 long currentBlockNumber = this.AllocateBlock(); LinkedFileBlockFlag currentBlockFlag = LinkedFileBlockFlag.Head; // 存储数据的头序号 long headBlockNumber = currentBlockNumber; int endAt = startAt + length; // special case: zero length chunk if (endAt > chunk.Length) { throw new LinkedFileException("array doesn't have this much data: " + endAt); } // store header with length information byte[] block = new byte[this.BlockSize]; // 存储块长度 StoredHelper.Store(length, block, 0); int fromIndex = startAt; int firstBlockLength = this.BlockSize - StoredConstants.IntegerLength; int storedLength = 0; if (firstBlockLength > length) { firstBlockLength = length; } // 存储数据 Array.Copy(chunk, fromIndex, block, StoredConstants.IntegerLength, firstBlockLength); storedLength += firstBlockLength; fromIndex += firstBlockLength; // 存储剩余数据 while (storedLength < length) { // 获取下一个块序号 long nextBlockNumber = this.AllocateBlock(); // 存储当前数据 this.WriteBlock(currentBlockNumber, currentBlockFlag, block, 0, block.Length, nextBlockNumber); currentBlockNumber = nextBlockNumber; currentBlockFlag = LinkedFileBlockFlag.Body; // 下一个块则为Body int nextLength = this.BlockSize; if (storedLength + nextLength > length) { nextLength = length - storedLength; } Array.Copy(chunk, fromIndex, block, 0, nextLength); storedLength += nextLength; fromIndex += nextLength; } // 存储最终块 this.WriteBlock(currentBlockNumber, currentBlockFlag, block, 0, block.Length, StoredConstants.NullBlockNumber); return(headBlockNumber); }