diff --git a/CatLib.Core.sln b/CatLib.Core.sln index 22d9a7b..1f1cacb 100644 --- a/CatLib.Core.sln +++ b/CatLib.Core.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2036 +VisualStudioVersion = 15.0.26430.4 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatLib.Core", "src\CatLib.Core\CatLib.Core.csproj", "{4204658E-81FD-4106-A347-890CD369C8A4}" EndProject diff --git a/README.md b/README.md index 8898278..37fe7c6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

+

@@ -28,7 +28,7 @@ **使用Nuget安装** ```PM -Install-Package CatLib.Core -Version 1.2.9 +Install-Package CatLib.Core -Version 1.2.10 ``` **直接下载发布版本** diff --git a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj index 606b700..b26650e 100644 --- a/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj +++ b/src/CatLib.Core.NetStandard/CatLib.Core.NetStandard.csproj @@ -106,6 +106,7 @@ + diff --git a/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj b/src/CatLib.Core.Tests/CatLib.Core.Tests.csproj index 3fb9c76..f52904b 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/Properties/AssemblyInfo.cs b/src/CatLib.Core.Tests/Properties/AssemblyInfo.cs index 3a549a0..3ac60bf 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.9.0")] -[assembly: AssemblyFileVersion("1.2.9.0")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.10.0")] diff --git a/src/CatLib.Core.Tests/Support/Container/ContainerTests.cs b/src/CatLib.Core.Tests/Support/Container/ContainerTests.cs index 519f57e..cba9cbe 100644 --- a/src/CatLib.Core.Tests/Support/Container/ContainerTests.cs +++ b/src/CatLib.Core.Tests/Support/Container/ContainerTests.cs @@ -742,7 +742,7 @@ public void TestOverflowParamNum() { container.Call(cls, "GetNumber", new object[256]); } - catch (Exception ex) + catch (Exception) { isThrow = true; } diff --git a/src/CatLib.Core.Tests/Support/Stream/PipelineStreamTests.cs b/src/CatLib.Core.Tests/Support/Stream/PipelineStreamTests.cs new file mode 100644 index 0000000..151d72e --- /dev/null +++ b/src/CatLib.Core.Tests/Support/Stream/PipelineStreamTests.cs @@ -0,0 +1,152 @@ +/* + * 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; +using System.Text; +using System.Threading; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CatLib.Core.Tests.Support.Stream +{ + [TestClass] + public class PipelineStreamTests + { + private PipelineStream stream; + + [TestMethod] + public void TestPipeline() + { + stream = new PipelineStream(256); + ThreadPool.QueueUserWorkItem(WriteThread); + + var wrote = false; + stream.OnWrote += (_) => + { + wrote = true; + }; + var data = new byte[100]; + int read; + var actual = new StringBuilder(); + var rand = new Random(); + while ((read = stream.Read(data, 0, data.Length)) != 0) + { + var str = Encoding.UTF8.GetString(data, 0, read); + actual.Append(str); + Thread.Sleep(rand.Next(1, 5)); + } + stream.Dispose(); + + var expected = new StringBuilder(); + for (var i = 0; i < 1000; i++) + { + expected.Append("0123456789"); + } + + Assert.AreEqual(expected.ToString(), actual.ToString()); + Assert.AreEqual(true, wrote); + } + + public void WriteThread(object obj) + { + var data = Encoding.UTF8.GetBytes("0123456789"); + for (var i = 0; i < 1000; i++) + { + stream.Write(data, 0, data.Length); + } + stream.Close(); + } + + [TestMethod] + [ExpectedException(typeof(ObjectDisposedException))] + public void TestClosedAndWrite() + { + var stream = new PipelineStream(256); + Assert.AreEqual(true, stream.CanWrite); + stream.Write(Encoding.UTF8.GetBytes("0123456789"), 0, 10); + stream.Close(); + Assert.AreEqual(false, stream.CanWrite); + Assert.AreEqual(true, stream.CanRead); + Assert.AreEqual(true, stream.Closed); + stream.Write(Encoding.UTF8.GetBytes("0123456789"), 0, 10); + } + + + [TestMethod] + [ExpectedException(typeof(ObjectDisposedException))] + public void TestDisposeAndWrite() + { + var stream = new PipelineStream(256); + Assert.AreEqual(true, stream.CanWrite); + Assert.AreEqual(false, stream.CanRead); + stream.Dispose(); + Assert.AreEqual(false, stream.CanWrite); + Assert.AreEqual(false, stream.CanRead); + stream.Write(Encoding.UTF8.GetBytes("0123456789"), 0, 10); + } + + [TestMethod] + public void TestCanReadCanWrite() + { + var stream = new PipelineStream(256); + stream.Write(Encoding.UTF8.GetBytes("0123456789"), 0, 10); + Assert.AreEqual(true, stream.CanWrite); + Assert.AreEqual(true, stream.CanRead); + } + + [TestMethod] + public void TestCanSeek() + { + var stream = new PipelineStream(256); + Assert.AreEqual(false, stream.CanSeek); + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void TestSeek() + { + var stream = new PipelineStream(256); + stream.Seek(0, SeekOrigin.Begin); + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void TestSetLength() + { + var stream = new PipelineStream(256); + stream.SetLength(0); + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void TestSetPosition() + { + var stream = new PipelineStream(256); + stream.Position = 10; + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void TestGetPosition() + { + var stream = new PipelineStream(256); + var pos = stream.Position; + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void TestGetLength() + { + var stream = new PipelineStream(256); + var length = stream.Length; + } + } +} diff --git a/src/CatLib.Core.Tests/Support/Template/ManagerTests.cs b/src/CatLib.Core.Tests/Support/Template/ManagerTests.cs index 23df793..644cc1f 100644 --- a/src/CatLib.Core.Tests/Support/Template/ManagerTests.cs +++ b/src/CatLib.Core.Tests/Support/Template/ManagerTests.cs @@ -51,7 +51,7 @@ public void TestReleaseExtendAndMake() return tmp = new TestManagerClass(); }); - cls.ReleaseExtend(); + cls.RemoveExtend(); ExceptionAssert.Throws(() => { diff --git a/src/CatLib.Core.Tests/Support/Template/SingleManagerTests.cs b/src/CatLib.Core.Tests/Support/Template/SingleManagerTests.cs index 5affb4f..a8760a0 100644 --- a/src/CatLib.Core.Tests/Support/Template/SingleManagerTests.cs +++ b/src/CatLib.Core.Tests/Support/Template/SingleManagerTests.cs @@ -129,7 +129,7 @@ public void TestReleaseExtend() manager.Extend(() => new InterfaceImpl(), "hello"); Assert.AreEqual(true, manager.ContainsExtend("hello")); - manager.ReleaseExtend("hello"); + manager.RemoveExtend("hello"); Assert.AreEqual(false, manager.ContainsExtend()); Assert.AreEqual(false, manager.ContainsExtend("hello")); } diff --git a/src/CatLib.Core.Tests/Support/Util/ArrTests.cs b/src/CatLib.Core.Tests/Support/Util/ArrTests.cs index e719e6a..a87d5a1 100644 --- a/src/CatLib.Core.Tests/Support/Util/ArrTests.cs +++ b/src/CatLib.Core.Tests/Support/Util/ArrTests.cs @@ -33,6 +33,36 @@ public void TestMerge() Assert.AreEqual(3, newArr.Length); } + [TestMethod] + public void TestMergeNull() + { + var arr1 = new[] { "1", "2" }; + string[] arr2 = null; + var arr3 = new[] { "3" }; + var newArr = Arr.Merge(arr1, arr2, arr3); + Assert.AreEqual(3, newArr.Length); + var i = 0; + foreach (var result in newArr) + { + Assert.AreEqual((++i).ToString(), result); + } + } + + [TestMethod] + public void TestMergeEmpty() + { + var arr1 = new[] { "1", "2" }; + var arr2 = new string[0]; + var arr3 = new[] { "3" }; + var newArr = Arr.Merge(arr1, arr2, arr3); + Assert.AreEqual(3, newArr.Length); + var i = 0; + foreach (var result in newArr) + { + Assert.AreEqual((++i).ToString(), result); + } + } + [TestMethod] public void TestRandom() { diff --git a/src/CatLib.Core.Tests/Support/Util/StreamExtensionTests.cs b/src/CatLib.Core.Tests/Support/Util/StreamExtensionTests.cs index ed5ea25..5d2ad7e 100644 --- a/src/CatLib.Core.Tests/Support/Util/StreamExtensionTests.cs +++ b/src/CatLib.Core.Tests/Support/Util/StreamExtensionTests.cs @@ -28,5 +28,52 @@ public void TestAppendTo() Assert.AreEqual(11, count); Assert.AreEqual("Hello world", Encoding.Default.GetString(stream2.GetBuffer(), 0, (int)stream2.Length)); } + + [TestMethod] + public void TestStreamToText() + { + var stream1 = new MemoryStream(Encoding.Default.GetBytes("Hello world")); + Assert.AreEqual("Hello world", stream1.ToText()); + } + + [TestMethod] + public void TestStreamToTextOtherStream() + { + var stream1 = new StorageStream(new MemoryStorage()); + stream1.Write(Encoding.Default.GetBytes("Hello world"), 0, 11); + stream1.Seek(0, SeekOrigin.Begin); + Assert.AreEqual("Hello world", stream1.ToText()); + } + + [TestMethod] + public void TestStreamToTextLarage() + { + var stream1 = new StorageStream(new MemoryStorage()); + var builder = new StringBuilder(); + for (var i = 0; i < (ThreadStatic.Buffer.Length / 10) + 1; i++) + { + stream1.Write(Encoding.Default.GetBytes("1234567890"), 0, 10); + builder.Append("1234567890"); + } + stream1.Seek(0, SeekOrigin.Begin); + Assert.AreEqual(builder.ToString(), stream1.ToText()); + } + + [TestMethod] + public void TestStreamToTextEmpty() + { + var stream1 = new MemoryStream(0); + Assert.AreEqual(string.Empty, stream1.ToText()); + } + + [TestMethod] + public void TestStreamClosed() + { + var stream1 = new MemoryStream(0); + Assert.AreEqual(string.Empty, stream1.ToText(null, false)); + Assert.AreEqual(true, stream1.CanWrite); + Assert.AreEqual(string.Empty, stream1.ToText()); + Assert.AreEqual(false, stream1.CanWrite); + } } } diff --git a/src/CatLib.Core/CatLib.Core.csproj b/src/CatLib.Core/CatLib.Core.csproj index fd50440..997e88a 100644 --- a/src/CatLib.Core/CatLib.Core.csproj +++ b/src/CatLib.Core/CatLib.Core.csproj @@ -86,6 +86,7 @@ + diff --git a/src/CatLib.Core/CatLib/Application.cs b/src/CatLib.Core/CatLib/Application.cs index 62fd368..b9a8666 100644 --- a/src/CatLib.Core/CatLib/Application.cs +++ b/src/CatLib.Core/CatLib/Application.cs @@ -23,7 +23,7 @@ public class Application : Container, IApplication, IOriginalDispatcher ///

/// 版本号 /// - private readonly Version version = new Version("1.2.9"); + private readonly Version version = new Version("1.2.10"); /// /// 框架启动流程 diff --git a/src/CatLib.Core/Properties/AssemblyInfo.cs b/src/CatLib.Core/Properties/AssemblyInfo.cs index 7121d22..2444b09 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.9.0")] -[assembly: AssemblyFileVersion("1.2.9.0")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.10.0")] [assembly: InternalsVisibleTo("Assembly-CSharp-Editor"), InternalsVisibleTo("Assembly-CSharp-Editor-firstpass"), diff --git a/src/CatLib.Core/Support/Stream/PipelineStream.cs b/src/CatLib.Core/Support/Stream/PipelineStream.cs new file mode 100644 index 0000000..6f1a461 --- /dev/null +++ b/src/CatLib.Core/Support/Stream/PipelineStream.cs @@ -0,0 +1,325 @@ +/* + * 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; +using System.Threading; + +namespace CatLib +{ + /// + /// 管道通讯流 + /// 一读一写线程安全 + /// + public class PipelineStream : Stream + { + /// + /// 可以被读取的长度 + /// + private volatile int count; + + /// + /// 容量 + /// + private readonly int capacity; + + /// + /// 休眠时间 + /// + private readonly int sleep; + + /// + /// 环形缓冲区 + /// + private readonly RingBuffer ringBuffer; + + /// + /// 当写入完成后触发 + /// + public event Action OnWrote; + + /// + /// 是否已经被释放了 + /// + private volatile bool disabled; + + /// + /// 是否已经关闭流了 + /// + private volatile bool closed; + + /// + /// 总流量 + /// + public long TotalFlow { get; private set; } + + /// + /// 是否可以被读取 + /// + public override bool CanRead + { + get { return count > 0 && !disabled; } + } + + /// + /// 是否可以被写入 + /// + public override bool CanWrite + { + get { return count < capacity && !closed; } + } + + /// + /// 偏移位置(不支持) + /// + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + /// + /// 流的长度 + /// + public override long Length + { + get { throw new NotSupportedException(); } + } + + /// + /// 是否能够设定偏移量 + /// + public override bool CanSeek + { + get { return false; } + } + + /// + /// 是否已经关闭了流 + /// + public bool Closed + { + get { return closed; } + } + + /// + /// 管道通讯流 + /// + /// 缓冲区容量 + /// 线程休眠时间 + public PipelineStream(int capacity = 4096, int sleep = 1) + { + this.capacity = capacity.ToPrime(); + this.sleep = Math.Max(0, sleep); + TotalFlow = 0; + ringBuffer = new RingBuffer(this.capacity, false); + } + + /// + /// GC回收时 + /// + ~PipelineStream() + { + Dispose(!disabled); + } + + /// + /// 偏移位置(不支持) + /// + /// 偏移量 + /// 偏移方向 + /// + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// 设定流的长度(不支持) + /// + /// 长度 + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// 刷新缓冲区 + /// + public override void Flush() + { + // ignore + } + + /// + /// 将流中的数据读取到指定缓冲区 + /// + /// 指定缓冲区 + /// 缓冲区起始偏移量 + /// 读取的长度 + /// 实际读取的长度 + public override int Read(byte[] buffer, int offset, int count) + { + while (true) + { + while (this.count <= 0) + { + AssertDisabled(); + if (closed) + { + return 0; + } + Thread.Sleep(sleep); + } + + AssertDisabled(); + + lock (ringBuffer.SyncRoot) + { + AssertDisabled(); + if (this.count <= 0) + { + if (closed) + { + return 0; + } + continue; + } + + try + { + var read = ringBuffer.Read(buffer, offset, count); + this.count -= read; + TotalFlow += read; + return read; + } + finally + { + Guard.Requires(this.count >= 0); + } + } + } + } + + /// + /// 将指定缓冲区数据写入流中 + /// + /// 指定缓冲区 + /// 缓冲区起始偏移量 + /// 写入的长度 + public override void Write(byte[] buffer, int offset, int count) + { + while (true) + { + while ((capacity - this.count) < count) + { + AssertDisabled(); + AssertClosed(); + + Thread.Sleep(sleep); + } + + AssertDisabled(); + AssertClosed(); + + lock (ringBuffer.SyncRoot) + { + AssertDisabled(); + AssertClosed(); + + if ((capacity - this.count) < count) + { + continue; + } + + Guard.Requires(ringBuffer.Write(buffer, offset, count) == count); + this.count += count; + + if (OnWrote != null) + { + OnWrote(this); + } + + return; + } + } + } + + /// + /// 关闭流 + /// + public override void Close() + { + if (closed) + { + return; + } + + lock (ringBuffer.SyncRoot) + { + closed = true; + } + } + + /// + /// 断言关闭 + /// + protected void AssertClosed() + { + if (closed) + { + throw new ObjectDisposedException("PipelineStream", "Stream is Closed Cannot write"); + } + } + + /// + /// 断言释放 + /// + protected void AssertDisabled() + { + if (disabled) + { + throw new ObjectDisposedException("PipelineStream", "Stream is dispose"); + } + } + + /// + /// 释放资源 + /// + /// 是否进行释放 + protected override void Dispose(bool disposing) + { + if (!disposing || disabled) + { + return; + } + + lock (ringBuffer.SyncRoot) + { + if (disabled) + { + return; + } + + try + { + disabled = true; + closed = true; + ringBuffer.Dispose(); + } + finally + { + base.Dispose(true); + } + } + } + } +} diff --git a/src/CatLib.Core/Support/Util/Arr.cs b/src/CatLib.Core/Support/Util/Arr.cs index 879834b..8e2e189 100644 --- a/src/CatLib.Core/Support/Util/Arr.cs +++ b/src/CatLib.Core/Support/Util/Arr.cs @@ -31,13 +31,26 @@ public static T[] Merge(params T[][] sources) var length = 0; foreach (var source in sources) { + if (source == null || source.Length <= 0) + { + continue; + } length += source.Length; } + if (length <= 0) + { + return new T[0]; + } + var merge = new T[length]; var current = 0; foreach (var source in sources) { + if (source == null || source.Length <= 0) + { + continue; + } Array.Copy(source, 0, merge, current, source.Length); current += source.Length; } diff --git a/src/CatLib.Core/Support/Util/Extension/StreamExtension.cs b/src/CatLib.Core/Support/Util/Extension/StreamExtension.cs index 261fb84..e263269 100644 --- a/src/CatLib.Core/Support/Util/Extension/StreamExtension.cs +++ b/src/CatLib.Core/Support/Util/Extension/StreamExtension.cs @@ -11,6 +11,7 @@ using System; using System.IO; +using System.Text; namespace CatLib { @@ -19,6 +20,14 @@ namespace CatLib /// public static class StreamExtension { + /// + /// 编码 + /// + private static Encoding Encoding + { + get { return Encoding.UTF8; } + } + /// /// 将当前流追加到目标流中 /// @@ -44,7 +53,7 @@ public static long AppendTo(this Stream source, Stream destination, byte[] buffe long result = 0; int read; - while ((read = source.Read(buffer, 0, buffer.Length)) != 0) + while ((read = source.Read(buffer, 0, buffer.Length)) > 0) { destination.Write(buffer, 0, read); result += read; @@ -52,5 +61,68 @@ public static long AppendTo(this Stream source, Stream destination, byte[] buffe return result; } + + /// + /// 将流转为字符串 + /// + /// 源数据流 + /// 编码 + /// 是否自动关闭流 + /// 字符串 + public static string ToText(this Stream source, Encoding encoding = null, bool closed = true) + { + try + { + encoding = encoding ?? Encoding; + if (source is MemoryStream) + { + var memoryStream = (MemoryStream) source; + byte[] buffer; + try + { + buffer = memoryStream.GetBuffer(); + } + catch (UnauthorizedAccessException) + { + buffer = memoryStream.ToArray(); + } + + return encoding.GetString(buffer, 0, (int) memoryStream.Length); + } + + var length = 0; + try + { + length = (int) source.Length; + } + catch (NotSupportedException) + { + // ignore + } + + MemoryStream targetStream; + if (length > 0 && length <= ThreadStatic.Buffer.Length) + { + targetStream = new MemoryStream(ThreadStatic.Buffer, 0, ThreadStatic.Buffer.Length, true, true); + } + else + { + targetStream = new MemoryStream(length); + } + + using (targetStream) + { + var read = source.AppendTo(targetStream); + return encoding.GetString(targetStream.GetBuffer(), 0, (int) read); + } + } + finally + { + if (closed) + { + source.Dispose(); + } + } + } } }