Skip to content

Commit 20f8bb2

Browse files
author
喻斌
committed
resolved #74
1 parent 405e6d8 commit 20f8bb2

File tree

3 files changed

+348
-0
lines changed

3 files changed

+348
-0
lines changed

src/CatLib.Core.Tests/CatLib.Core.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
<Compile Include="Support\Storage\MemoryStorageTests.cs" />
5555
<Compile Include="Support\Stream\CombineStreamTests.cs" />
5656
<Compile Include="Support\Stream\PipelineStreamTests.cs" />
57+
<Compile Include="Support\Stream\SegmentStreamTests.cs" />
5758
<Compile Include="Support\Stream\StorageStreamTests.cs" />
5859
<Compile Include="Support\Template\ManagerTests.cs" />
5960
<Compile Include="Support\Template\SingleManagerTests.cs" />
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* This file is part of the CatLib package.
3+
*
4+
* (c) Yu Bin <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*
9+
* Document: http://catlib.io/
10+
*/
11+
12+
using System;
13+
using System.IO;
14+
using Microsoft.VisualStudio.TestTools.UnitTesting;
15+
16+
namespace CatLib.Core.Tests.Support.Stream
17+
{
18+
[TestClass]
19+
public class SegmentStreamTests
20+
{
21+
[TestMethod]
22+
public void TestRead()
23+
{
24+
var baseStream = "hello world , my name is miaomiao".ToStream();
25+
var segmentStream = new SegmentStream(baseStream, 11);
26+
Assert.AreEqual("hello world", segmentStream.ToText());
27+
}
28+
29+
[TestMethod]
30+
public void TestReadMiddle()
31+
{
32+
var baseStream = "hello world , my name is miaomiao".ToStream();
33+
baseStream.Seek(14, SeekOrigin.Begin);
34+
var segmentStream = new SegmentStream(baseStream, 10);
35+
36+
Assert.AreEqual("my name is", segmentStream.ToText());
37+
}
38+
39+
[TestMethod]
40+
public void TestReadEnd()
41+
{
42+
var baseStream = "hello world , my name is miaomiao".ToStream();
43+
baseStream.Seek(14, SeekOrigin.Begin);
44+
var segmentStream = new SegmentStream(baseStream);
45+
46+
Assert.AreEqual("my name is miaomiao", segmentStream.ToText());
47+
}
48+
49+
50+
[TestMethod]
51+
public void TestSeekEnd()
52+
{
53+
var baseStream = "hello world , my name is miaomiao".ToStream();
54+
var segmentStream = new SegmentStream(baseStream, 11);
55+
segmentStream.Seek(-5, SeekOrigin.End);
56+
57+
Assert.AreEqual("world", segmentStream.ToText());
58+
}
59+
60+
[TestMethod]
61+
public void TestSeekCurrent()
62+
{
63+
var baseStream = "hello world , my name is miaomiao".ToStream();
64+
baseStream.Seek(2, SeekOrigin.Begin);
65+
var segmentStream = new SegmentStream(baseStream, 9);
66+
segmentStream.Seek(4, SeekOrigin.Current);
67+
Assert.AreEqual("world", segmentStream.ToText());
68+
}
69+
70+
[TestMethod]
71+
public void TestSeekBegin()
72+
{
73+
var baseStream = "hello world , my name is miaomiao".ToStream();
74+
var segmentStream = new SegmentStream(baseStream, 11);
75+
segmentStream.ToText(null, false);
76+
segmentStream.Seek(0, SeekOrigin.Begin);
77+
Assert.AreEqual("hello world", segmentStream.ToText());
78+
}
79+
80+
[TestMethod]
81+
public void TestGetLength()
82+
{
83+
var baseStream = "hello world , my name is miaomiao".ToStream();
84+
var segmentStream = new SegmentStream(baseStream, 11);
85+
Assert.AreEqual(11, segmentStream.Length);
86+
}
87+
88+
[TestMethod]
89+
public void TestGetPosition()
90+
{
91+
var baseStream = "hello world , my name is miaomiao".ToStream();
92+
baseStream.Seek(2, SeekOrigin.Begin);
93+
var segmentStream = new SegmentStream(baseStream, 9) {Position = 4};
94+
Assert.AreEqual(4, segmentStream.Position);
95+
}
96+
97+
[TestMethod]
98+
public void TestReadBuffer()
99+
{
100+
var baseStream = "hello world , my name is miaomiao".ToStream();
101+
var segmentStream = new SegmentStream(baseStream, 11);
102+
var buffer = new byte[255];
103+
Assert.AreEqual(11, segmentStream.Read(buffer, 0, 255));
104+
Assert.AreEqual("hello world", CatLib.Util.Encoding.GetString(buffer, 0, 11));
105+
}
106+
107+
[TestMethod]
108+
public void TestReadBufferEnd()
109+
{
110+
var baseStream = "hello world , my name is miaomiao".ToStream();
111+
var segmentStream = new SegmentStream(baseStream, 11);
112+
var buffer = new byte[255];
113+
Assert.AreEqual(11, segmentStream.Read(buffer, 0, 255));
114+
Assert.AreEqual("hello world", CatLib.Util.Encoding.GetString(buffer, 0, 11));
115+
Assert.AreEqual(0, segmentStream.Read(buffer, 0, 255));
116+
}
117+
118+
[TestMethod]
119+
public void TestReadBufferMin()
120+
{
121+
var baseStream = "hello world , my name is miaomiao".ToStream();
122+
var segmentStream = new SegmentStream(baseStream, 11);
123+
var buffer = new byte[5];
124+
Assert.AreEqual(5, segmentStream.Read(buffer, 0, 5));
125+
Assert.AreEqual("hello", CatLib.Util.Encoding.GetString(buffer, 0, 5));
126+
}
127+
128+
[TestMethod]
129+
[ExpectedException(typeof(NotSupportedException))]
130+
public void TestSetLength()
131+
{
132+
var baseStream = "hello world , my name is miaomiao".ToStream();
133+
var segmentStream = new SegmentStream(baseStream, 11);
134+
segmentStream.SetLength(100);
135+
}
136+
137+
[TestMethod]
138+
[ExpectedException(typeof(NotSupportedException))]
139+
public void TestWrite()
140+
{
141+
var baseStream = "hello world , my name is miaomiao".ToStream();
142+
var segmentStream = new SegmentStream(baseStream, 11);
143+
var buffer = CatLib.Util.Encoding.GetBytes("hello world");
144+
145+
segmentStream.Write(buffer, 0, buffer.Length);
146+
}
147+
148+
[TestMethod]
149+
[ExpectedException(typeof(ArgumentOutOfRangeException))]
150+
public void TestNotSupportSeekOrigin()
151+
{
152+
var baseStream = "hello world , my name is miaomiao".ToStream();
153+
var segmentStream = new SegmentStream(baseStream, 11);
154+
segmentStream.Seek(0, (SeekOrigin) 100);
155+
}
156+
157+
[TestMethod]
158+
public void TestSeekEndToRead()
159+
{
160+
var baseStream = "hello world , my name is miaomiao".ToStream();
161+
var segmentStream = new SegmentStream(baseStream, 11);
162+
segmentStream.Seek(100, SeekOrigin.End);
163+
Assert.AreEqual(string.Empty, segmentStream.ToText());
164+
}
165+
166+
[TestMethod]
167+
public void TestSeekSmallThenStart()
168+
{
169+
var baseStream = "hello world , my name is miaomiao".ToStream();
170+
var segmentStream = new SegmentStream(baseStream, 11);
171+
segmentStream.Seek(-100, SeekOrigin.Begin);
172+
Assert.AreEqual("hello world", segmentStream.ToText());
173+
}
174+
175+
176+
private class CanNotSeekStream : WrapperStream
177+
{
178+
public override bool CanSeek => false;
179+
}
180+
181+
[TestMethod]
182+
[ExpectedException(typeof(InvalidOperationException))]
183+
public void TestGivenCanNotSeek()
184+
{
185+
var segmentStream = new SegmentStream(new CanNotSeekStream());
186+
}
187+
}
188+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* This file is part of the CatLib package.
3+
*
4+
* (c) Yu Bin <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*
9+
* Document: http://catlib.io/
10+
*/
11+
12+
using System;
13+
using System.IO;
14+
15+
namespace CatLib
16+
{
17+
/// <summary>
18+
/// 分片流可以用于包装指定分片的流。
19+
/// 使指定分片的流访问起来就像传统流那样从开头到结尾。
20+
/// </summary>
21+
public class SegmentStream : WrapperStream
22+
{
23+
/// <summary>
24+
/// 基础流的初始的位置
25+
/// </summary>
26+
private readonly long initialPosition;
27+
28+
/// <summary>
29+
/// 分片的大小
30+
/// </summary>
31+
private readonly long partSize;
32+
33+
/// <summary>
34+
/// 构造一个分片流
35+
/// </summary>
36+
/// <param name="stream">基础流</param>
37+
/// <param name="partSize">分片大小</param>
38+
public SegmentStream(Stream stream, long partSize = 0)
39+
: base(stream)
40+
{
41+
if (!stream.CanSeek)
42+
{
43+
throw new InvalidOperationException($"Base stream of {nameof(SegmentStream)} must be seekable");
44+
}
45+
46+
initialPosition = stream.Position;
47+
var remainingSize = stream.Length - stream.Position;
48+
49+
if (partSize == 0 || remainingSize < partSize)
50+
{
51+
this.partSize = remainingSize;
52+
}
53+
else
54+
{
55+
this.partSize = partSize;
56+
}
57+
}
58+
59+
/// <summary>
60+
/// 剩余长度
61+
/// </summary>
62+
private long RemainingSize => partSize - Position;
63+
64+
/// <summary>
65+
/// 流的长度
66+
/// </summary>
67+
public override long Length
68+
{
69+
get
70+
{
71+
var length = base.Length - initialPosition;
72+
if (length > partSize)
73+
{
74+
length = partSize;
75+
}
76+
return length;
77+
}
78+
}
79+
80+
/// <summary>
81+
/// 当前流的位置
82+
/// </summary>
83+
public override long Position
84+
{
85+
get => base.Position - initialPosition;
86+
set => base.Position = value;
87+
}
88+
89+
/// <summary>
90+
/// 设定流的位置
91+
/// </summary>
92+
/// <param name="offset">偏移量</param>
93+
/// <param name="origin">偏移方向</param>
94+
/// <returns>流的位置</returns>
95+
public override long Seek(long offset, SeekOrigin origin)
96+
{
97+
long position;
98+
switch (origin)
99+
{
100+
case SeekOrigin.Begin:
101+
position = initialPosition + offset;
102+
break;
103+
case SeekOrigin.Current:
104+
position = base.Position + offset;
105+
break;
106+
case SeekOrigin.End:
107+
position = base.Position + partSize + offset;
108+
break;
109+
default:
110+
throw new ArgumentOutOfRangeException(nameof(origin), origin, null);
111+
}
112+
113+
if (position < initialPosition)
114+
{
115+
position = initialPosition;
116+
}
117+
else if (position > initialPosition + partSize)
118+
{
119+
position = initialPosition + partSize;
120+
}
121+
122+
base.Seek(position, SeekOrigin.Begin);
123+
return Position;
124+
}
125+
126+
/// <summary>
127+
/// 读取流的数据到指定缓冲区
128+
/// </summary>
129+
/// <param name="buffer">指定缓冲区</param>
130+
/// <param name="offset">缓冲区偏移量</param>
131+
/// <param name="count">希望读取的长度</param>
132+
/// <returns>实际读取的长度</returns>
133+
public override int Read(byte[] buffer, int offset, int count)
134+
{
135+
var bytesToRead = count < RemainingSize ? count : (int)RemainingSize;
136+
return bytesToRead < 0 ? 0 : base.Read(buffer, offset, bytesToRead);
137+
}
138+
139+
/// <summary>
140+
/// 设定流的长度
141+
/// </summary>
142+
/// <param name="value"></param>
143+
public override void SetLength(long value)
144+
{
145+
throw new NotSupportedException();
146+
}
147+
148+
/// <summary>
149+
/// 将指定缓冲区的数据写入流中
150+
/// </summary>
151+
/// <param name="buffer">指定缓冲区</param>
152+
/// <param name="offset">缓冲区偏移量</param>
153+
/// <param name="count">写入数据的长度</param>
154+
public override void Write(byte[] buffer, int offset, int count)
155+
{
156+
throw new NotSupportedException();
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)