protected override Result DoWrite(long offset, ReadOnlySpan <byte> source) { long remaining = source.Length; long inOffset = offset; int outOffset = 0; if (!IsRangeValid(offset, source.Length, Length)) { return(ResultFs.OutOfRange.Log()); } lock (Blocks) { while (remaining > 0) { long blockIndex = inOffset / BlockSize; int blockPos = (int)(inOffset % BlockSize); CacheBlock block = GetBlock(blockIndex); int bytesToWrite = (int)Math.Min(remaining, BlockSize - blockPos); source.Slice(outOffset, bytesToWrite).CopyTo(block.Buffer.AsSpan(blockPos)); block.Dirty = true; outOffset += bytesToWrite; inOffset += bytesToWrite; remaining -= bytesToWrite; } } return(Result.Success); }
private void Elevate(CacheBlock block) { CacheBlock target = block; // can we do better? faster find the position to insert while (target.Next != null && target.Next.Frequency <= block.Frequency) { target = target.Next; } if (target == block) { return; } if (block == least) { least = block.Next; least.Prev = null; } else { block.Prev.Next = block.Next; block.Next.Prev = block.Prev; } // insert block after target if (target.Next != null) { target.Next.Prev = block; } block.Next = target.Next; block.Prev = target; target.Next = block; }
protected override Result DoRead(long offset, Span <byte> destination) { long remaining = destination.Length; long inOffset = offset; int outOffset = 0; if (!IsRangeValid(offset, destination.Length, Length)) { return(ResultFs.OutOfRange.Log()); } lock (Blocks) { while (remaining > 0) { long blockIndex = inOffset / BlockSize; int blockPos = (int)(inOffset % BlockSize); CacheBlock block = GetBlock(blockIndex); int bytesToRead = (int)Math.Min(remaining, BlockSize - blockPos); block.Buffer.AsSpan(blockPos, bytesToRead).CopyTo(destination.Slice(outOffset)); outOffset += bytesToRead; inOffset += bytesToRead; remaining -= bytesToRead; } } return(Result.Success); }
private CacheBlock GetBlock(long blockIndex) { if (BlockDict.TryGetValue(blockIndex, out LinkedListNode <CacheBlock> node)) { if (Blocks.First != node) { Blocks.Remove(node); Blocks.AddFirst(node); } return(node.Value); } node = Blocks.Last; FlushBlock(node.Value); CacheBlock block = node.Value; Blocks.RemoveLast(); BlockDict.Remove(block.Index); FlushBlock(block); ReadBlock(block, blockIndex); Blocks.AddFirst(node); BlockDict.Add(blockIndex, node); return(block); }
protected override void WriteImpl(ReadOnlySpan <byte> source, long offset) { long remaining = source.Length; long inOffset = offset; int outOffset = 0; lock (Blocks) { while (remaining > 0) { long blockIndex = inOffset / BlockSize; int blockPos = (int)(inOffset % BlockSize); CacheBlock block = GetBlock(blockIndex); int bytesToWrite = (int)Math.Min(remaining, BlockSize - blockPos); source.Slice(outOffset, bytesToWrite).CopyTo(block.Buffer.AsSpan(blockPos)); block.Dirty = true; outOffset += bytesToWrite; inOffset += bytesToWrite; remaining -= bytesToWrite; } } }
/// <summary> /// 将一个资源注册到缓冲区中 /// </summary> /// <param name="resourceId">资源id</param> /// <param name="allocPtr">资源的字节数组</param> /// <param name="cType">资源缓存类型</param> /// <param name="priority">初始引用计数</param> public static void Register(string resourceId, byte[] allocPtr, ResourceCacheType cType, long priority = 1) { switch (cType) { case ResourceCacheType.Eden: var edenIdx = ResourceCachePool.EdenQueue.FindIndex(x => x.CacheId == resourceId); if (edenIdx == -1) { var cb = new CacheBlock(resourceId, allocPtr, priority); cb.Referred(); ResourceCachePool.EdenQueue.Push(cb); } else { var cBlock = ResourceCachePool.EdenQueue.ElementAt(edenIdx); cBlock.Referred(); cBlock.AllocReference = allocPtr; ResourceCachePool.EdenQueue.Exchange(edenIdx, ResourceCachePool.EdenQueue.Count() - 1); ResourceCachePool.EdenQueue.Adjust(); ResourceCachePool.EdenQueue.ForEachFrom(GlobalConfigContext.EdenResourceCacheSize, t => t.Abandon()); } break; case ResourceCacheType.Permanent: ResourceCachePool.PermanentDictionary.Add(resourceId, new CacheBlock(resourceId, allocPtr, priority)); break; } }
/// <summary> /// Determines whether the specified <see cref="System.Object" />, is equal to this instance. /// </summary> /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param> /// <returns> /// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>. /// </returns> public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) { return(false); } CacheBlock <TKey, TValue> other = (CacheBlock <TKey, TValue>)obj; return(this.Key.Equals(other.Key) && this.Value.Equals(other.Value)); }
/// <summary> /// Writes data to the stream at the current location. /// </summary> /// <param name="buffer">The data to write</param> /// <param name="offset">The first byte to write from buffer</param> /// <param name="count">The number of bytes to write</param> public override void Write(byte[] buffer, int offset, int count) { CheckDisposed(); _stats.TotalWritesIn++; long startPos = _position; int blockSize = _settings.BlockSize; long firstBlock = _position / blockSize; long endBlock = Utilities.Ceil(Math.Min(_position + count, Length), blockSize); int numBlocks = (int)(endBlock - firstBlock); try { _wrappedStream.Position = _position; _wrappedStream.Write(buffer, offset, count); } catch { InvalidateBlocks(firstBlock, numBlocks); throw; } int offsetInNextBlock = (int)(_position % blockSize); if (offsetInNextBlock != 0) { _stats.UnalignedWritesIn++; } // For each block touched, if it's cached, update it int bytesProcessed = 0; for (int i = 0; i < numBlocks; ++i) { int bufferPos = offset + bytesProcessed; int bytesThisBlock = Math.Min(count - bufferPos, blockSize - offsetInNextBlock); if (_blocks.ContainsKey(firstBlock + i)) { CacheBlock block = _blocks[firstBlock + i]; Array.Copy(buffer, bufferPos, block.Data, offsetInNextBlock, bytesThisBlock); } offsetInNextBlock = 0; bytesProcessed += bytesThisBlock; } _position += count; }
private void FlushBlock(CacheBlock block) { if (!block.Dirty) { return; } long offset = block.Index * BlockSize; BaseStorage.Write(block.Buffer.AsSpan(0, block.Length), offset); block.Dirty = false; }
private void Evict() { if (lfucache.Count != Capacity) { return; } lfucache.Remove(least.Key); if (least.Next != null) { least.Next.Prev = null; } least = least.Next; }
private void Add(CacheBlock block) { lfucache.Add(block.Key, block); block.Frequency = 1; block.Next = least; if (least != null) { least.Prev = block; } least = block; Elevate(least); }
private void InvalidateBlocks(long firstBlock, int numBlocks) { for (long i = firstBlock; i < (firstBlock + numBlocks); ++i) { if (_blocks.ContainsKey(i)) { CacheBlock block = _blocks[i]; _blocks.Remove(i); _lru.Remove(block); _freeBlocks.Add(block); _stats.FreeReadBlocks++; } } }
private void ReadBlock(CacheBlock block, long index) { long offset = index * BlockSize; int length = BlockSize; if (Length != -1) { length = (int)Math.Min(Length - offset, length); } BaseStorage.Read(block.Buffer.AsSpan(0, length), offset); block.Length = length; block.Index = index; block.Dirty = false; }
public static IntPtr Alloc(int size) { lock (memoryBlocks) { if (((busyBlocks >= maximumCacheSize) || (size > maxSizeToCache)) || (size < minSizeToCache)) { return(Marshal.AllocHGlobal(size)); } if (currentCacheSize == busyBlocks) { IntPtr memoryBlock = Marshal.AllocHGlobal(size); memoryBlocks.Add(new CacheBlock(memoryBlock, size)); busyBlocks++; currentCacheSize++; cachedMemory += size; return(memoryBlock); } for (int i = 0; i < currentCacheSize; i++) { CacheBlock block = memoryBlocks[i]; if (block.Free && (block.Size >= size)) { block.Free = false; busyBlocks++; return(block.MemoryBlock); } } for (int j = 0; j < currentCacheSize; j++) { CacheBlock block2 = memoryBlocks[j]; if (block2.Free) { Marshal.FreeHGlobal(block2.MemoryBlock); memoryBlocks.RemoveAt(j); currentCacheSize--; cachedMemory -= block2.Size; IntPtr ptr2 = Marshal.AllocHGlobal(size); memoryBlocks.Add(new CacheBlock(ptr2, size)); busyBlocks++; currentCacheSize++; cachedMemory += size; return(ptr2); } } return(IntPtr.Zero); } }
public CachedStorage(IStorage baseStorage, int blockSize, int cacheSize, bool leaveOpen) { BaseStorage = baseStorage; BlockSize = blockSize; LeaveOpen = leaveOpen; BaseStorage.GetSize(out long baseSize).ThrowIfFailure(); Length = baseSize; for (int i = 0; i < cacheSize; i++) { var block = new CacheBlock { Buffer = new byte[blockSize], Index = -1 }; Blocks.AddLast(block); } }
public Cache(int cacheSize) { CacheSize = cacheSize; // Fill up the block array with null values Blocks = new CacheBlock <T> [cacheSize]; for (var i = 0; i < cacheSize; i++) { var words = new T[Constants.WordsInBlock]; for (var j = 0; j < Constants.WordsInBlock; j++) { words[j] = default(T); } Blocks[i] = new CacheBlock <T>(words); } }
private void Add(CacheBlock block) { lrucache.Add(block.Key, block); if (latest != null) { block.Prev = latest; block.Next = latest.Next; latest.Next = block; latest = block; } else { latest = block; latest.Next = latest; } }
private CacheBlock GetBlock(long blockIndex) { if (BlockDict.TryGetValue(blockIndex, out LinkedListNode <CacheBlock> node)) { if (Blocks.First != node) { Blocks.Remove(node); Blocks.AddFirst(node); } // If a supposedly active block is null, something's really wrong if (node is null) { throw new NullReferenceException("CachedStorage cache block is unexpectedly null."); } return(node.Value); } // An inactive node shouldn't be null, but we'll fix it if it is anyway node = Blocks.Last ?? new LinkedListNode <CacheBlock>(new CacheBlock { Buffer = new byte[BlockSize], Index = -1 }); FlushBlock(node.Value); CacheBlock block = node.Value; Blocks.RemoveLast(); if (block.Index != -1) { BlockDict.Remove(block.Index); } FlushBlock(block); ReadBlock(block, blockIndex); Blocks.AddFirst(node); BlockDict.Add(blockIndex, node); return(block); }
public CachedStorage(IStorage baseStorage, int blockSize, int cacheSize, bool leaveOpen) { BaseStorage = baseStorage; BlockSize = blockSize; _length = BaseStorage.GetSize(); if (!leaveOpen) { ToDispose.Add(BaseStorage); } for (int i = 0; i < cacheSize; i++) { var block = new CacheBlock { Buffer = new byte[blockSize], Index = -1 }; Blocks.AddLast(block); } }
public CachedStorage(IStorage baseStorage, int blockSize, int cacheSize, bool leaveOpen) { BaseStorage = baseStorage; BlockSize = blockSize; Length = BaseStorage.Length; if (!leaveOpen) { ToDispose.Add(BaseStorage); } for (int i = 0; i < cacheSize; i++) { var block = new CacheBlock { Buffer = ArrayPool <byte> .Shared.Rent(blockSize) }; Blocks.AddLast(block); } }
public void Put(int key, int value) { CacheBlock block; // no such block, try add it if (!lrucache.TryGetValue(key, out block)) { Evict(); Add(block = new CacheBlock { Key = key, Value = value }); } else { block.Value = value; Elevate(block); } }
public MarkdownResponse(string path) { StatusCode = HttpStatusCode.OK; ContentType = "text/html; charset=utf-8"; Contents = stream => { using (var writer = new StreamWriter(stream)) { FileInfo fi = new FileInfo(path); var cache = CacheManager.Instance.MarkdownCache.GetCache(path); if (cache != null && cache.IsEffective(fi)) { // Using Cache writer.Write(cache.Data); return; } // Cache miss string context = null; using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (var reader = new StreamReader(fs, Encoding.UTF8)) { context = CommonMarkConverter.Convert(reader.ReadToEnd()); writer.Write(context); } } // cache update if (cache == null) { cache = new CacheBlock(); } cache.Update(context, fi); CacheManager.Instance.MarkdownCache.Update(path, cache); } }; }
private void Elevate(CacheBlock block) { if (block == latest) { return; } block.Next.Prev = block.Prev; if (block.Prev != null) { block.Prev.Next = block.Next; } var oldest = latest.Next != block ? latest.Next : block.Next; oldest.Prev = null; block.Next = oldest; block.Prev = latest; latest.Next = block; latest = block; }
private CacheBlock GetBlock(long blockIndex) { if (BlockDict.TryGetValue(blockIndex, out LinkedListNode <CacheBlock> node)) { if (Blocks.First != node) { Blocks.Remove(node); Blocks.AddFirst(node); } return(node !.Value); } // An inactive node shouldn't be null, but we'll fix it if it is anyway node = Blocks.Last ?? new LinkedListNode <CacheBlock>(new CacheBlock { Buffer = new byte[BlockSize], Index = -1 }); FlushBlock(node.Value); CacheBlock block = node.Value; Blocks.RemoveLast(); if (block.Index != -1) { BlockDict.Remove(block.Index); } FlushBlock(block); ReadBlock(block, blockIndex); Blocks.AddFirst(node); BlockDict.Add(blockIndex, node); return(block); }
protected override void ReadImpl(Span <byte> destination, long offset) { long remaining = destination.Length; long inOffset = offset; int outOffset = 0; lock (Blocks) { while (remaining > 0) { long blockIndex = inOffset / BlockSize; int blockPos = (int)(inOffset % BlockSize); CacheBlock block = GetBlock(blockIndex); int bytesToRead = (int)Math.Min(remaining, BlockSize - blockPos); block.Buffer.AsSpan(blockPos, bytesToRead).CopyTo(destination.Slice(outOffset)); outOffset += bytesToRead; inOffset += bytesToRead; remaining -= bytesToRead; } } }
private CacheBlock GetFreeBlock() { if (_freeBlocks.Count > 0) { int idx = _freeBlocks.Count - 1; CacheBlock block = _freeBlocks[idx]; _freeBlocks.RemoveAt(idx); _stats.FreeReadBlocks--; return(block); } else if (_blocksCreated < _totalBlocks) { _blocksCreated++; _stats.FreeReadBlocks--; return(new CacheBlock(_settings.BlockSize)); } else { CacheBlock block = _lru.Last.Value; _lru.RemoveLast(); _blocks.Remove(block.Block); return(block); } }
private void StoreBlock(CacheBlock block) { _blocks[block.Block] = block; _lru.AddFirst(block); }
public LFUCache(int capacity) { Capacity = capacity < 0 ? 0 : capacity; lfucache = new System.Collections.Generic.Dictionary <int, CacheBlock>(Capacity); least = null; }
public T LoadData(int index) { //Debug.WriteLine(string.Format("LoadData {0}",index)); ++_cacheRequest; T returnValue; var cacheBlockNode = _cacheBlock.First; int indexInCacheBlock = -1; while (cacheBlockNode != null) { if (cacheBlockNode.Value.Contains(index, out indexInCacheBlock)) { break; } cacheBlockNode = cacheBlockNode.Next; } if (cacheBlockNode == null) { ++_cacheMisses; CacheBlock cacheBlock; if (_cacheBlock.Count < NumCacheBlocks) { cacheBlockNode = new LinkedListNode <CacheBlock>(cacheBlock = new CacheBlock(NumItemsPerCacheBlock)); } else { cacheBlockNode = _cacheBlock.Last; _cacheBlock.RemoveLast(); cacheBlock = cacheBlockNode.Value; } indexInCacheBlock = index % cacheBlock.Data.Length; //request to fill the cacheblock cacheBlock.StartIndex = index - indexInCacheBlock; int count = InternalLoad(cacheBlock.StartIndex, cacheBlock.Data); cacheBlock.EndIndex = Math.Min(count, cacheBlock.StartIndex + cacheBlock.Data.Length) - 1; if (count != _count /*collection has changed in the meantime*/) { bool firstTime = _count == UninitializedCount; // update the count [HÄÄ??->] unless it was undefined _count = count; // signal that our collection has changed, if this is not the first time aroud // failure to check for this will give a nullreferenceexception on collectionchanged if (!firstTime) { _list.NotifyCollectionChanged(); } /*DEPRECATED*/ //TODO remove list access // clear the cache: the only block left is the one we're holding _cacheBlock.Clear(); } _cacheBlock.AddFirst(cacheBlockNode); // if the index is outside the bounds of the new count, return nothing if (indexInCacheBlock > cacheBlock.EndIndex) { returnValue = null; } else { returnValue = cacheBlock.Data[indexInCacheBlock]; } if (/*DEBUG*/ returnValue == null) { Debug.WriteLine("returning null!"); } } else { // move the block to the front of the cache if it's not already there if (cacheBlockNode != _cacheBlock.First) { _cacheBlock.Remove(cacheBlockNode); _cacheBlock.AddFirst(cacheBlockNode); } returnValue = cacheBlockNode.Value.Data[indexInCacheBlock]; if (/*DEBUG*/ returnValue == null) { Debug.WriteLine("returning null!"); } } if (/*DEBUG*/ returnValue == null) { Debug.WriteLine("returning null!"); } return(returnValue); }
/// <summary> /// Reads data from the stream. /// </summary> /// <param name="buffer">The buffer to fill</param> /// <param name="offset">The buffer offset to start from</param> /// <param name="count">The number of bytes to read</param> /// <returns>The number of bytes read</returns> public override int Read(byte[] buffer, int offset, int count) { CheckDisposed(); long readStartPos = _position; if (_position >= Length) { if (_atEof) { throw new IOException("Attempt to read beyond end of stream"); } else { _atEof = true; return(0); } } _stats.TotalReadsIn++; if (count > _settings.LargeReadSize) { _stats.LargeReadsIn++; _stats.TotalReadsOut++; _wrappedStream.Position = _position; int numRead = _wrappedStream.Read(buffer, offset, count); _position = _wrappedStream.Position; if (_position >= Length) { _atEof = true; } return(numRead); } int totalBytesRead = 0; bool servicedFromCache = false; bool servicedOutsideCache = false; int blockSize = _settings.BlockSize; long firstBlock = _position / blockSize; int offsetInNextBlock = (int)(_position % blockSize); long endBlock = Utilities.Ceil(Math.Min(_position + count, Length), blockSize); int numBlocks = (int)(endBlock - firstBlock); if (offsetInNextBlock != 0) { _stats.UnalignedReadsIn++; } int blocksRead = 0; while (blocksRead < numBlocks) { // Read from the cache as much as possible while (blocksRead < numBlocks && _blocks.ContainsKey(firstBlock + blocksRead)) { CacheBlock block = _blocks[firstBlock + blocksRead]; int bytesToRead = Math.Min(count - totalBytesRead, block.Data.Length - offsetInNextBlock); Array.Copy(block.Data, offsetInNextBlock, buffer, offset + totalBytesRead, bytesToRead); offsetInNextBlock = 0; totalBytesRead += bytesToRead; _position += bytesToRead; blocksRead++; servicedFromCache = true; } // Now handle a sequence of (one or more) blocks that are not cached if (blocksRead < numBlocks && !_blocks.ContainsKey(firstBlock + blocksRead)) { servicedOutsideCache = true; // Figure out how many blocks to read from the wrapped stream int blocksToRead = 0; while (blocksRead + blocksToRead < numBlocks && blocksToRead < _blocksInReadBuffer && !_blocks.ContainsKey(firstBlock + blocksRead + blocksToRead)) { ++blocksToRead; } // Allow for the end of the stream not being block-aligned long readPosition = (firstBlock + blocksRead) * (long)blockSize; int bytesToRead = (int)Math.Min(blocksToRead * blockSize, Length - readPosition); // Do the read _stats.TotalReadsOut++; _wrappedStream.Position = readPosition; int bytesRead = Utilities.ReadFully(_wrappedStream, _readBuffer, 0, bytesToRead); if (bytesRead != bytesToRead) { throw new IOException("Short read before end of stream"); } // Cache the read blocks for (int i = 0; i < blocksToRead; ++i) { int copyBytes = Math.Min(blockSize, bytesToRead - i * blockSize); CacheBlock block = GetFreeBlock(); block.Block = firstBlock + blocksRead + i; Array.Copy(_readBuffer, i * blockSize, block.Data, 0, copyBytes); if (copyBytes < blockSize) { Array.Clear(_readBuffer, copyBytes, blockSize - copyBytes); } StoreBlock(block); } blocksRead += blocksToRead; // Propogate the data onto the caller int bytesToCopy = Math.Min(count - totalBytesRead, bytesRead - offsetInNextBlock); Array.Copy(_readBuffer, offsetInNextBlock, buffer, offset + totalBytesRead, bytesToCopy); totalBytesRead += bytesToCopy; _position += bytesToCopy; offsetInNextBlock = 0; } } if (_position >= Length) { _atEof = true; } if (servicedFromCache) { _stats.ReadCacheHits++; } if (servicedOutsideCache) { _stats.ReadCacheMisses++; } return(totalBytesRead); }