/// <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> /// <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="chunksInUse">若指定使用中的块列表,则逐个匹配确认是否存在对应的块</param> /// <param name="throwExceptionWhenNotMatch">当发现未匹配的使用块时,是否抛出异常</param> private void CheckStructure(IDictionary <long, string> chunksInUse, bool throwExceptionWhenNotMatch) { Hashtable blockNumberToFlag = new Hashtable(); Hashtable blockNumberToNext = new Hashtable(); Hashtable visited = new Hashtable(); long lastBlockNumber = this.BlockFile.NextBlockNumber(); for (long number = 0; number < lastBlockNumber; number++) { LinkedFileBlockFlag blockFlag; long nextBufferNumber; this.ReadBlock(number, out blockFlag, out nextBufferNumber); blockNumberToFlag[number] = blockFlag; blockNumberToNext[number] = nextBufferNumber; } // traverse the freelist long currentFreeBlockNumber = this.FreeBlockHead; while (currentFreeBlockNumber != StoredConstants.NullBlockNumber) { if (visited.ContainsKey(currentFreeBlockNumber)) { throw new LinkedFileException("cycle in free list " + currentFreeBlockNumber); } visited[currentFreeBlockNumber] = currentFreeBlockNumber; LinkedFileBlockFlag blockFlag = (LinkedFileBlockFlag)blockNumberToFlag[currentFreeBlockNumber]; long nextBlockNumber = (long)blockNumberToNext[currentFreeBlockNumber]; if (blockFlag != LinkedFileBlockFlag.Free) { throw new LinkedFileException("free list element not marked free " + currentFreeBlockNumber); } currentFreeBlockNumber = nextBlockNumber; } // traverse all nodes marked head Hashtable allChunks = new Hashtable(); for (long number = 0; number < lastBlockNumber; number++) { LinkedFileBlockFlag blockFlag = (LinkedFileBlockFlag)blockNumberToFlag[number]; if (blockFlag == LinkedFileBlockFlag.Head) { if (visited.ContainsKey(number)) { throw new LinkedFileException("head buffer already visited " + number); } allChunks[number] = number; visited[number] = number; long bodyBlockNumber = (long)blockNumberToNext[number]; while (bodyBlockNumber != StoredConstants.NullBlockNumber) { LinkedFileBlockFlag bodyBlockFlag = (LinkedFileBlockFlag)blockNumberToFlag[bodyBlockNumber]; long nextBlockNumber = (long)blockNumberToNext[bodyBlockNumber]; if (visited.ContainsKey(bodyBlockNumber)) { throw new LinkedFileException("body block visited twice " + bodyBlockNumber); } visited[bodyBlockNumber] = bodyBlockFlag; if (bodyBlockFlag != LinkedFileBlockFlag.Body) { throw new LinkedFileException("body block not marked body " + blockFlag); } bodyBlockNumber = nextBlockNumber; } // check retrieval this.GetChunk(number); } } // make sure all were visited for (long number = 0; number < lastBlockNumber; number++) { if (!visited.ContainsKey(number)) { throw new LinkedFileException("block not found either as data or free " + number); } } // check against in use list if (chunksInUse != null) { ArrayList notInUse = new ArrayList(); foreach (var d in chunksInUse) { long blockNumber = (long)d.Key; if (!allChunks.ContainsKey(blockNumber)) { throw new LinkedFileException("block in used list not found in linked file " + blockNumber + " " + d.Value); } } foreach (DictionaryEntry d in allChunks) { long blockNumber = (long)d.Key; if (!chunksInUse.ContainsKey(blockNumber)) { if (!throwExceptionWhenNotMatch) { throw new LinkedFileException("block in linked file not in used list " + blockNumber); } notInUse.Add(blockNumber); } } notInUse.Sort(); notInUse.Reverse(); foreach (object item in notInUse) { long blockNumber = (long)item; this.ReleaseBlocks(blockNumber); } } }
/// <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); }
/// <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; }