From a034f290666fd6c79128fd9651fbfdccfb2380c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Tue, 18 Sep 2018 15:28:53 +0800 Subject: [PATCH 01/11] add stream extension --- .../CatLib.Core.NetStandard.csproj | 2 + .../CatLib.Core.Tests.csproj | 1 + .../Support/Util/StreamExtensionTests.cs | 32 ++++++++++ src/CatLib.Core/CatLib.Core.csproj | 2 + .../Support/Stream/MemoryBlockStream.cs | 60 +++++++++++++++++++ .../Support/Util/StreamExtension.cs | 58 ++++++++++++++++++ 6 files changed, 155 insertions(+) create mode 100644 src/CatLib.Core.Tests/Support/Util/StreamExtensionTests.cs create mode 100644 src/CatLib.Core/Support/Stream/MemoryBlockStream.cs create mode 100644 src/CatLib.Core/Support/Util/StreamExtension.cs diff --git a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj index e622e81..8ca83e8 100644 --- a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj +++ b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj @@ -104,6 +104,7 @@ + @@ -134,6 +135,7 @@ + diff --git a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj index e13b715..e3277d3 100644 --- a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj +++ b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj @@ -56,6 +56,7 @@ + diff --git a/src/CatLib.Core.Tests/Support/Util/StreamExtensionTests.cs b/src/CatLib.Core.Tests/Support/Util/StreamExtensionTests.cs new file mode 100644 index 0000000..ed5ea25 --- /dev/null +++ b/src/CatLib.Core.Tests/Support/Util/StreamExtensionTests.cs @@ -0,0 +1,32 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System.IO; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CatLib.Core.Tests.Support.Util +{ + [TestClass] + public class StreamExtensionTests + { + [TestMethod] + public void TestAppendTo() + { + var stream1 = new MemoryStream(Encoding.Default.GetBytes("Hello world")); + var stream2 = new MemoryStream(16); + + var count = stream1.AppendTo(stream2); + Assert.AreEqual(11, count); + Assert.AreEqual("Hello world", Encoding.Default.GetString(stream2.GetBuffer(), 0, (int)stream2.Length)); + } + } +} diff --git a/src/CatLib.Core/CatLib.Core.csproj b/src/CatLib.Core/CatLib.Core.csproj index 2fb2a97..87acb96 100644 --- a/src/CatLib.Core/CatLib.Core.csproj +++ b/src/CatLib.Core/CatLib.Core.csproj @@ -85,6 +85,7 @@ + @@ -100,6 +101,7 @@ + diff --git a/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs b/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs new file mode 100644 index 0000000..a68f059 --- /dev/null +++ b/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs @@ -0,0 +1,60 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.IO; +/* +namespace CatLib +{ + + /// + /// 内存块数据流 + /// + public class MemoryBlockStream : Stream + { + /// + /// 单个内存块的大小 + /// + public int BlockSize { get; private set; } + + /// + /// 内存块数据流 + /// + /// 单个内存块的分块 + public MemoryBlockStream(int blockSize = 4096) + { + BlockSize = blockSize; + } + + /// + /// 计算规定值最近的二的次幂的容量 + /// + /// 规定值值 + /// 容量 + private static int GetPrime(int min) + { + min = Math.Max(0, min); + + var result = 8192; + for (var i = 2; i < int.MaxValue; i = i << 1) + { + if (i >= min) + { + result = i; + break; + } + } + + return result; + } + } +} +*/ diff --git a/src/CatLib.Core/Support/Util/StreamExtension.cs b/src/CatLib.Core/Support/Util/StreamExtension.cs new file mode 100644 index 0000000..3cd58b8 --- /dev/null +++ b/src/CatLib.Core/Support/Util/StreamExtension.cs @@ -0,0 +1,58 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.IO; + +namespace CatLib +{ + /// + /// Stream扩展函数 + /// + public static class StreamExtension + { + /// + /// 默认的缓冲区 + /// + [ThreadStatic] + private static readonly byte[] buffer; + + /// + /// Stream扩展函数 + /// + static StreamExtension() + { + buffer = new byte[4096]; + } + + /// + /// 将当前流追加到目标流中 + /// + /// 源数据流 + /// 目标数据流 + /// 总共传输了多少数据 + public static long AppendTo(this Stream source, Stream destination) + { + Guard.Requires(source != null); + Guard.Requires(destination != null); + + long result = 0; + int read; + while ((read = source.Read(buffer, 0, buffer.Length)) != 0) + { + destination.Write(buffer, 0, read); + result += read; + } + + return result; + } + } +} From f2892ed092bbf609ac356a775fb2d0b95ba2c6be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Tue, 18 Sep 2018 17:00:14 +0800 Subject: [PATCH 02/11] MemoryBlockStream update(undone) --- .../Support/Stream/MemoryBlockStream.cs | 262 +++++++++++++++++- .../Support/Util/StreamExtension.cs | 12 + 2 files changed, 269 insertions(+), 5 deletions(-) diff --git a/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs b/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs index a68f059..a595544 100644 --- a/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs +++ b/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs @@ -10,34 +10,245 @@ */ using System; +using System.Collections.Generic; using System.IO; -/* + namespace CatLib { - /// /// 内存块数据流 /// - public class MemoryBlockStream : Stream + public class MemoryBlockStream : Stream , ICloneable { + /// + /// 最大内存使用量 + /// + public long MaxMemoryUsable { get; private set; } + /// /// 单个内存块的大小 /// public int BlockSize { get; private set; } + /// + /// 存储的数据 + /// + private LinkedList storage; + + /// + /// 当前游标所处的内存区块 + /// + private LinkedListNode current; + + /// + /// 当前游标所处的位置 + /// + private long position; + + /// + /// 数据的长度 + /// + private long length; + + /// + /// 是否已经被释放了 + /// + private bool disabled; + + /// + /// 偏移量 + /// + public override long Position + { + get + { + AssertDisabled(); + return position; + } + set + { + AssertDisabled(); + Guard.Requires(value >= 0); + position = value; + } + } + + /// + /// 总内存使用量 + /// + public long Capacity + { + get + { + AssertDisabled(); + return storage.Count * MaxMemoryUsable; + } + } + + /// + /// 数据的长度 + /// + public override long Length + { + get + { + AssertDisabled(); + return length; + } + } + + /// + /// 是否是可以写入数据的 + /// + public override bool CanWrite + { + get { return !disabled; } + } + + /// + /// 是否可以进行游标偏移 + /// + public override bool CanSeek + { + get { return !disabled; } + } + + /// + /// 是否可以读取数据 + /// + public override bool CanRead + { + get { return !disabled; } + } + /// /// 内存块数据流 /// /// 单个内存块的分块 public MemoryBlockStream(int blockSize = 4096) + : this(long.MaxValue, blockSize) + { + + } + + /// + /// 克隆 + /// + /// + public object Clone() + { + return null; + } + + /// + /// 内存块数据流 + /// + /// 最大内存使用量 + /// 单个内存块的分块 + public MemoryBlockStream(long maxMemoryUsable, int blockSize = 4096) { BlockSize = blockSize; + MaxMemoryUsable = Math.Max(maxMemoryUsable, blockSize); + storage = new LinkedList(); + length = 0; + position = 0; + disabled = false; + current = null; + } + + /// + /// 清除当前流的缓冲区 + /// + public override void Flush() + { + // 只有存在数据落地或者转移的情况下此函数才有效 + // 由于是内存缓存,所以这里我们忽略这个函数 + } + + /// + /// 偏移游标到指定位置 + /// + /// 偏移量 + /// 偏移方向 + /// 新的位置 + public override long Seek(long offset, SeekOrigin origin) + { + Guard.Requires(offset >= 0); + AssertDisabled(); + + long tempPosition; + switch (origin) + { + case SeekOrigin.Begin: + { + tempPosition = offset; + break; + } + case SeekOrigin.Current: + { + tempPosition = unchecked(position + offset); + + break; + } + case SeekOrigin.End: + { + tempPosition = unchecked(length + offset); + break; + } + default: + throw new ArgumentException("Unknow SeekOrigin"); + } + + if (tempPosition < 0) + { + throw new IOException("seek position less than 0"); + } + + EnsureStorage(tempPosition); + position = tempPosition; + RefreshCurrentNode(position); + return position; + } + + /// + /// 设定长度 + /// + /// 新的长度值 + public override void SetLength(long value) + { + Guard.Requires(value >= 0); + AssertDisabled(); + EnsureStorage(value); + length = value; + } + + /// + /// 写入数据 + /// + /// 需要写入的字节流 + /// 字节流的起始位置 + /// 需要写入的长度 + public override void Write(byte[] buffer, int offset, int count) + { + + } + + /// + /// 读取数据到指定缓冲区 + /// + /// 指定缓冲区 + /// 缓冲区的起始位置 + /// 需要读取的长度 + /// 实际读取的长度 + public override int Read(byte[] buffer, int offset, int count) + { + return 0; } /// /// 计算规定值最近的二的次幂的容量 /// - /// 规定值值 + /// 规定值 /// 容量 private static int GetPrime(int min) { @@ -55,6 +266,47 @@ private static int GetPrime(int min) return result; } + + /// + /// 刷新当前的游标节点 + /// + /// 偏移量 + private void RefreshCurrentNode(long position) + { + // 将current 刷新至position可读节点 + } + + /// + /// 保障需要占用的内存存储空间 + /// + /// 需要占用的存储空间 + private void EnsureStorage(long value) + { + AssertMemoryUseable(value); + } + + /// + /// 断言是否已经被释放 + /// + private void AssertDisabled() + { + if (disabled) + { + throw new ObjectDisposedException(null, "[" + GetType() + "] Stream is closed."); + } + } + + /// + /// 断言内存使用 + /// + /// 内存占用 + private void AssertMemoryUseable(long value) + { + if (value > MaxMemoryUsable) + { + throw new OutOfMemoryException("Memory exceeds usage limit " + (MaxMemoryUsable / 1048576) + " MB"); + } + } } } -*/ + diff --git a/src/CatLib.Core/Support/Util/StreamExtension.cs b/src/CatLib.Core/Support/Util/StreamExtension.cs index 3cd58b8..d33c8e9 100644 --- a/src/CatLib.Core/Support/Util/StreamExtension.cs +++ b/src/CatLib.Core/Support/Util/StreamExtension.cs @@ -40,6 +40,18 @@ static StreamExtension() /// 目标数据流 /// 总共传输了多少数据 public static long AppendTo(this Stream source, Stream destination) + { + return source.AppendTo(destination, buffer); + } + + /// + /// 将当前流追加到目标流中 + /// + /// 源数据流 + /// 目标数据流 + /// 所使用的缓冲区 + /// 总共传输了多少数据 + public static long AppendTo(this Stream source, Stream destination, byte[] buffer) { Guard.Requires(source != null); Guard.Requires(destination != null); From 006090a59a72a161e376925e4c678e96ebf60909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Tue, 18 Sep 2018 19:00:53 +0800 Subject: [PATCH 03/11] update(undone) --- .../Support/Stream/MemoryBlockStream.cs | 185 ++++++++++++++++-- 1 file changed, 169 insertions(+), 16 deletions(-) diff --git a/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs b/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs index a595544..85d9293 100644 --- a/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs +++ b/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs @@ -33,12 +33,12 @@ public class MemoryBlockStream : Stream , ICloneable /// /// 存储的数据 /// - private LinkedList storage; + private LinkedList> storage; /// /// 当前游标所处的内存区块 /// - private LinkedListNode current; + private LinkedListNode> current; /// /// 当前游标所处的位置 @@ -55,6 +55,11 @@ public class MemoryBlockStream : Stream , ICloneable /// private bool disabled; + /// + /// 是否是可写的 + /// + private bool writable; + /// /// 偏移量 /// @@ -102,7 +107,7 @@ public override long Length /// public override bool CanWrite { - get { return !disabled; } + get { return !disabled && writable; } } /// @@ -122,26 +127,25 @@ public override bool CanRead } /// - /// 内存块数据流 + /// 构建一个内存块数据流实例 /// - /// 单个内存块的分块 - public MemoryBlockStream(int blockSize = 4096) - : this(long.MaxValue, blockSize) + protected MemoryBlockStream() { - + } /// - /// 克隆 + /// 内存块数据流 /// - /// - public object Clone() + /// 单个内存块的分块 + public MemoryBlockStream(int blockSize = 4096) + : this(long.MaxValue, blockSize) { - return null; + } /// - /// 内存块数据流 + /// 构建一个内存块数据流实例 /// /// 最大内存使用量 /// 单个内存块的分块 @@ -149,13 +153,45 @@ public MemoryBlockStream(long maxMemoryUsable, int blockSize = 4096) { BlockSize = blockSize; MaxMemoryUsable = Math.Max(maxMemoryUsable, blockSize); - storage = new LinkedList(); + storage = new LinkedList>(); length = 0; position = 0; disabled = false; + writable = true; current = null; } + /// + /// 克隆一个只读流 + /// + /// + public object Clone() + { + return new MemoryBlockStream + { + storage = storage, + BlockSize = BlockSize, + MaxMemoryUsable = MaxMemoryUsable, + length = length, + position = 0, + disabled = false, + writable = false, + current = storage.First + }; + } + + /// + /// 获取缓冲区 + /// + /// 获取缓冲区 + public byte[] GetBuffer() + { + AssertDisabled(); + + var buffer = new byte[length]; + var data = GetNearstBlockStartOffset(length); + } + /// /// 清除当前流的缓冲区 /// @@ -204,7 +240,11 @@ public override long Seek(long offset, SeekOrigin origin) throw new IOException("seek position less than 0"); } - EnsureStorage(tempPosition); + if (tempPosition > length) + { + throw new IOException("seek position is large then length(" + length + ")"); + } + position = tempPosition; RefreshCurrentNode(position); return position; @@ -230,6 +270,34 @@ public override void SetLength(long value) /// 需要写入的长度 public override void Write(byte[] buffer, int offset, int count) { + Guard.Requires(buffer != null); + Guard.Requires(offset >= 0); + Guard.Requires(count >= 0); + AssertDisabled(); + + EnsureStorage(Length + count); + + var nearstBlockStartOffset = GetNearstBlockStartOffset(position); + var nearstBlockEndOffset = nearstBlockStartOffset + BlockSize; + var nearstBlockCurrentOffset = (int) (position - nearstBlockStartOffset); + + if (count < nearstBlockEndOffset - position) + { + // 如果当前区块可以写入全部的数据 + Buffer.BlockCopy(buffer, offset, current.Value.Array, nearstBlockCurrentOffset, count); + count = 0; + } + else + { + var blockFreeSize = (int) (nearstBlockEndOffset - position); + Buffer.BlockCopy(buffer, offset, current.Value.Array, nearstBlockCurrentOffset, blockFreeSize); + count -= blockFreeSize; + offset += blockFreeSize; + current = current.Next; + position += count; + Guard.Requires(current != null); + } + } @@ -245,6 +313,25 @@ public override int Read(byte[] buffer, int offset, int count) return 0; } + /// + /// 释放资源 + /// + /// 是否进行释放 + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + disabled = true; + } + } + finally + { + base.Dispose(disposing); + } + } + /// /// 计算规定值最近的二的次幂的容量 /// @@ -267,13 +354,40 @@ private static int GetPrime(int min) return result; } + /// + /// 创建缓冲区 + /// + /// 缓冲区 + protected virtual ArraySegment CreateBuffer(int blockSize) + { + return new ArraySegment(new byte[blockSize]); + } + + /// + /// 释放缓冲区 + /// + /// + protected virtual void ReleaseBuffer(ArraySegment buffer) + { + + } + /// /// 刷新当前的游标节点 /// /// 偏移量 private void RefreshCurrentNode(long position) { - // 将current 刷新至position可读节点 + var nodeIndex = position / BlockSize; + LinkedListNode> node = null; + + do + { + Guard.Requires(node == null || node.Next != null); + node = (node == null) ? storage.First : node.Next; + } while (nodeIndex-- > 0); + + current = node; } /// @@ -283,6 +397,34 @@ private void RefreshCurrentNode(long position) private void EnsureStorage(long value) { AssertMemoryUseable(value); + + if (value <= 0) + { + return; + } + + var minBlockCount = (value / BlockSize) + 1; + if (storage.Count >= minBlockCount) + { + return; + } + + var needsBlock = minBlockCount - storage.Count; + + while (needsBlock-- > 0) + { + storage.AddLast(CreateBuffer(BlockSize)); + } + } + + /// + /// 获取最近区块的写入点 + /// + /// 基础偏移量 + /// 最近区块的起始偏移量 + private long GetNearstBlockStartOffset(long position) + { + return position / BlockSize * BlockSize; } /// @@ -296,6 +438,17 @@ private void AssertDisabled() } } + /// + /// 断言是否能够写入 + /// + private void AssertWritable() + { + if (!writable) + { + throw new NotSupportedException("Not supported writable"); + } + } + /// /// 断言内存使用 /// From b72ceab7e9342545f785d1bf36cf828304d84cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Wed, 19 Sep 2018 16:46:30 +0800 Subject: [PATCH 04/11] bug fixed #34 --- .../CatLib.Core.NetStandard.csproj | 5 +- .../CatLib.Core.Tests.csproj | 1 + .../Support/SortSet/SortSetTests.cs | 43 +- src/CatLib.Core/CatLib.Core.csproj | 4 +- src/CatLib.Core/Support/SortSet/SortSet.cs | 19 +- .../Support/Stream/MemoryBlockStream.cs | 465 ------------------ 6 files changed, 46 insertions(+), 491 deletions(-) delete mode 100644 src/CatLib.Core/Support/Stream/MemoryBlockStream.cs diff --git a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj index 8ca83e8..cab410f 100644 --- a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj +++ b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj @@ -104,7 +104,9 @@ - + + + @@ -136,6 +138,7 @@ + diff --git a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj index e3277d3..62ea2e7 100644 --- a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj +++ b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj @@ -51,6 +51,7 @@ + diff --git a/src/CatLib.Core.Tests/Support/SortSet/SortSetTests.cs b/src/CatLib.Core.Tests/Support/SortSet/SortSetTests.cs index 8b33683..68702d3 100644 --- a/src/CatLib.Core.Tests/Support/SortSet/SortSetTests.cs +++ b/src/CatLib.Core.Tests/Support/SortSet/SortSetTests.cs @@ -58,25 +58,36 @@ public void TestRandValue() } [TestMethod] + public void TestRank() { - var sortSets = new SortSet(); + var n = 100; + while (n-- > 0) + { + var sortSets = new SortSet(); + + sortSets.Add(1000, 85); + sortSets.Add(999, 75); + sortSets.Add(998, 185); + sortSets.Add(997, 85); + sortSets.Add(996, 185); + sortSets.Add(995, 85); + + Assert.AreEqual(1, sortSets.GetRank(995)); + Assert.AreEqual(995, sortSets.GetElementByRank(1)); + Assert.AreEqual(997, sortSets.GetElementByRank(2)); + Assert.AreEqual(1000, sortSets.GetElementByRank(3)); + Assert.AreEqual(996, sortSets.GetElementByRank(4)); + Assert.AreEqual(998, sortSets.GetElementByRank(5)); - sortSets.Add(1000, 85); - sortSets.Add(999, 75); - sortSets.Add(998, 185); - sortSets.Add(997, 85); - sortSets.Add(996, 185); - sortSets.Add(995, 85); - - Assert.AreEqual(1, sortSets.GetRank(995)); - Assert.AreEqual(995, sortSets.GetElementByRank(1)); - Assert.AreEqual(997, sortSets.GetElementByRank(2)); - Assert.AreEqual(1000, sortSets.GetElementByRank(3)); - Assert.AreEqual(996, sortSets.GetElementByRank(4)); - Assert.AreEqual(998, sortSets.GetElementByRank(5)); - - Assert.AreEqual(3, sortSets.GetRangeCount(80, 90)); + var i = 100; + var faild = 0; + while (i-- > 0) + { + Assert.AreEqual(3, sortSets.GetRangeCount(80, 90)); + } + Console.WriteLine(faild); + } } [TestMethod] diff --git a/src/CatLib.Core/CatLib.Core.csproj b/src/CatLib.Core/CatLib.Core.csproj index 87acb96..d55ee00 100644 --- a/src/CatLib.Core/CatLib.Core.csproj +++ b/src/CatLib.Core/CatLib.Core.csproj @@ -85,7 +85,9 @@ - + + + diff --git a/src/CatLib.Core/Support/SortSet/SortSet.cs b/src/CatLib.Core/Support/SortSet/SortSet.cs index 5b27a71..1a1e17f 100644 --- a/src/CatLib.Core/Support/SortSet/SortSet.cs +++ b/src/CatLib.Core/Support/SortSet/SortSet.cs @@ -472,8 +472,8 @@ public int GetRangeCount(TScore start, TScore end) Guard.Requires(end != null); Guard.Requires(Compare(start, end) <= 0); - int rank = 0, bakRank = 0; - SkipNode bakCursor = null; + int rank = 0, leftRank = 0; + SkipNode leftCursor = null; var isRight = false; var cursor = header; @@ -490,13 +490,15 @@ public int GetRangeCount(TScore start, TScore end) cursor = cursor.Level[i].Forward; } - if (bakCursor != null) + if (leftCursor != null) { continue; } - bakCursor = cursor; - bakRank = rank; + // 设定最左边最高层的跳跃游标和排名 + // 之后将由这个游标来开始向下查找 + leftCursor = cursor; + leftRank = rank; } if (isRight) @@ -504,11 +506,12 @@ public int GetRangeCount(TScore start, TScore end) continue; } - cursor = bakCursor; - rank ^= bakRank ^= rank ^= bakRank; + cursor = leftCursor; + leftRank ^= (rank ^= leftRank); + rank ^= leftRank; } while (isRight = !isRight); - return Math.Max(0, rank - bakRank); + return Math.Max(0, rank - leftRank); } /// diff --git a/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs b/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs deleted file mode 100644 index 85d9293..0000000 --- a/src/CatLib.Core/Support/Stream/MemoryBlockStream.cs +++ /dev/null @@ -1,465 +0,0 @@ -/* - * This file is part of the CatLib package. - * - * (c) Yu Bin - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Document: http://catlib.io/ - */ - -using System; -using System.Collections.Generic; -using System.IO; - -namespace CatLib -{ - /// - /// 内存块数据流 - /// - public class MemoryBlockStream : Stream , ICloneable - { - /// - /// 最大内存使用量 - /// - public long MaxMemoryUsable { get; private set; } - - /// - /// 单个内存块的大小 - /// - public int BlockSize { get; private set; } - - /// - /// 存储的数据 - /// - private LinkedList> storage; - - /// - /// 当前游标所处的内存区块 - /// - private LinkedListNode> current; - - /// - /// 当前游标所处的位置 - /// - private long position; - - /// - /// 数据的长度 - /// - private long length; - - /// - /// 是否已经被释放了 - /// - private bool disabled; - - /// - /// 是否是可写的 - /// - private bool writable; - - /// - /// 偏移量 - /// - public override long Position - { - get - { - AssertDisabled(); - return position; - } - set - { - AssertDisabled(); - Guard.Requires(value >= 0); - position = value; - } - } - - /// - /// 总内存使用量 - /// - public long Capacity - { - get - { - AssertDisabled(); - return storage.Count * MaxMemoryUsable; - } - } - - /// - /// 数据的长度 - /// - public override long Length - { - get - { - AssertDisabled(); - return length; - } - } - - /// - /// 是否是可以写入数据的 - /// - public override bool CanWrite - { - get { return !disabled && writable; } - } - - /// - /// 是否可以进行游标偏移 - /// - public override bool CanSeek - { - get { return !disabled; } - } - - /// - /// 是否可以读取数据 - /// - public override bool CanRead - { - get { return !disabled; } - } - - /// - /// 构建一个内存块数据流实例 - /// - protected MemoryBlockStream() - { - - } - - /// - /// 内存块数据流 - /// - /// 单个内存块的分块 - public MemoryBlockStream(int blockSize = 4096) - : this(long.MaxValue, blockSize) - { - - } - - /// - /// 构建一个内存块数据流实例 - /// - /// 最大内存使用量 - /// 单个内存块的分块 - public MemoryBlockStream(long maxMemoryUsable, int blockSize = 4096) - { - BlockSize = blockSize; - MaxMemoryUsable = Math.Max(maxMemoryUsable, blockSize); - storage = new LinkedList>(); - length = 0; - position = 0; - disabled = false; - writable = true; - current = null; - } - - /// - /// 克隆一个只读流 - /// - /// - public object Clone() - { - return new MemoryBlockStream - { - storage = storage, - BlockSize = BlockSize, - MaxMemoryUsable = MaxMemoryUsable, - length = length, - position = 0, - disabled = false, - writable = false, - current = storage.First - }; - } - - /// - /// 获取缓冲区 - /// - /// 获取缓冲区 - public byte[] GetBuffer() - { - AssertDisabled(); - - var buffer = new byte[length]; - var data = GetNearstBlockStartOffset(length); - } - - /// - /// 清除当前流的缓冲区 - /// - public override void Flush() - { - // 只有存在数据落地或者转移的情况下此函数才有效 - // 由于是内存缓存,所以这里我们忽略这个函数 - } - - /// - /// 偏移游标到指定位置 - /// - /// 偏移量 - /// 偏移方向 - /// 新的位置 - public override long Seek(long offset, SeekOrigin origin) - { - Guard.Requires(offset >= 0); - AssertDisabled(); - - long tempPosition; - switch (origin) - { - case SeekOrigin.Begin: - { - tempPosition = offset; - break; - } - case SeekOrigin.Current: - { - tempPosition = unchecked(position + offset); - - break; - } - case SeekOrigin.End: - { - tempPosition = unchecked(length + offset); - break; - } - default: - throw new ArgumentException("Unknow SeekOrigin"); - } - - if (tempPosition < 0) - { - throw new IOException("seek position less than 0"); - } - - if (tempPosition > length) - { - throw new IOException("seek position is large then length(" + length + ")"); - } - - position = tempPosition; - RefreshCurrentNode(position); - return position; - } - - /// - /// 设定长度 - /// - /// 新的长度值 - public override void SetLength(long value) - { - Guard.Requires(value >= 0); - AssertDisabled(); - EnsureStorage(value); - length = value; - } - - /// - /// 写入数据 - /// - /// 需要写入的字节流 - /// 字节流的起始位置 - /// 需要写入的长度 - public override void Write(byte[] buffer, int offset, int count) - { - Guard.Requires(buffer != null); - Guard.Requires(offset >= 0); - Guard.Requires(count >= 0); - AssertDisabled(); - - EnsureStorage(Length + count); - - var nearstBlockStartOffset = GetNearstBlockStartOffset(position); - var nearstBlockEndOffset = nearstBlockStartOffset + BlockSize; - var nearstBlockCurrentOffset = (int) (position - nearstBlockStartOffset); - - if (count < nearstBlockEndOffset - position) - { - // 如果当前区块可以写入全部的数据 - Buffer.BlockCopy(buffer, offset, current.Value.Array, nearstBlockCurrentOffset, count); - count = 0; - } - else - { - var blockFreeSize = (int) (nearstBlockEndOffset - position); - Buffer.BlockCopy(buffer, offset, current.Value.Array, nearstBlockCurrentOffset, blockFreeSize); - count -= blockFreeSize; - offset += blockFreeSize; - current = current.Next; - position += count; - Guard.Requires(current != null); - } - - - } - - /// - /// 读取数据到指定缓冲区 - /// - /// 指定缓冲区 - /// 缓冲区的起始位置 - /// 需要读取的长度 - /// 实际读取的长度 - public override int Read(byte[] buffer, int offset, int count) - { - return 0; - } - - /// - /// 释放资源 - /// - /// 是否进行释放 - protected override void Dispose(bool disposing) - { - try - { - if (disposing) - { - disabled = true; - } - } - finally - { - base.Dispose(disposing); - } - } - - /// - /// 计算规定值最近的二的次幂的容量 - /// - /// 规定值 - /// 容量 - private static int GetPrime(int min) - { - min = Math.Max(0, min); - - var result = 8192; - for (var i = 2; i < int.MaxValue; i = i << 1) - { - if (i >= min) - { - result = i; - break; - } - } - - return result; - } - - /// - /// 创建缓冲区 - /// - /// 缓冲区 - protected virtual ArraySegment CreateBuffer(int blockSize) - { - return new ArraySegment(new byte[blockSize]); - } - - /// - /// 释放缓冲区 - /// - /// - protected virtual void ReleaseBuffer(ArraySegment buffer) - { - - } - - /// - /// 刷新当前的游标节点 - /// - /// 偏移量 - private void RefreshCurrentNode(long position) - { - var nodeIndex = position / BlockSize; - LinkedListNode> node = null; - - do - { - Guard.Requires(node == null || node.Next != null); - node = (node == null) ? storage.First : node.Next; - } while (nodeIndex-- > 0); - - current = node; - } - - /// - /// 保障需要占用的内存存储空间 - /// - /// 需要占用的存储空间 - private void EnsureStorage(long value) - { - AssertMemoryUseable(value); - - if (value <= 0) - { - return; - } - - var minBlockCount = (value / BlockSize) + 1; - if (storage.Count >= minBlockCount) - { - return; - } - - var needsBlock = minBlockCount - storage.Count; - - while (needsBlock-- > 0) - { - storage.AddLast(CreateBuffer(BlockSize)); - } - } - - /// - /// 获取最近区块的写入点 - /// - /// 基础偏移量 - /// 最近区块的起始偏移量 - private long GetNearstBlockStartOffset(long position) - { - return position / BlockSize * BlockSize; - } - - /// - /// 断言是否已经被释放 - /// - private void AssertDisabled() - { - if (disabled) - { - throw new ObjectDisposedException(null, "[" + GetType() + "] Stream is closed."); - } - } - - /// - /// 断言是否能够写入 - /// - private void AssertWritable() - { - if (!writable) - { - throw new NotSupportedException("Not supported writable"); - } - } - - /// - /// 断言内存使用 - /// - /// 内存占用 - private void AssertMemoryUseable(long value) - { - if (value > MaxMemoryUsable) - { - throw new OutOfMemoryException("Memory exceeds usage limit " + (MaxMemoryUsable / 1048576) + " MB"); - } - } - } -} - From 1d15b3fef90960220335bdc168ffcf5a6f05074d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Wed, 19 Sep 2018 17:34:08 +0800 Subject: [PATCH 05/11] storage stream update --- CatLib.Core.sln.DotSettings.user | 4 + .../CatLib.Core.NetStandard.csproj | 4 +- .../Support/Storage/MemoryStorageTests.cs | 139 ++++++ src/CatLib.Core/CatLib.Core.csproj | 2 +- src/CatLib.Core/Support/Storage/IStorage.cs | 64 +++ .../Support/Storage/MemoryStorage.cs | 458 ++++++++++++++++++ .../Support/Stream/StorageStream.cs | 302 ++++++++++++ 7 files changed, 970 insertions(+), 3 deletions(-) create mode 100644 CatLib.Core.sln.DotSettings.user create mode 100644 src/CatLib.Core.Tests/Support/Storage/MemoryStorageTests.cs create mode 100644 src/CatLib.Core/Support/Storage/IStorage.cs create mode 100644 src/CatLib.Core/Support/Storage/MemoryStorage.cs create mode 100644 src/CatLib.Core/Support/Stream/StorageStream.cs diff --git a/CatLib.Core.sln.DotSettings.user b/CatLib.Core.sln.DotSettings.user new file mode 100644 index 0000000..4997d71 --- /dev/null +++ b/CatLib.Core.sln.DotSettings.user @@ -0,0 +1,4 @@ + + <AssemblyExplorer> + <Assembly Path="D:\Software\VS2017\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll" /> +</AssemblyExplorer> \ No newline at end of file diff --git a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj index cab410f..5c0b3ab 100644 --- a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj +++ b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj @@ -106,7 +106,7 @@ - + @@ -135,9 +135,9 @@ + - diff --git a/src/CatLib.Core.Tests/Support/Storage/MemoryStorageTests.cs b/src/CatLib.Core.Tests/Support/Storage/MemoryStorageTests.cs new file mode 100644 index 0000000..4aa4493 --- /dev/null +++ b/src/CatLib.Core.Tests/Support/Storage/MemoryStorageTests.cs @@ -0,0 +1,139 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CatLib +{ + [TestClass] + public class MemoryStorageTests + { + [TestMethod] + public void TestWrite() + { + // Append() is alias as Write + var storage = new MemoryStorage(4); + storage.Append(Encoding.Default.GetBytes("hello world"), 0, 11); + var data = new byte[16]; + Assert.AreEqual(11, storage.Read(data, 0, data.Length)); + Assert.AreEqual("hello world", Encoding.Default.GetString(Arr.Slice(data, 0, 11))); + } + + [TestMethod] + public void TestAppend() + { + var storage = new MemoryStorage(4); + storage.Append(Encoding.Default.GetBytes("hello world"), 0, 11); + storage.Append(Encoding.Default.GetBytes("1"), 0, 1); + var data = new byte[16]; + Assert.AreEqual(12, storage.Read(data, 0, data.Length)); + Assert.AreEqual("hello world1", Encoding.Default.GetString(Arr.Slice(data, 0, 12))); + + storage.Append(Encoding.Default.GetBytes("2"), 0, 1); + Assert.AreEqual(13, storage.Read(data, 0, data.Length)); + Assert.AreEqual("hello world12", Encoding.Default.GetString(Arr.Slice(data, 0, 13))); + } + + [TestMethod] + public void TestBlockAutomaticExpansion() + { + var storage = new MemoryStorage(4, 2); + storage.Append(Encoding.Default.GetBytes("12345678"), 0, 8); + storage.Append(Encoding.Default.GetBytes("9"), 0, 1); + + var data = new byte[16]; + Assert.AreEqual(9, storage.Read(data, 0, data.Length)); + Assert.AreEqual("123456789", Encoding.Default.GetString(Arr.Slice(data, 0, 9))); + } + + [TestMethod] + [ExpectedException(typeof(ObjectDisposedException))] + public void TestDispose() + { + MemoryStorage storage; + using (storage = new MemoryStorage(4, 2)) + { + storage.Append(Encoding.Default.GetBytes("12345678"), 0, 8); + } + + var data = new byte[16]; + storage.Read(data, 0, data.Length); + } + + [TestMethod] + [ExpectedException(typeof(ObjectDisposedException))] + public void TestDispose2() + { + MemoryStorage storage; + using (storage = new MemoryStorage(4, 2)) + { + + } + storage.Append(Encoding.Default.GetBytes("12345678"), 0, 8); + } + + [TestMethod] + public void TestJumpWrite() + { + var storage = new MemoryStorage(4, 2); + storage.Append(Encoding.Default.GetBytes("1234"), 0, 4); + storage.Write(Encoding.Default.GetBytes("5678"), 0, 4, 13); + Assert.AreEqual(17, storage.Length); + + var data = new byte[] + { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + }; + Assert.AreEqual(17, storage.Read(data, 0, data.Length)); + Assert.AreEqual("1234\0\0\0\0\0\0\0\0\05678", Encoding.Default.GetString(Arr.Slice(data, 0, 17))); + } + + [TestMethod] + [ExpectedException(typeof(OutOfMemoryException))] + public void TestOutOfMemory() + { + var storage = new MemoryStorage(8, 4, 2); + try + { + storage.Append(Encoding.Default.GetBytes("12345678"), 0, 8); + } + catch (OutOfMemoryException) + { + + } + + storage.Append(Encoding.Default.GetBytes("1"), 0, 1); + } + + [TestMethod] + public void TestDoubleDispose() + { + var storage = new MemoryStorage(8, 4, 2); + Assert.AreEqual(false, storage.Disabled); + storage.Dispose(); + Assert.AreEqual(true, storage.Disabled); + storage.Dispose(); + Assert.AreEqual(true, storage.Disabled); + } + + [TestMethod] + public void TestGetLocker() + { + using (var storage = new MemoryStorage(8, 4, 2)) + { + Assert.AreNotEqual(null, storage.Locker); + } + } + } +} diff --git a/src/CatLib.Core/CatLib.Core.csproj b/src/CatLib.Core/CatLib.Core.csproj index d55ee00..ff2c583 100644 --- a/src/CatLib.Core/CatLib.Core.csproj +++ b/src/CatLib.Core/CatLib.Core.csproj @@ -86,7 +86,7 @@ - + diff --git a/src/CatLib.Core/Support/Storage/IStorage.cs b/src/CatLib.Core/Support/Storage/IStorage.cs new file mode 100644 index 0000000..2a277b2 --- /dev/null +++ b/src/CatLib.Core/Support/Storage/IStorage.cs @@ -0,0 +1,64 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Threading; + +namespace CatLib +{ + /// + /// 基础存储 + /// + public interface IStorage : IDisposable + { + /// + /// 存储数据的长度 + /// + long Length { get; } + + /// + /// 读写锁 + /// + ReaderWriterLockSlim Locker { get; } + + /// + /// 是否应被释放 + /// + bool Disabled { get; } + + /// + /// 将指定缓冲区的数据写入到内存存储 + /// + /// 指定缓冲区 + /// 缓冲区的起始位置 + /// 缓冲区的长度 + /// 存储的起始位置 + void Write(byte[] buffer, int offset, int count, long index = 0); + + /// + /// 将指定缓冲区的数据追加到内存存储 + /// + /// 指定缓冲区 + /// 缓冲区的起始位置 + /// 写入的长度 + void Append(byte[] buffer, int offset, int count); + + /// + /// 读取数据到指定缓冲区 + /// + /// 指定缓冲区 + /// 缓冲区的起始位置 + /// 读取的长度 + /// 存储的起始位置 + /// 实际读取的长度 + int Read(byte[] buffer, int offset, int count, long index = 0); + } +} diff --git a/src/CatLib.Core/Support/Storage/MemoryStorage.cs b/src/CatLib.Core/Support/Storage/MemoryStorage.cs new file mode 100644 index 0000000..a8e07e4 --- /dev/null +++ b/src/CatLib.Core/Support/Storage/MemoryStorage.cs @@ -0,0 +1,458 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.Threading; + +namespace CatLib +{ + /// + /// 内存存储 + /// + public class MemoryStorage : IStorage + { + /// + /// 区块元数据 + /// + private class BlockMeta + { + /// + /// 区块下标 + /// + public int BlockIndex; + + /// + /// 起始偏移量 + /// + public long StartOffset; + + /// + /// 终止偏移量 + /// + public long EndOffset; + + /// + /// 存储数据 + /// + public ArraySegment Storage; + + /// + /// 获取相对偏移量 + /// + /// 全局偏移量 + /// 相对偏移量 + public int GetRelativeOffset(long position) + { + return (int)(position - StartOffset); + } + + /// + /// 获取当前区块剩余量 + /// + /// 全局偏移量 + /// 剩余量 + public int GetFreeSize(long position) + { + return (int)(EndOffset - position); + } + } + + /// + /// 当前存储最大内存使用量 + /// + public long MaxMemoryUsable { get; private set; } + + /// + /// 单个内存块的大小 + /// + public int BlockSize { get; private set; } + + /// + /// 数据长度 + /// + public long Length + { + get + { + AssertDisabled(); + return length; + } + } + + /// + /// 存储的数据 + /// + private BlockMeta[] storage; + + /// + /// 实际存储的长度 + /// + private long length; + + /// + /// 读写锁 + /// + private ReaderWriterLockSlim locker; + + /// + /// 读写锁 + /// + public ReaderWriterLockSlim Locker + { + get + { + AssertDisabled(); + return locker ?? (locker = new ReaderWriterLockSlim()); + } + } + + /// + /// 是否已经被释放的 + /// + public bool Disabled { get; private set; } + + /// + /// 构建一个新的内存存储 + /// + /// 块缓冲区大小 + /// 起始块数量 + public MemoryStorage(int blockBuffer = 4096, int capacity = 16) + : this(long.MaxValue, blockBuffer, capacity) + { + + } + + /// + /// 构建一个新的内存存储 + /// + /// 最大内存使用量 + /// 块缓冲区大小 + /// 起始块数量 + public MemoryStorage(long maxMemoryUsable, int blockBuffer = 4096, int capacity = 16) + { + MaxMemoryUsable = maxMemoryUsable; + BlockSize = blockBuffer; + length = 0; + storage = new BlockMeta[GetPrime(capacity)]; + } + + /// + /// 将指定缓冲区的数据写入到内存存储 + /// + /// 指定缓冲区 + /// 缓冲区的起始位置 + /// 缓冲区的长度 + /// 内存存储的起始位置 + public void Write(byte[] buffer, int offset, int count, long index = 0) + { + Guard.Requires(buffer != null); + Guard.Requires(index >= 0); + Guard.Requires(offset >= 0); + Guard.Requires(count >= 0); + Guard.Requires(buffer.Length - offset >= count); + AssertDisabled(); + + EnsureStorageBlock(index + count); + var blockMeta = GetBlockByPosition(index); + length = Math.Max(length, index); + + do + { + if (count <= blockMeta.EndOffset - index) + { + // 如果当前区块可以写入全部的数据 + Buffer.BlockCopy(buffer, offset, blockMeta.Storage.Array, blockMeta.GetRelativeOffset(index), + count); + length += count; + count = 0; + } + else + { + var blockFreeSize = blockMeta.GetFreeSize(index); + Buffer.BlockCopy(buffer, offset, blockMeta.Storage.Array, blockMeta.GetRelativeOffset(index), + blockFreeSize); + length += blockFreeSize; + index += blockFreeSize; + offset += blockFreeSize; + count -= blockFreeSize; + blockMeta = GetBlockByIndex(blockMeta.BlockIndex + 1); + } + } while (count > 0); + } + + /// + /// 将指定缓冲区的数据追加到内存存储 + /// + /// 指定缓冲区 + /// 缓冲区的起始位置 + /// 缓冲区的长度 + public void Append(byte[] buffer, int offset, int count) + { + Write(buffer, offset, count, length); + } + + /// + /// 读取数据到指定缓冲区 + /// + /// 指定缓冲区 + /// 缓冲区的起始位置 + /// 读取的长度 + /// 存储的起始位置 + /// 实际读取的长度 + public int Read(byte[] buffer, int offset, int count, long index = 0) + { + Guard.Requires(buffer != null); + Guard.Requires(index >= 0); + Guard.Requires(offset >= 0); + Guard.Requires(count >= 0); + Guard.Requires(buffer.Length - offset >= count); + AssertDisabled(); + + var read = 0; + var blockMeta = GetBlockByPosition(index, true); + + if (count + index > length) + { + count = (int)(length - index); + } + + if (count <= 0) + { + return 0; + } + + do + { + if (blockMeta == null) + { + // 如果是跳空块那么直接给定数据 + Array.Clear(buffer, offset, BlockSize); + read += BlockSize; + index += BlockSize; + offset += BlockSize; + count -= BlockSize; + blockMeta = GetBlockByPosition(index, true); + continue; + } + + var startIndex = blockMeta.GetRelativeOffset(index); + var blockFreeSize = blockMeta.GetFreeSize(index); + + if (count <= blockFreeSize) + { + Buffer.BlockCopy(blockMeta.Storage.Array, startIndex, buffer, offset, count); + read += count; + count = 0; + } + else + { + Buffer.BlockCopy(blockMeta.Storage.Array, startIndex, buffer, offset, blockFreeSize); + read += blockFreeSize; + index += blockFreeSize; + offset += blockFreeSize; + count -= blockFreeSize; + blockMeta = GetBlockByIndex(blockMeta.BlockIndex + 1, true); + } + } while (count > 0); + + return read; + } + + /// + /// 获取指定位置的区块下标 + /// + /// 指定位置 + /// 是否允许为空 + /// 区块数据 + private BlockMeta GetBlockByPosition(long position, bool allowNull = false) + { + return GetBlockByIndex((int)(position / BlockSize)); + } + + /// + /// 获取指定区块下标的区块数据 + /// + /// 区块下标 + /// 是否允许为空 + /// 区块数据 + private BlockMeta GetBlockByIndex(int index, bool allowNull = false) + { + Guard.Requires(index < storage.Length); + return storage[index] ?? (allowNull ? null : (storage[index] = CreateBlock(BlockSize, index))); + } + + /// + /// GC回收时 + /// + ~MemoryStorage() + { + Dispose(!Disabled); + } + + /// + /// 释放内存存储 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 创建缓冲区 + /// + /// 缓冲区 + private BlockMeta CreateBlock(int blockSize, int index) + { + return new BlockMeta + { + BlockIndex = index, + StartOffset = index * blockSize, + EndOffset = (index + 1) * blockSize, + Storage = CreateBuffer(blockSize) + }; + } + + /// + /// 创建缓冲区 + /// + /// 缓冲区 + protected virtual ArraySegment CreateBuffer(int blockSize) + { + return new ArraySegment(new byte[blockSize]); + } + + /// + /// 释放缓冲区 + /// + /// 区块元数据 + private void ReleaseBlock(BlockMeta blockMeta) + { + if (blockMeta != null) + { + ReleaseBuffer(blockMeta.Storage); + } + } + + /// + /// 释放缓冲区 + /// + /// 缓冲大小 + protected virtual void ReleaseBuffer(ArraySegment buffer) + { + + } + + /// + /// 保障存储块满足存储条件 + /// + /// 总共需要占用的空间 + private void EnsureStorageBlock(long value) + { + AssertMemoryUseable(value); + + if (value <= 0) + { + return; + } + + var minBlockCount = (int) ((value / BlockSize) + ((value % BlockSize) == 0 ? 0 : 1)); + if (storage.Length >= minBlockCount) + { + return; + } + + var newStorage = new BlockMeta[GetPrime(minBlockCount)]; + Array.Copy(storage, 0, newStorage, 0, storage.Length); + storage = newStorage; + } + + /// + /// 释放内存存储 + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!disposing || Disabled) + { + return; + } + + Disabled = true; + foreach (var block in storage) + { + ReleaseBlock(block); + } + storage = null; + + if (locker != null) + { + locker.Dispose(); + locker = null; + } + } + + /// + /// 计算规定值最近的二的次幂的容量 + /// + /// 规定值 + /// 容量 + private static int GetPrime(int min) + { + min = Math.Max(0, min); + + var result = 8192; + for (var i = 2; i < int.MaxValue; i = i << 1) + { + if (i >= min) + { + result = i; + break; + } + } + + return result; + } + + /// + /// 断言是否已经被释放 + /// + private void AssertDisabled() + { + if (Disabled) + { + throw new ObjectDisposedException(null, "[" + GetType() + "] Stream is closed."); + } + } + + /// + /// 断言内存使用 + /// + /// 内存占用 + private void AssertMemoryUseable(long value) + { + if (value > MaxMemoryUsable) + { + if (MaxMemoryUsable >= 1048576) + { + throw new OutOfMemoryException("Memory exceeds usage limit " + (MaxMemoryUsable / 1048576) + " MB"); + } + + if (MaxMemoryUsable >= 1024) + { + throw new OutOfMemoryException("Memory exceeds usage limit " + (MaxMemoryUsable / 1024) + " KB"); + } + + throw new OutOfMemoryException("Memory exceeds usage limit " + MaxMemoryUsable + " bit"); + } + } + } +} diff --git a/src/CatLib.Core/Support/Stream/StorageStream.cs b/src/CatLib.Core/Support/Stream/StorageStream.cs new file mode 100644 index 0000000..109beae --- /dev/null +++ b/src/CatLib.Core/Support/Stream/StorageStream.cs @@ -0,0 +1,302 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System; +using System.IO; + +namespace CatLib +{ + /// + /// 存储数据流 + /// + public class StorageStream : Stream + { + /// + /// 当前游标所处的位置 + /// + private long position; + + /// + /// 是否已经被释放了 + /// + private bool disabled; + + /// + /// 是否是可写的 + /// + private readonly bool writable; + + /// + /// 存储数据 + /// + private readonly IStorage storage; + + /// + /// 偏移量 + /// + public override long Position + { + get + { + AssertDisabled(); + return position; + } + set + { + AssertDisabled(); + Guard.Requires(value >= 0); + position = value; + } + } + + /// + /// 数据的长度 + /// + public override long Length + { + get + { + AssertDisabled(); + return storage.Length; + } + } + + /// + /// 是否是可以写入数据的 + /// + public override bool CanWrite + { + get { return !Disposed && writable; } + } + + /// + /// 是否可以进行游标偏移 + /// + public override bool CanSeek + { + get { return !Disposed; } + } + + /// + /// 是否可以读取数据 + /// + public override bool CanRead + { + get { return !Disposed; } + } + + /// + /// 是否已经被释放 + /// + protected bool Disposed + { + get { return disabled || storage.Disabled; } + } + + /// + /// 存储数据流 + /// + /// 单个内存块的分块 + /// 是否是可写的 + /// 锁超时时间 + public StorageStream(IStorage storage, bool writable = true, int timeout = 1000) + { + Guard.Requires(storage != null); + + this.storage = storage; + this.writable = writable; + position = 0; + disabled = false; + + if (writable) + { + if (storage.Locker.TryEnterWriteLock(timeout)) + { + throw GetOccupyException(); + } + } + else + { + if (storage.Locker.TryEnterReadLock(timeout)) + { + throw GetOccupyException(); + } + } + } + + /// + /// GC回收时 + /// + ~StorageStream() + { + Dispose(!disabled); + } + + /// + /// 偏移游标到指定位置 + /// + /// 偏移量 + /// 偏移方向 + /// 新的位置 + public override long Seek(long offset, SeekOrigin origin) + { + Guard.Requires(offset >= 0); + AssertDisabled(); + + long tempPosition; + switch (origin) + { + case SeekOrigin.Begin: + { + tempPosition = offset; + break; + } + case SeekOrigin.Current: + { + tempPosition = unchecked(position + offset); + + break; + } + case SeekOrigin.End: + { + tempPosition = unchecked(Length + offset); + break; + } + default: + throw new ArgumentException("Unknow SeekOrigin"); + } + + if (tempPosition < 0) + { + throw new IOException("seek position less than 0"); + } + + if (tempPosition > Length) + { + throw new IOException("seek position is large then length(" + Length + ")"); + } + + position = tempPosition; + return position; + } + + /// + /// 写入数据 + /// + /// 需要写入的字节流 + /// 字节流的起始位置 + /// 需要写入的长度 + public override void Write(byte[] buffer, int offset, int count) + { + AssertWritable(); + AssertDisabled(); + storage.Write(buffer, offset, count, position); + position += count; + } + + /// + /// 读取数据到指定缓冲区 + /// + /// 指定缓冲区 + /// 缓冲区的起始位置 + /// 需要读取的长度 + /// 实际读取的长度 + public override int Read(byte[] buffer, int offset, int count) + { + AssertDisabled(); + var read = storage.Read(buffer, offset, count, position); + position += read; + return read; + } + + /// + /// 设定长度 + /// + /// 新的长度值 + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// 清除当前流的缓冲区 + /// + public override void Flush() + { + // 只有存在数据落地或者转移的情况下此函数才有效 + // 由于是内存缓存,所以这里我们忽略这个函数 + } + + /// + /// 获取线程占用异常 + /// + /// 异常 + protected IOException GetOccupyException() + { + return new IOException("The resource is already occupied by other threads"); + } + + /// + /// 释放资源 + /// + /// 是否进行释放 + protected override void Dispose(bool disposing) + { + try + { + if (disposing) + { + disabled = true; + + if (storage.Disabled) + { + return; + } + + if (writable) + { + storage.Locker.ExitWriteLock(); + } + else + { + storage.Locker.ExitReadLock(); + } + } + } + finally + { + base.Dispose(disposing); + } + } + + /// + /// 断言是否已经被释放 + /// + private void AssertDisabled() + { + if (Disposed) + { + throw new ObjectDisposedException(null, "[" + GetType() + "] Stream is closed."); + } + } + + /// + /// 断言是否能够写入 + /// + private void AssertWritable() + { + if (!writable) + { + throw new NotSupportedException("Not supported writable"); + } + } + } +} \ No newline at end of file From d720d2c72d9a3329a0782eef6d38977ba01d6fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Wed, 19 Sep 2018 18:59:45 +0800 Subject: [PATCH 06/11] add unit test --- .../CatLib.Core.Tests.csproj | 1 + .../Support/Stream/StorageStreamTests.cs | 74 +++++++++++++++++++ .../Support/Storage/MemoryStorage.cs | 2 +- .../Support/Stream/StorageStream.cs | 38 +++++----- 4 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs diff --git a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj index 62ea2e7..3fb9c76 100644 --- a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj +++ b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj @@ -52,6 +52,7 @@ + diff --git a/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs b/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs new file mode 100644 index 0000000..f8cca4e --- /dev/null +++ b/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs @@ -0,0 +1,74 @@ +/* + * This file is part of the CatLib package. + * + * (c) Yu Bin + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Document: http://catlib.io/ + */ + +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CatLib +{ + [TestClass] + public class StorageStreamTests + { + [TestMethod] + public void TestWrite() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + using (var readStream = new StorageStream(storage, false)) + { + var data = new byte[16]; + Assert.AreEqual(9, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("123456789", Encoding.Default.GetString(data, 0, 9)); + } + } + + [TestMethod] + public void TestReadMult() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + + StorageStream readStream1 = null; + StorageStream readStream2 = null; + + try + { + readStream1 = new StorageStream(storage, false); + readStream2 = new StorageStream(storage, false); + + var data = new byte[16]; + Assert.AreEqual(9, readStream1.Read(data, 0, data.Length)); + Assert.AreEqual("123456789", Encoding.Default.GetString(data, 0, 9)); + + Assert.AreEqual(9, readStream2.Read(data, 0, data.Length)); + Assert.AreEqual("123456789", Encoding.Default.GetString(data, 0, 9)); + } + finally + { + if (readStream1 != null) + { + readStream1.Dispose(); + } + + if (readStream2 != null) + { + readStream2.Dispose(); + } + } + } + } +} diff --git a/src/CatLib.Core/Support/Storage/MemoryStorage.cs b/src/CatLib.Core/Support/Storage/MemoryStorage.cs index a8e07e4..70c9350 100644 --- a/src/CatLib.Core/Support/Storage/MemoryStorage.cs +++ b/src/CatLib.Core/Support/Storage/MemoryStorage.cs @@ -110,7 +110,7 @@ public ReaderWriterLockSlim Locker get { AssertDisabled(); - return locker ?? (locker = new ReaderWriterLockSlim()); + return locker ?? (locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)); } } diff --git a/src/CatLib.Core/Support/Stream/StorageStream.cs b/src/CatLib.Core/Support/Stream/StorageStream.cs index 109beae..da6d965 100644 --- a/src/CatLib.Core/Support/Stream/StorageStream.cs +++ b/src/CatLib.Core/Support/Stream/StorageStream.cs @@ -51,9 +51,7 @@ public override long Position } set { - AssertDisabled(); - Guard.Requires(value >= 0); - position = value; + Seek(value, SeekOrigin.Begin); } } @@ -118,14 +116,14 @@ public StorageStream(IStorage storage, bool writable = true, int timeout = 1000) if (writable) { - if (storage.Locker.TryEnterWriteLock(timeout)) + if (!storage.Locker.TryEnterWriteLock(timeout)) { throw GetOccupyException(); } } else { - if (storage.Locker.TryEnterReadLock(timeout)) + if (!storage.Locker.TryEnterReadLock(timeout)) { throw GetOccupyException(); } @@ -252,23 +250,25 @@ protected override void Dispose(bool disposing) { try { - if (disposing) + if (!disposing) { - disabled = true; + return; + } - if (storage.Disabled) - { - return; - } + disabled = true; - if (writable) - { - storage.Locker.ExitWriteLock(); - } - else - { - storage.Locker.ExitReadLock(); - } + if (storage.Disabled) + { + return; + } + + if (writable) + { + storage.Locker.ExitWriteLock(); + } + else + { + storage.Locker.ExitReadLock(); } } finally From 17db34ddf93c4ca5e241dbc6cd05d16fe853a5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Thu, 20 Sep 2018 15:08:20 +0800 Subject: [PATCH 07/11] memory storage update --- .../Support/Stream/StorageStreamTests.cs | 200 ++++++++++++++++++ .../Support/Storage/MemoryStorage.cs | 12 +- .../Support/Stream/StorageStream.cs | 13 +- 3 files changed, 212 insertions(+), 13 deletions(-) diff --git a/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs b/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs index f8cca4e..30fae8f 100644 --- a/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs +++ b/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs @@ -9,6 +9,8 @@ * Document: http://catlib.io/ */ +using System; +using System.IO; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -70,5 +72,203 @@ public void TestReadMult() } } } + + [TestMethod] + public void TestAttr() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + Assert.AreEqual(true, stream.CanWrite); + } + + var readStream = new StorageStream(storage, false); + Assert.AreEqual(9, readStream.Length); + Assert.AreEqual(false, readStream.CanWrite); + Assert.AreEqual(true, readStream.CanRead); + Assert.AreEqual(true, readStream.CanSeek); + Assert.AreEqual(false, readStream.CanTimeout); + + readStream.Dispose(); + Assert.AreEqual(false, readStream.CanWrite); + Assert.AreEqual(false, readStream.CanRead); + Assert.AreEqual(false, readStream.CanSeek); + Assert.AreEqual(false, readStream.CanTimeout); + } + + [TestMethod] + public void TestSeek() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + + using (var readStream = new StorageStream(storage, false)) + { + var data = new byte[16]; + Assert.AreEqual(9, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("123456789", Encoding.Default.GetString(data, 0, 9)); + Assert.AreEqual(0, readStream.Read(data, 0, data.Length)); + + readStream.Seek(1, SeekOrigin.Begin); + Assert.AreEqual(8, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("23456789", Encoding.Default.GetString(data, 0, 8)); + + readStream.Seek(-1, SeekOrigin.Current); + Assert.AreEqual(1, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("9", Encoding.Default.GetString(data, 0, 1)); + + readStream.Seek(0, SeekOrigin.Begin); + Assert.AreEqual(9, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("123456789", Encoding.Default.GetString(data, 0, 9)); + + readStream.Seek(-1, SeekOrigin.End); + Assert.AreEqual(1, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("9", Encoding.Default.GetString(data, 0, 1)); + } + } + + [TestMethod] + public void TestSetPosition() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + + using (var readStream = new StorageStream(storage, false)) + { + var data = new byte[16]; + Assert.AreEqual(9, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("123456789", Encoding.Default.GetString(data, 0, 9)); + Assert.AreEqual(0, readStream.Read(data, 0, data.Length)); + + readStream.Position = 1; + Assert.AreEqual(8, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("23456789", Encoding.Default.GetString(data, 0, 8)); + } + } + + [TestMethod] + public void TestGetPosition() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + + using (var readStream = new StorageStream(storage, false)) + { + var data = new byte[16]; + Assert.AreEqual(9, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("123456789", Encoding.Default.GetString(data, 0, 9)); + Assert.AreEqual(0, readStream.Read(data, 0, data.Length)); + Assert.AreEqual(9, readStream.Position); + } + } + + [TestMethod] + [ExpectedException(typeof(IOException))] + public void TestSeekSmallZero() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + + using (var readStream = new StorageStream(storage, false)) + { + readStream.Seek(-1, SeekOrigin.Begin); + } + } + + [TestMethod] + [ExpectedException(typeof(IOException))] + public void TestSeekLargeLength() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + + using (var readStream = new StorageStream(storage, false)) + { + readStream.Seek(1, SeekOrigin.End); + } + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void TestSetLength() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.SetLength(0); + } + } + + [TestMethod] + [ExpectedException(typeof(ObjectDisposedException))] + public void TestStorageDispose() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + Assert.AreEqual(true, stream.CanWrite); + } + storage.Dispose(); + var a = storage.Length; + } + + [TestMethod] + [ExpectedException(typeof(ObjectDisposedException))] + public void TestDisposeStorage() + { + var storage = new MemoryStorage(); + storage.Dispose(); + var stream = new StorageStream(storage); + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void TestCannotWriteable() + { + var storage = new MemoryStorage(); + using (var stream = new StorageStream(storage, false)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + } + + [TestMethod] + public void TestDoubleDispose() + { + var storage = new MemoryStorage(); + StorageStream stream; + using (stream = new StorageStream(storage)) + { + stream.Write(Encoding.Default.GetBytes("123456789"), 0, 9); + } + stream.Dispose(); + } + + [TestMethod] + [ExpectedException(typeof(ObjectDisposedException))] + public void TestDisposeGetPosition() + { + var storage = new MemoryStorage(); + var stream = new StorageStream(storage, false); + stream.Dispose(); + var a = stream.Position; + } } } diff --git a/src/CatLib.Core/Support/Storage/MemoryStorage.cs b/src/CatLib.Core/Support/Storage/MemoryStorage.cs index 70c9350..685bba4 100644 --- a/src/CatLib.Core/Support/Storage/MemoryStorage.cs +++ b/src/CatLib.Core/Support/Storage/MemoryStorage.cs @@ -386,18 +386,18 @@ protected virtual void Dispose(bool disposing) return; } + if (locker != null) + { + locker.Dispose(); + locker = null; + } + Disabled = true; foreach (var block in storage) { ReleaseBlock(block); } storage = null; - - if (locker != null) - { - locker.Dispose(); - locker = null; - } } /// diff --git a/src/CatLib.Core/Support/Stream/StorageStream.cs b/src/CatLib.Core/Support/Stream/StorageStream.cs index da6d965..d3c5794 100644 --- a/src/CatLib.Core/Support/Stream/StorageStream.cs +++ b/src/CatLib.Core/Support/Stream/StorageStream.cs @@ -109,6 +109,11 @@ public StorageStream(IStorage storage, bool writable = true, int timeout = 1000) { Guard.Requires(storage != null); + if (storage.Disabled) + { + throw new ObjectDisposedException("storage", "Storage is disposed"); + } + this.storage = storage; this.writable = writable; position = 0; @@ -146,7 +151,6 @@ public StorageStream(IStorage storage, bool writable = true, int timeout = 1000) /// 新的位置 public override long Seek(long offset, SeekOrigin origin) { - Guard.Requires(offset >= 0); AssertDisabled(); long tempPosition; @@ -250,18 +254,13 @@ protected override void Dispose(bool disposing) { try { - if (!disposing) + if (!disposing || disabled) { return; } disabled = true; - if (storage.Disabled) - { - return; - } - if (writable) { storage.Locker.ExitWriteLock(); From bde2a64a1811636fc338bdfdc41fd2cb5b7de8c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Thu, 20 Sep 2018 15:32:18 +0800 Subject: [PATCH 08/11] bug fixed --- src/CatLib.Core/CatLib/Facade.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CatLib.Core/CatLib/Facade.cs b/src/CatLib.Core/CatLib/Facade.cs index 4382351..7ff6f4b 100644 --- a/src/CatLib.Core/CatLib/Facade.cs +++ b/src/CatLib.Core/CatLib/Facade.cs @@ -62,7 +62,7 @@ static Facade() /// public static TService Instance { - get { return Make(); } + get { return HasInstance ? instance : Resolve(); } } /// From 41af4ef67d7fe8ccafe6b704cfd4cb85649f9639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Thu, 20 Sep 2018 15:52:17 +0800 Subject: [PATCH 09/11] update --- src/CatLib.Core/Support/Util/StreamExtension.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/CatLib.Core/Support/Util/StreamExtension.cs b/src/CatLib.Core/Support/Util/StreamExtension.cs index d33c8e9..c0632bd 100644 --- a/src/CatLib.Core/Support/Util/StreamExtension.cs +++ b/src/CatLib.Core/Support/Util/StreamExtension.cs @@ -23,15 +23,7 @@ public static class StreamExtension /// 默认的缓冲区 /// [ThreadStatic] - private static readonly byte[] buffer; - - /// - /// Stream扩展函数 - /// - static StreamExtension() - { - buffer = new byte[4096]; - } + private static readonly byte[] buffer = new byte[4096]; /// /// 将当前流追加到目标流中 From e40b758f04cd050797a319aed18b86100f9b14af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Fri, 21 Sep 2018 18:50:30 +0800 Subject: [PATCH 10/11] bug fixed --- .../Support/Stream/StorageStreamTests.cs | 21 +++++++++++++++++++ .../Support/Storage/MemoryStorage.cs | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs b/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs index 30fae8f..23f8fea 100644 --- a/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs +++ b/src/CatLib.Core.Tests/Support/Stream/StorageStreamTests.cs @@ -270,5 +270,26 @@ public void TestDisposeGetPosition() stream.Dispose(); var a = stream.Position; } + + [TestMethod] + public void TestNotAppend() + { + var storage = new MemoryStorage(4, 4); + + var stream = new StorageStream(storage); + stream.Write(Encoding.Default.GetBytes("hello world"), 0, 11); + stream.Dispose(); + + stream = new StorageStream(storage); + stream.Write(Encoding.Default.GetBytes("hello 12345"), 0, 11); + + using (var readStream = new StorageStream(storage, false)) + { + var data = new byte[32]; + Assert.AreEqual(11, readStream.Read(data, 0, data.Length)); + Assert.AreEqual("hello 12345", Encoding.Default.GetString(data, 0, 11)); + Assert.AreEqual(0, readStream.Read(data, 0, data.Length)); + } + } } } diff --git a/src/CatLib.Core/Support/Storage/MemoryStorage.cs b/src/CatLib.Core/Support/Storage/MemoryStorage.cs index 685bba4..f439416 100644 --- a/src/CatLib.Core/Support/Storage/MemoryStorage.cs +++ b/src/CatLib.Core/Support/Storage/MemoryStorage.cs @@ -162,7 +162,6 @@ public void Write(byte[] buffer, int offset, int count, long index = 0) EnsureStorageBlock(index + count); var blockMeta = GetBlockByPosition(index); - length = Math.Max(length, index); do { @@ -171,7 +170,7 @@ public void Write(byte[] buffer, int offset, int count, long index = 0) // 如果当前区块可以写入全部的数据 Buffer.BlockCopy(buffer, offset, blockMeta.Storage.Array, blockMeta.GetRelativeOffset(index), count); - length += count; + index += count; count = 0; } else @@ -179,13 +178,14 @@ public void Write(byte[] buffer, int offset, int count, long index = 0) var blockFreeSize = blockMeta.GetFreeSize(index); Buffer.BlockCopy(buffer, offset, blockMeta.Storage.Array, blockMeta.GetRelativeOffset(index), blockFreeSize); - length += blockFreeSize; index += blockFreeSize; offset += blockFreeSize; count -= blockFreeSize; blockMeta = GetBlockByIndex(blockMeta.BlockIndex + 1); } } while (count > 0); + + length = Math.Max(length, index + count); } /// From 4dce8442fa73e599f4136a2d50fbaa6a1a05b1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=96=B5=E5=96=B5=E5=A4=A7=E4=BA=BA?= Date: Tue, 25 Sep 2018 10:34:55 +0800 Subject: [PATCH 11/11] update --- src/CatLib.Core.Tests/Properties/AssemblyInfo.cs | 4 ++-- src/CatLib.Core/Properties/AssemblyInfo.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs b/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs index cb1810d..f5b6759 100644 --- a/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs +++ b/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs @@ -25,5 +25,5 @@ [assembly: Guid("3c9f4024-910c-4881-a04d-34a6c3a09019")] -[assembly: AssemblyVersion("1.2.7.0")] -[assembly: AssemblyFileVersion("1.2.7.0")] +[assembly: AssemblyVersion("1.2.8.0")] +[assembly: AssemblyFileVersion("1.2.8.0")] diff --git a/src/CatLib.Core/Properties/AssemblyInfo.cs b/src/CatLib.Core/Properties/AssemblyInfo.cs index 604d6bc..a4a8b9b 100644 --- a/src/CatLib.Core/Properties/AssemblyInfo.cs +++ b/src/CatLib.Core/Properties/AssemblyInfo.cs @@ -26,8 +26,8 @@ [assembly: Guid("4204658e-81fd-4106-a347-890cd369c8a4")] -[assembly: AssemblyVersion("1.2.7.0")] -[assembly: AssemblyFileVersion("1.2.7.0")] +[assembly: AssemblyVersion("1.2.8.0")] +[assembly: AssemblyFileVersion("1.2.8.0")] [assembly: InternalsVisibleTo("Assembly-CSharp-Editor"), InternalsVisibleTo("Assembly-CSharp-Editor-firstpass"),