forked from OpenRA/OpenRA
-
Notifications
You must be signed in to change notification settings - Fork 0
/
StreamExts.cs
240 lines (208 loc) · 6.36 KB
/
StreamExts.cs
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace OpenRA
{
public static class StreamExts
{
public static byte[] ReadBytes(this Stream s, int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), "Non-negative number required.");
var buffer = new byte[count];
s.ReadBytes(buffer, 0, count);
return buffer;
}
public static void ReadBytes(this Stream s, byte[] buffer, int offset, int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), "Non-negative number required.");
while (count > 0)
{
int bytesRead;
if ((bytesRead = s.Read(buffer, offset, count)) == 0)
throw new EndOfStreamException();
offset += bytesRead;
count -= bytesRead;
}
}
public static int Peek(this Stream s)
{
var b = s.ReadByte();
if (b == -1)
return -1;
s.Seek(-1, SeekOrigin.Current);
return (byte)b;
}
public static byte ReadUInt8(this Stream s)
{
var b = s.ReadByte();
if (b == -1)
throw new EndOfStreamException();
return (byte)b;
}
public static ushort ReadUInt16(this Stream s)
{
return (ushort)(s.ReadUInt8() | s.ReadUInt8() << 8);
}
public static short ReadInt16(this Stream s)
{
return (short)(s.ReadUInt8() | s.ReadUInt8() << 8);
}
public static uint ReadUInt32(this Stream s)
{
return (uint)(s.ReadUInt8() | s.ReadUInt8() << 8 | s.ReadUInt8() << 16 | s.ReadUInt8() << 24);
}
public static int ReadInt32(this Stream s)
{
return s.ReadUInt8() | s.ReadUInt8() << 8 | s.ReadUInt8() << 16 | s.ReadUInt8() << 24;
}
public static void Write(this Stream s, int value)
{
s.WriteArray(BitConverter.GetBytes(value));
}
public static void Write(this Stream s, float value)
{
s.WriteArray(BitConverter.GetBytes(value));
}
public static float ReadFloat(this Stream s)
{
return BitConverter.ToSingle(s.ReadBytes(4), 0);
}
public static double ReadDouble(this Stream s)
{
return BitConverter.ToDouble(s.ReadBytes(8), 0);
}
public static string ReadASCII(this Stream s, int length)
{
return new string(Encoding.ASCII.GetChars(s.ReadBytes(length)));
}
public static string ReadASCIIZ(this Stream s)
{
var bytes = new List<byte>();
byte b;
while ((b = s.ReadUInt8()) != 0)
bytes.Add(b);
return new string(Encoding.ASCII.GetChars(bytes.ToArray()));
}
public static string ReadAllText(this Stream s)
{
using (s)
using (var sr = new StreamReader(s))
return sr.ReadToEnd();
}
public static byte[] ReadAllBytes(this Stream s)
{
using (s)
{
if (s.CanSeek)
return s.ReadBytes((int)(s.Length - s.Position));
var bytes = new List<byte>();
var buffer = new byte[1024];
int count;
while ((count = s.Read(buffer, 0, buffer.Length)) > 0)
bytes.AddRange(buffer.Take(count));
return bytes.ToArray();
}
}
// Note: renamed from Write() to avoid being aliased by
// System.IO.Stream.Write(System.ReadOnlySpan) (which is not implemented in Mono)
public static void WriteArray(this Stream s, byte[] data)
{
s.Write(data, 0, data.Length);
}
public static IEnumerable<string> ReadAllLines(this Stream s)
{
string line;
using (var sr = new StreamReader(s))
while ((line = sr.ReadLine()) != null)
yield return line;
}
/// <summary>
/// Streams each line of characters from a stream, exposing the line as <see cref="ReadOnlyMemory{T}"/>.
/// The memory lifetime is only valid during that iteration. Advancing the iteration invalidates the memory.
/// Consumers should call <see cref="ReadOnlyMemory{T}.Span"/> on each line and otherwise avoid operating on
/// the memory to ensure they meet the lifetime restrictions.
/// </summary>
public static IEnumerable<ReadOnlyMemory<char>> ReadAllLinesAsMemory(this Stream s)
{
var buffer = ArrayPool<char>.Shared.Rent(128);
try
{
using (var sr = new StreamReader(s))
{
var offset = 0;
int read;
while ((read = sr.ReadBlock(buffer, offset, buffer.Length - offset)) != 0)
{
offset += read;
var consumedIndex = 0;
int newlineIndex;
while ((newlineIndex = Array.IndexOf(buffer, '\n', offset - read, read)) != -1)
{
if (newlineIndex > 0 && buffer[newlineIndex - 1] == '\r')
yield return buffer.AsMemory(consumedIndex, newlineIndex - consumedIndex - 1);
else
yield return buffer.AsMemory(consumedIndex, newlineIndex - consumedIndex);
var afterNewlineIndex = newlineIndex + 1;
read = offset - afterNewlineIndex;
consumedIndex = afterNewlineIndex;
}
if (consumedIndex > 0)
{
Array.Copy(buffer, consumedIndex, buffer, 0, offset - consumedIndex);
offset = read;
}
if (offset == buffer.Length)
{
var newBuffer = ArrayPool<char>.Shared.Rent(buffer.Length * 2);
Array.Copy(buffer, newBuffer, buffer.Length);
ArrayPool<char>.Shared.Return(buffer);
buffer = newBuffer;
}
}
if (offset > 0)
yield return buffer.AsMemory(0, offset);
}
}
finally
{
ArrayPool<char>.Shared.Return(buffer);
}
}
// The string is assumed to be length-prefixed, as written by WriteString()
public static string ReadString(this Stream s, Encoding encoding, int maxLength)
{
var length = s.ReadInt32();
if (length > maxLength)
throw new InvalidOperationException($"The length of the string ({length}) is longer than the maximum allowed ({maxLength}).");
return encoding.GetString(s.ReadBytes(length));
}
// Writes a length-prefixed string using the specified encoding and returns
// the number of bytes written.
public static int WriteString(this Stream s, Encoding encoding, string text)
{
byte[] bytes;
if (!string.IsNullOrEmpty(text))
bytes = encoding.GetBytes(text);
else
bytes = Array.Empty<byte>();
s.Write(bytes.Length);
s.WriteArray(bytes);
return 4 + bytes.Length;
}
}
}