-
-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathBencodeReader.cs
More file actions
168 lines (140 loc) · 5.39 KB
/
BencodeReader.cs
File metadata and controls
168 lines (140 loc) · 5.39 KB
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
using System;
using System.IO;
namespace BencodeNET.IO
{
/// <summary>
/// Reads bencode from a stream.
/// </summary>
public class BencodeReader : IDisposable
{
private readonly byte[] _tinyBuffer = new byte[1];
private readonly Stream _stream;
private readonly bool _leaveOpen;
private readonly bool _supportsLength;
private bool _hasPeeked;
private char _peekedChar;
/// <summary>
/// The previously read/consumed char (does not include peeked char).
/// </summary>
public char PreviousChar { get; private set; }
/// <summary>
/// The position in the stream (does not included peeked char).
/// </summary>
public long Position { get; set; }
/// <summary>
/// The length of the stream, or <c>null</c> if the stream doesn't support the feature.
/// </summary>
public long? Length => _supportsLength ? _stream.Length : (long?) null;
/// <summary>
/// Returns true if the end of the stream has been reached.
/// This is true if either <see cref="Position"/> is greater than <see cref="Length"/> or if next char is <c>default(char)</c>.
/// </summary>
public bool EndOfStream => Position > Length || PeekChar() == default;
/// <summary>
/// Creates a new <see cref="BencodeReader"/> for the specified <see cref="Stream"/>.
/// </summary>
/// <param name="stream">The stream to read from.</param>
public BencodeReader(Stream stream)
: this(stream, leaveOpen: false)
{
}
/// <summary>
/// Creates a new <see cref="BencodeReader"/> for the specified <see cref="Stream"/>
/// using the default buffer size of 40,960 bytes and the option of leaving the stream open after disposing of this instance.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="leaveOpen">Indicates if the stream should be left open when this <see cref="BencodeReader"/> is disposed.</param>
public BencodeReader(Stream stream, bool leaveOpen)
{
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
_leaveOpen = leaveOpen;
try
{
_ = stream.Length;
_supportsLength = true;
}
catch
{
_supportsLength = false;
}
if (!_stream.CanRead) throw new ArgumentException("The stream is not readable.", nameof(stream));
}
/// <summary>
/// Peeks at the next character in the stream, or <c>default(char)</c> if the end of the stream has been reached.
/// </summary>
public char PeekChar()
{
if (_hasPeeked)
return _peekedChar;
var read = _stream.Read(_tinyBuffer, 0, 1);
_peekedChar = read == 0 ? default : (char)_tinyBuffer[0];
_hasPeeked = true;
return _peekedChar;
}
/// <summary>
/// Reads the next character from the stream.
/// Returns <c>default(char)</c> if the end of the stream has been reached.
/// </summary>
public char ReadChar()
{
if (_hasPeeked)
{
_hasPeeked = _peekedChar == default; // If null then EOS so don't reset peek as peeking again will just be EOS again
if (_peekedChar != default)
Position++;
return _peekedChar;
}
var read = _stream.Read(_tinyBuffer, 0, 1);
PreviousChar = read == 0
? default
: (char) _tinyBuffer[0];
if (read > 0)
Position++;
return PreviousChar;
}
/// <summary>
/// Reads into the <paramref name="buffer"/> by reading from the stream.
/// Returns the number of bytes actually read from the stream.
/// </summary>
/// <param name="buffer">The buffer to read into.</param>
/// <returns>The number of bytes actually read from the stream and filled into the buffer.</returns>
public int Read(byte[] buffer)
{
var totalRead = 0;
if (_hasPeeked && _peekedChar != default)
{
buffer[0] = (byte) _peekedChar;
totalRead = 1;
_hasPeeked = false;
// Just return right away if only reading this 1 byte
if (buffer.Length == 1)
{
Position++;
return 1;
}
}
int read = -1;
while (read != 0 && totalRead < buffer.Length)
{
read = _stream.Read(buffer, totalRead, buffer.Length - totalRead);
totalRead += read;
}
if (totalRead > 0)
PreviousChar = (char) buffer[totalRead - 1];
Position += totalRead;
return totalRead;
}
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
}
/// <inheritdoc cref="Dispose()"/>
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (_stream != null && !_leaveOpen)
_stream.Dispose();
}
}
}