-
Notifications
You must be signed in to change notification settings - Fork 639
/
StreamExtensions.cs
214 lines (192 loc) · 8.77 KB
/
StreamExtensions.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
using J2N.IO;
using Lucene.Net.Support.Threading;
using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace Lucene.Net.Support.IO
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <summary>
/// Extension methods that make a <see cref="Stream"/> effectively into a
/// binary serializer with no encoding. We simply convert types into bytes
/// and write them without any concern whether surrogate pairs are respected,
/// similar to what BinaryFormatter does.
/// This makes it possible to serialize/deserialize raw character arrays
/// and get the data back in the same order without any exceptions warning
/// that the order is not valid and without the need for BinaryFormatter.
/// <para/>
/// Byte order is little-endian (same as <see cref="BinaryReader"/> and <see cref="BinaryWriter"/>).
/// </summary>
internal static class StreamExtensions
{
private static readonly ConditionalWeakTable<Stream, object> lockCache = new ConditionalWeakTable<Stream, object>();
/// <summary>
/// Reads a sequence of bytes from a <see cref="Stream"/> to the given <see cref="ByteBuffer"/>, starting at the given position.
/// The <paramref name="stream"/> must be both seekable and readable.
/// </summary>
/// <param name="stream">The stream to read.</param>
/// <param name="destination">The <see cref="ByteBuffer"/> to write to.</param>
/// <param name="position">The file position at which the transfer is to begin; must be non-negative.</param>
/// <returns>The number of bytes read, possibly zero.</returns>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> or <paramref name="destination"/> is <c>null</c></exception>
/// <exception cref="NotSupportedException">
/// <paramref name="stream"/> is not readable.
/// <para/>
/// -or-
/// <para/>
/// <paramref name="stream"/> is not seekable.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="position"/> is less than 0.
/// <para/>
/// -or-
/// <para/>
/// <paramref name="position"/> is greater than the <see cref="Stream.Length"/> of the stream.
/// </exception>
/// <exception cref="IOException">An I/O error occurs.</exception>
/// <exception cref="ObjectDisposedException"><paramref name="stream"/> has already been disposed.</exception>
/// <remarks>
/// This method is atomic when used by itself, but does not synchronize with the rest of the stream methods.
/// </remarks>
public static int Read(this Stream stream, ByteBuffer destination, long position)
{
if (stream is null)
throw new ArgumentNullException(nameof(stream));
if (destination is null)
throw new ArgumentNullException(nameof(destination));
if (position < 0)
throw new ArgumentOutOfRangeException(nameof(position));
if (!stream.CanSeek)
throw new NotSupportedException("Stream does not support seeking.");
if (!stream.CanRead)
throw new NotSupportedException("Stream does not support reading.");
if (position > stream.Length)
return 0;
int read = 0;
object readLock = lockCache.GetOrCreateValue(stream);
UninterruptableMonitor.Enter(readLock);
try
{
long originalPosition = stream.Position;
stream.Seek(position, SeekOrigin.Begin);
if (destination.HasArray)
{
// If the buffer has an array, we can write to it directly and save
// an extra copy operation.
// Read from the stream
read = stream.Read(destination.Array, destination.Position, destination.Remaining);
destination.Position += read;
}
else
{
// If the buffer has no array, we must use a local buffer
byte[] buffer = new byte[destination.Remaining];
// Read from the stream
read = stream.Read(buffer, 0, buffer.Length);
// Write to the byte buffer
destination.Put(buffer, 0, read);
}
// Per Java's FileChannel.Read(), we don't want to alter the position
// of the stream, so we return it as it was originally.
stream.Seek(originalPosition, SeekOrigin.Begin);
}
finally
{
UninterruptableMonitor.Exit(readLock);
}
return read;
}
public static void Write(this Stream stream, char[] chars)
{
if (stream is null)
throw new ArgumentNullException(nameof(stream));
if (chars is null)
throw new ArgumentNullException(nameof(chars));
byte[] newBytes = new byte[chars.Length * 2];
for (int index = 0; index < chars.Length; index++)
{
int newIndex = index == 0 ? index : index * 2;
newBytes[newIndex] = (byte)chars[index];
newBytes[newIndex + 1] = (byte)(chars[index] >> 8);
}
stream.Write(newBytes, 0, newBytes.Length);
}
public static char[] ReadChars(this Stream stream, int count)
{
if (stream is null)
throw new ArgumentNullException(nameof(stream));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
byte[] buff = new byte[2];
char[] newChars = new char[count];
for (int i = 0; i < count; i++)
{
stream.Read(buff, 0, 2);
newChars[i] = (char)((buff[0] & 0xff) | ((buff[1] & 0xff) << 8));
}
return newChars;
}
public static void Write(this Stream stream, int value)
{
if (stream is null)
throw new ArgumentNullException(nameof(stream));
byte[] buff = new byte[4];
buff[0] = (byte)(value);
buff[1] = (byte)(value >> 8);
buff[2] = (byte)(value >> 16);
buff[3] = (byte)(value >> 24);
stream.Write(buff, 0, buff.Length);
}
public static int ReadInt32(this Stream stream)
{
if (stream is null)
throw new ArgumentNullException(nameof(stream));
byte[] buff = new byte[4];
stream.Read(buff, 0, buff.Length);
return (buff[0] & 0xff) | ((buff[1] & 0xff) << 8) |
((buff[2] & 0xff) << 16) | ((buff[3] & 0xff) << 24);
}
public static void Write(this Stream stream, long value)
{
if (stream is null)
throw new ArgumentNullException(nameof(stream));
byte[] buff = new byte[8];
buff[0] = (byte)value;
buff[1] = (byte)(value >> 8);
buff[2] = (byte)(value >> 16);
buff[3] = (byte)(value >> 24);
buff[4] = (byte)(value >> 32);
buff[5] = (byte)(value >> 40);
buff[6] = (byte)(value >> 48);
buff[7] = (byte)(value >> 56);
stream.Write(buff, 0, buff.Length);
}
public static long ReadInt64(this Stream stream)
{
if (stream is null)
throw new ArgumentNullException(nameof(stream));
byte[] buff = new byte[8];
stream.Read(buff, 0, buff.Length);
uint lo = (uint)(buff[0] | buff[1] << 8 |
buff[2] << 16 | buff[3] << 24);
uint hi = (uint)(buff[4] | buff[5] << 8 |
buff[6] << 16 | buff[7] << 24);
return (long)((ulong)hi) << 32 | lo;
}
}
}