Code Coverage Statistics for Source File
Newtonsoft.Json\Bson\BsonReader.cs
Symbol Coverage: 93.83% (289 of 308)
Branch Coverage: 88.70% (102 of 115)
Cyclomatic Complexity Avg: 3.55 Max:18
Code Lines: 303
Symbol Coverage Trend
View:
L | V | Source |
---|---|---|
1 |
#region License |
|
2 |
// Copyright (c) 2007 James Newton-King |
|
3 |
// |
|
4 |
// Permission is hereby granted, free of charge, to any person |
|
5 |
// obtaining a copy of this software and associated documentation |
|
6 |
// files (the "Software"), to deal in the Software without |
|
7 |
// restriction, including without limitation the rights to use, |
|
8 |
// copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
9 |
// copies of the Software, and to permit persons to whom the |
|
10 |
// Software is furnished to do so, subject to the following |
|
11 |
// conditions: |
|
12 |
// |
|
13 |
// The above copyright notice and this permission notice shall be |
|
14 |
// included in all copies or substantial portions of the Software. |
|
15 |
// |
|
16 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
17 |
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
|
18 |
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
19 |
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
|
20 |
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
|
21 |
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
22 |
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
|
23 |
// OTHER DEALINGS IN THE SOFTWARE. |
|
24 |
#endregion |
|
25 |
||
26 |
using System; |
|
27 |
using System.Collections.Generic; |
|
28 |
using System.Globalization; |
|
29 |
using System.Text; |
|
30 |
using System.IO; |
|
31 |
using Newtonsoft.Json.Utilities; |
|
32 |
using Newtonsoft.Json.Linq; |
|
33 |
||
34 |
namespace Newtonsoft.Json.Bson |
|
35 |
{ |
|
36 |
/// <summary> |
|
37 |
/// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. |
|
38 |
/// </summary> |
|
39 |
public class BsonReader : JsonReader |
|
40 |
{ |
|
41 |
private const int MaxCharBytesSize = 128; |
|
42 |
1 |
private static readonly byte[] _seqRange1 = new byte[] { 0, 127 }; // range of 1-byte sequence
|
43 |
1 |
private static readonly byte[] _seqRange2 = new byte[] { 194, 223 }; // range of 2-byte sequence
|
44 |
1 |
private static readonly byte[] _seqRange3 = new byte[] { 224, 239 }; // range of 3-byte sequence
|
45 |
1 |
private static readonly byte[] _seqRange4 = new byte[] { 240, 244 }; // range of 4-byte sequence
|
46 |
||
47 |
private readonly BinaryReader _reader; |
|
48 |
private readonly List<ContainerContext> _stack; |
|
49 |
||
50 |
private byte[] _byteBuffer; |
|
51 |
private char[] _charBuffer; |
|
52 |
||
53 |
private BsonType _currentElementType; |
|
54 |
private BsonReaderState _bsonReaderState; |
|
55 |
private ContainerContext _currentContext; |
|
56 |
||
57 |
private bool _readRootValueAsArray; |
|
58 |
private DateTimeKind _dateTimeKindHandling; |
|
59 |
||
60 |
private enum BsonReaderState |
|
61 |
{ |
|
62 |
Normal, |
|
63 |
ReferenceStart, |
|
64 |
ReferenceRef, |
|
65 |
ReferenceId, |
|
66 |
CodeWScopeStart, |
|
67 |
CodeWScopeCode, |
|
68 |
CodeWScopeScope, |
|
69 |
CodeWScopeScopeObject, |
|
70 |
CodeWScopeScopeEnd |
|
71 |
} |
|
72 |
||
73 |
private class ContainerContext |
|
74 |
{ |
|
75 |
public readonly BsonType Type; |
|
76 |
public int Length; |
|
77 |
public int Position; |
|
78 |
||
79 |
35083 |
public ContainerContext(BsonType type)
|
80 |
{ |
|
81 |
35083 |
Type = type;
|
82 |
35083 |
}
|
83 |
} |
|
84 |
||
85 |
/// <summary> |
|
86 |
/// Gets or sets a value indicating whether the root object will be read as a JSON array. |
|
87 |
/// </summary> |
|
88 |
/// <value> |
|
89 |
/// <c>true</c> if the root object will be read as a JSON array; otherwise, <c>false</c>. |
|
90 |
/// </value> |
|
91 |
public bool ReadRootValueAsArray |
|
92 |
{ |
|
93 |
3 |
get { return _readRootValueAsArray; }
|
94 |
3 |
set { _readRootValueAsArray = value; }
|
95 |
} |
|
96 |
||
97 |
/// <summary> |
|
98 |
/// Gets or sets the <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON. |
|
99 |
/// </summary> |
|
100 |
/// <value>The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</value> |
|
101 |
public DateTimeKind DateTimeKindHandling |
|
102 |
{ |
|
103 |
20018 |
get { return _dateTimeKindHandling; }
|
104 |
2 |
set { _dateTimeKindHandling = value; }
|
105 |
} |
|
106 |
||
107 |
/// <summary> |
|
108 |
/// Initializes a new instance of the <see cref="BsonReader"/> class. |
|
109 |
/// </summary> |
|
110 |
/// <param name="stream">The stream.</param> |
|
111 |
5045 |
public BsonReader(Stream stream)
|
112 |
5045 |
: this(stream, false, DateTimeKind.Local)
|
113 |
{ |
|
114 |
5045 |
}
|
115 |
||
116 |
/// <summary> |
|
117 |
/// Initializes a new instance of the <see cref="BsonReader"/> class. |
|
118 |
/// </summary> |
|
119 |
/// <param name="stream">The stream.</param> |
|
120 |
/// <param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param> |
|
121 |
/// <param name="dateTimeKindHandling">The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</param> |
|
122 |
5049 |
public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
|
123 |
{ |
|
124 |
5049 |
ValidationUtils.ArgumentNotNull(stream, "stream");
|
125 |
5049 |
_reader = new BinaryReader(stream);
|
126 |
5049 |
_stack = new List<ContainerContext>();
|
127 |
5049 |
_readRootValueAsArray = readRootValueAsArray;
|
128 |
5049 |
_dateTimeKindHandling = dateTimeKindHandling;
|
129 |
5049 |
}
|
130 |
||
131 |
private string ReadElement() |
|
132 |
{ |
|
133 |
120214 |
_currentElementType = ReadType();
|
134 |
120214 |
string elementName = ReadString();
|
135 |
120214 |
return elementName;
|
136 |
120214 |
}
|
137 |
||
138 |
/// <summary> |
|
139 |
/// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>. |
|
140 |
/// </summary> |
|
141 |
/// <returns> |
|
142 |
/// A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null. |
|
143 |
/// </returns> |
|
144 |
public override byte[] ReadAsBytes() |
|
145 |
{ |
|
146 |
1 |
Read();
|
147 |
1 |
if (TokenType != JsonToken.Bytes)
|
148 |
0 |
throw new JsonReaderException("Error reading bytes. Expected bytes but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
|
149 |
||
150 |
1 |
return (byte[])Value;
|
151 |
1 |
}
|
152 |
||
153 |
/// <summary> |
|
154 |
/// Reads the next JSON token from the stream. |
|
155 |
/// </summary> |
|
156 |
/// <returns> |
|
157 |
/// true if the next token was read successfully; false if there are no more tokens to read. |
|
158 |
/// </returns> |
|
159 |
public override bool Read() |
|
160 |
{ |
|
161 |
try |
|
162 |
{ |
|
163 |
255478 |
switch (_bsonReaderState)
|
164 |
{ |
|
165 |
case BsonReaderState.Normal: |
|
166 |
255465 |
return ReadNormal();
|
167 |
case BsonReaderState.ReferenceStart: |
|
168 |
case BsonReaderState.ReferenceRef: |
|
169 |
case BsonReaderState.ReferenceId: |
|
170 |
5 |
return ReadReference();
|
171 |
case BsonReaderState.CodeWScopeStart: |
|
172 |
case BsonReaderState.CodeWScopeCode: |
|
173 |
case BsonReaderState.CodeWScopeScope: |
|
174 |
case BsonReaderState.CodeWScopeScopeObject: |
|
175 |
case BsonReaderState.CodeWScopeScopeEnd: |
|
176 |
8 |
return ReadCodeWScope();
|
177 |
default: |
|
178 |
0 |
throw new JsonReaderException("Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState));
|
179 |
} |
|
180 |
} |
|
181 |
1 |
catch (EndOfStreamException)
|
182 |
{ |
|
183 |
1 |
return false;
|
184 |
} |
|
185 |
255478 |
}
|
186 |
||
187 |
private bool ReadCodeWScope() |
|
188 |
{ |
|
189 |
8 |
switch (_bsonReaderState)
|
190 |
{ |
|
191 |
case BsonReaderState.CodeWScopeStart: |
|
192 |
1 |
SetToken(JsonToken.PropertyName, "$code");
|
193 |
1 |
_bsonReaderState = BsonReaderState.CodeWScopeCode;
|
194 |
1 |
return true;
|
195 |
case BsonReaderState.CodeWScopeCode: |
|
196 |
// total CodeWScope size - not used |
|
197 |
1 |
ReadInt32();
|
198 |
||
199 |
1 |
SetToken(JsonToken.String, ReadLengthString());
|
200 |
1 |
_bsonReaderState = BsonReaderState.CodeWScopeScope;
|
201 |
1 |
return true;
|
202 |
case BsonReaderState.CodeWScopeScope: |
|
203 |
2 |
if (CurrentState == State.PostValue)
|
204 |
{ |
|
205 |
1 |
SetToken(JsonToken.PropertyName, "$scope");
|
206 |
1 |
return true;
|
207 |
} |
|
208 |
else |
|
209 |
{ |
|
210 |
1 |
SetToken(JsonToken.StartObject);
|
211 |
1 |
_bsonReaderState = BsonReaderState.CodeWScopeScopeObject;
|
212 |
||
213 |
1 |
ContainerContext newContext = new ContainerContext(BsonType.Object);
|
214 |
1 |
PushContext(newContext);
|
215 |
1 |
newContext.Length = ReadInt32();
|
216 |
||
217 |
1 |
return true;
|
218 |
} |
|
219 |
case BsonReaderState.CodeWScopeScopeObject: |
|
220 |
3 |
bool result = ReadNormal();
|
221 |
3 |
if (result && TokenType == JsonToken.EndObject)
|
222 |
1 |
_bsonReaderState = BsonReaderState.CodeWScopeScopeEnd;
|
223 |
||
224 |
3 |
return result;
|
225 |
case BsonReaderState.CodeWScopeScopeEnd: |
|
226 |
1 |
SetToken(JsonToken.EndObject);
|
227 |
1 |
_bsonReaderState = BsonReaderState.Normal;
|
228 |
1 |
return true;
|
229 |
default: |
|
230 |
0 |
throw new ArgumentOutOfRangeException();
|
231 |
} |
|
232 |
8 |
}
|
233 |
||
234 |
private bool ReadReference() |
|
235 |
{ |
|
236 |
5 |
switch (CurrentState)
|
237 |
{ |
|
238 |
case State.ObjectStart: |
|
239 |
{ |
|
240 |
1 |
SetToken(JsonToken.PropertyName, "$ref");
|
241 |
1 |
_bsonReaderState = BsonReaderState.ReferenceRef;
|
242 |
1 |
return true;
|
243 |
} |
|
244 |
case State.Property: |
|
245 |
{ |
|
246 |
2 |
if (_bsonReaderState == BsonReaderState.ReferenceRef)
|
247 |
{ |
|
248 |
1 |
SetToken(JsonToken.String, ReadLengthString());
|
249 |
1 |
return true;
|
250 |
} |
|
251 |
1 |
else if (_bsonReaderState == BsonReaderState.ReferenceId)
|
252 |
{ |
|
253 |
1 |
SetToken(JsonToken.Bytes, ReadBytes(12));
|
254 |
1 |
return true;
|
255 |
} |
|
256 |
else |
|
257 |
{ |
|
258 |
0 |
throw new JsonReaderException("Unexpected state when reading BSON reference: " + _bsonReaderState);
|
259 |
} |
|
260 |
} |
|
261 |
case State.PostValue: |
|
262 |
{ |
|
263 |
2 |
if (_bsonReaderState == BsonReaderState.ReferenceRef)
|
264 |
{ |
|
265 |
1 |
SetToken(JsonToken.PropertyName, "$id");
|
266 |
1 |
_bsonReaderState = BsonReaderState.ReferenceId;
|
267 |
1 |
return true;
|
268 |
} |
|
269 |
1 |
else if (_bsonReaderState == BsonReaderState.ReferenceId)
|
270 |
{ |
|
271 |
1 |
SetToken(JsonToken.EndObject);
|
272 |
1 |
_bsonReaderState = BsonReaderState.Normal;
|
273 |
1 |
return true;
|
274 |
} |
|
275 |
else |
|
276 |
{ |
|
277 |
0 |
throw new JsonReaderException("Unexpected state when reading BSON reference: " + _bsonReaderState);
|
278 |
} |
|
279 |
} |
|
280 |
default: |
|
281 |
0 |
throw new JsonReaderException("Unexpected state when reading BSON reference: " + CurrentState);
|
282 |
} |
|
283 |
5 |
}
|
284 |
||
285 |
private bool ReadNormal() |
|
286 |
{ |
|
287 |
255468 |
switch (CurrentState)
|
288 |
{ |
|
289 |
case State.Start: |
|
290 |
{ |
|
291 |
5049 |
JsonToken token = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray;
|
292 |
5049 |
BsonType type = (!_readRootValueAsArray) ? BsonType.Object : BsonType.Array;
|
293 |
||
294 |
5049 |
SetToken(token);
|
295 |
5049 |
ContainerContext newContext = new ContainerContext(type);
|
296 |
5049 |
PushContext(newContext);
|
297 |
5049 |
newContext.Length = ReadInt32();
|
298 |
5048 |
return true;
|
299 |
} |
|
300 |
case State.Complete: |
|
301 |
case State.Closed: |
|
302 |
0 |
return false;
|
303 |
case State.Property: |
|
304 |
{ |
|
305 |
95119 |
ReadType(_currentElementType);
|
306 |
95119 |
return true;
|
307 |
} |
|
308 |
case State.ObjectStart: |
|
309 |
case State.ArrayStart: |
|
310 |
case State.PostValue: |
|
311 |
155300 |
ContainerContext context = _currentContext;
|
312 |
155300 |
if (context == null)
|
313 |
18 |
return false;
|
314 |
||
315 |
155282 |
int lengthMinusEnd = context.Length - 1;
|
316 |
||
317 |
155282 |
if (context.Position < lengthMinusEnd)
|
318 |
{ |
|
319 |
120214 |
if (context.Type == BsonType.Array)
|
320 |
{ |
|
321 |
25088 |
ReadElement();
|
322 |
25088 |
ReadType(_currentElementType);
|
323 |
25088 |
return true;
|
324 |
} |
|
325 |
else |
|
326 |
{ |
|
327 |
95126 |
SetToken(JsonToken.PropertyName, ReadElement());
|
328 |
95126 |
return true;
|
329 |
} |
|
330 |
} |
|
331 |
35068 |
else if (context.Position == lengthMinusEnd)
|
332 |
{ |
|
333 |
35068 |
if (ReadByte() != 0)
|
334 |
0 |
throw new JsonReaderException("Unexpected end of object byte value.");
|
335 |
||
336 |
35068 |
PopContext();
|
337 |
35068 |
if (_currentContext != null)
|
338 |
30034 |
MovePosition(context.Length);
|
339 |
||
340 |
35068 |
JsonToken endToken = (context.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray;
|
341 |
35068 |
SetToken(endToken);
|
342 |
35068 |
return true;
|
343 |
} |
|
344 |
else |
|
345 |
{ |
|
346 |
0 |
throw new JsonReaderException("Read past end of current container context.");
|
347 |
} |
|
348 |
case State.ConstructorStart: |
|
349 |
0 |
break;
|
350 |
case State.Constructor: |
|
351 |
0 |
break;
|
352 |
case State.Error: |
|
353 |
0 |
break;
|
354 |
case State.Finished: |
|
355 |
0 |
break;
|
356 |
default: |
|
357 |
0 |
throw new ArgumentOutOfRangeException();
|
358 |
} |
|
359 |
||
360 |
0 |
return false;
|
361 |
255467 |
}
|
362 |
||
363 |
private void PopContext() |
|
364 |
{ |
|
365 |
35068 |
_stack.RemoveAt(_stack.Count - 1);
|
366 |
35068 |
if (_stack.Count == 0)
|
367 |
5034 |
_currentContext = null;
|
368 |
else |
|
369 |
30034 |
_currentContext = _stack[_stack.Count - 1];
|
370 |
35068 |
}
|
371 |
||
372 |
private void PushContext(ContainerContext newContext) |
|
373 |
{ |
|
374 |
35083 |
_stack.Add(newContext);
|
375 |
35083 |
_currentContext = newContext;
|
376 |
35083 |
}
|
377 |
||
378 |
private byte ReadByte() |
|
379 |
{ |
|
380 |
35074 |
MovePosition(1);
|
381 |
35074 |
return _reader.ReadByte();
|
382 |
35074 |
}
|
383 |
||
384 |
private void ReadType(BsonType type) |
|
385 |
{ |
|
386 |
120207 |
switch (type)
|
387 |
{ |
|
388 |
case BsonType.Number: |
|
389 |
5046 |
SetToken(JsonToken.Float, ReadDouble());
|
390 |
5046 |
break;
|
391 |
case BsonType.String: |
|
392 |
case BsonType.Symbol: |
|
393 |
40061 |
SetToken(JsonToken.String, ReadLengthString());
|
394 |
40061 |
break;
|
395 |
case BsonType.Object: |
|
396 |
{ |
|
397 |
20018 |
SetToken(JsonToken.StartObject);
|
398 |
||
399 |
20018 |
ContainerContext newContext = new ContainerContext(BsonType.Object);
|
400 |
20018 |
PushContext(newContext);
|
401 |
20018 |
newContext.Length = ReadInt32();
|
402 |
20018 |
break;
|
403 |
} |
|
404 |
case BsonType.Array: |
|
405 |
{ |
|
406 |
10015 |
SetToken(JsonToken.StartArray);
|
407 |
||
408 |
10015 |
ContainerContext newContext = new ContainerContext(BsonType.Array);
|
409 |
10015 |
PushContext(newContext);
|
410 |
10015 |
newContext.Length = ReadInt32();
|
411 |
10015 |
break;
|
412 |
} |
|
413 |
case BsonType.Binary: |
|
414 |
4 |
SetToken(JsonToken.Bytes, ReadBinary());
|
415 |
4 |
break;
|
416 |
case BsonType.Undefined: |
|
417 |
1 |
SetToken(JsonToken.Undefined);
|
418 |
1 |
break;
|
419 |
case BsonType.Oid: |
|
420 |
5 |
byte[] oid = ReadBytes(12);
|
421 |
5 |
SetToken(JsonToken.Bytes, oid);
|
422 |
5 |
break;
|
423 |
case BsonType.Boolean: |
|
424 |
2 |
bool b = Convert.ToBoolean(ReadByte());
|
425 |
2 |
SetToken(JsonToken.Boolean, b);
|
426 |
2 |
break;
|
427 |
case BsonType.Date: |
|
428 |
20015 |
long ticks = ReadInt64();
|
429 |
20015 |
DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks);
|
430 |
||
431 |
DateTime dateTime; |
|
432 |
20015 |
switch (DateTimeKindHandling)
|
433 |
{ |
|
434 |
case DateTimeKind.Unspecified: |
|
435 |
1 |
dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
|
436 |
1 |
break;
|
437 |
case DateTimeKind.Local: |
|
438 |
20011 |
dateTime = utcDateTime.ToLocalTime();
|
439 |
20011 |
break;
|
440 |
default: |
|
441 |
3 |
dateTime = utcDateTime;
|
442 |
3 |
break;
|
443 |
} |
|
444 |
||
445 |
20015 |
SetToken(JsonToken.Date, dateTime);
|
446 |
20015 |
break;
|
447 |
case BsonType.Null: |
|
448 |
10008 |
SetToken(JsonToken.Null);
|
449 |
10008 |
break;
|
450 |
case BsonType.Regex: |
|
451 |
6 |
string expression = ReadString();
|
452 |
6 |
string modifiers = ReadString();
|
453 |
||
454 |
6 |
string regex = @"/" + expression + @"/" + modifiers;
|
455 |
6 |
SetToken(JsonToken.String, regex);
|
456 |
6 |
break;
|
457 |
case BsonType.Reference: |
|
458 |
1 |
SetToken(JsonToken.StartObject);
|
459 |
1 |
_bsonReaderState = BsonReaderState.ReferenceStart;
|
460 |
1 |
break;
|
461 |
case BsonType.Code: |
|
462 |
1 |
SetToken(JsonToken.String, ReadLengthString());
|
463 |
1 |
break;
|
464 |
case BsonType.CodeWScope: |
|
465 |
1 |
SetToken(JsonToken.StartObject);
|
466 |
1 |
_bsonReaderState = BsonReaderState.CodeWScopeStart;
|
467 |
1 |
break;
|
468 |
case BsonType.Integer: |
|
469 |
15020 |
SetToken(JsonToken.Integer, (long)ReadInt32());
|
470 |
15020 |
break;
|
471 |
case BsonType.TimeStamp: |
|
472 |
case BsonType.Long: |
|
473 |
3 |
SetToken(JsonToken.Integer, ReadInt64());
|
474 |
3 |
break;
|
475 |
default: |
|
476 |
0 |
throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + type);
|
477 |
} |
|
478 |
120207 |
}
|
479 |
||
480 |
private byte[] ReadBinary() |
|
481 |
{ |
|
482 |
4 |
int dataLength = ReadInt32();
|
483 |
||
484 |
// BsonBinaryType not used |
|
485 |
4 |
ReadByte();
|
486 |
||
487 |
4 |
return ReadBytes(dataLength);
|
488 |
4 |
}
|
489 |
||
490 |
private string ReadString() |
|
491 |
{ |
|
492 |
120226 |
EnsureBuffers();
|
493 |
||
494 |
120226 |
StringBuilder builder = null;
|
495 |
||
496 |
120226 |
int totalBytesRead = 0;
|
497 |
// used in case of left over multibyte characters in the buffer |
|
498 |
120226 |
int offset = 0;
|
499 |
do |
|
500 |
{ |
|
501 |
120235 |
int count = offset;
|
502 |
byte b; |
|
503 |
827428 |
while (count < MaxCharBytesSize && (b = _reader.ReadByte()) > 0)
|
504 |
{ |
|
505 |
707193 |
_byteBuffer[count++] = b;
|
506 |
} |
|
507 |
120235 |
int byteCount = count - offset;
|
508 |
120235 |
totalBytesRead += byteCount;
|
509 |
||
510 |
120235 |
if (count < MaxCharBytesSize && builder == null)
|
511 |
{ |
|
512 |
// pref optimization to avoid reading into a string builder |
|
513 |
// if string is smaller than the buffer then return it directly |
|
514 |
120221 |
int length = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0);
|
515 |
||
516 |
120221 |
MovePosition(totalBytesRead + 1);
|
517 |
120221 |
return new string(_charBuffer, 0, length);
|
518 |
} |
|
519 |
else |
|
520 |
{ |
|
521 |
// calculate the index of the end of the last full character in the buffer |
|
522 |
14 |
int lastFullCharStop = GetLastFullCharStop(count - 1);
|
523 |
||
524 |
14 |
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
|
525 |
||
526 |
14 |
if (builder == null)
|
527 |
5 |
builder = new StringBuilder(MaxCharBytesSize * 2);
|
528 |
||
529 |
14 |
builder.Append(_charBuffer, 0, charCount);
|
530 |
||
531 |
14 |
if (lastFullCharStop < byteCount - 1)
|
532 |
{ |
|
533 |
2 |
offset = byteCount - lastFullCharStop - 1;
|
534 |
// copy left over multi byte characters to beginning of buffer for next iteration |
|
535 |
2 |
Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset);
|
536 |
} |
|
537 |
else |
|
538 |
{ |
|
539 |
// reached end of string |
|
540 |
12 |
if (count < MaxCharBytesSize)
|
541 |
{ |
|
542 |
5 |
MovePosition(totalBytesRead + 1);
|
543 |
5 |
return builder.ToString();
|
544 |
} |
|
545 |
||
546 |
7 |
offset = 0;
|
547 |
} |
|
548 |
} |
|
549 |
} |
|
550 |
9 |
while (true);
|
551 |
120226 |
}
|
552 |
||
553 |
private string ReadLengthString() |
|
554 |
{ |
|
555 |
40064 |
int length = ReadInt32();
|
556 |
||
557 |
40064 |
MovePosition(length);
|
558 |
||
559 |
40064 |
string s = GetString(length - 1);
|
560 |
40064 |
_reader.ReadByte();
|
561 |
||
562 |
40064 |
return s;
|
563 |
40064 |
}
|
564 |
||
565 |
private string GetString(int length) |
|
566 |
{ |
|
567 |
40064 |
if (length == 0)
|
568 |
3 |
return string.Empty;
|
569 |
||
570 |
40061 |
EnsureBuffers();
|
571 |
||
572 |
40061 |
StringBuilder builder = null;
|
573 |
||
574 |
40061 |
int totalBytesRead = 0;
|
575 |
40061 |
int offset = 0;
|
576 |
do |
|
577 |
{ |
|
578 |
40070 |
int count = ((length - totalBytesRead) > MaxCharBytesSize - offset)
|
579 |
40070 |
? MaxCharBytesSize - offset
|
580 |
40070 |
: length - totalBytesRead;
|
581 |
||
582 |
40070 |
int byteCount = _reader.BaseStream.Read(_byteBuffer, offset, count);
|
583 |
||
584 |
40070 |
if (byteCount == 0)
|
585 |
0 |
throw new EndOfStreamException("Unable to read beyond the end of the stream.");
|
586 |
||
587 |
40070 |
byteCount += offset;
|
588 |
||
589 |
40070 |
if (totalBytesRead == 0 && byteCount == length)
|
590 |
{ |
|
591 |
// pref optimization to avoid reading into a string builder |
|
592 |
// first iteration and all bytes read then return string directly |
|
593 |
40056 |
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0);
|
594 |
40056 |
return new string(_charBuffer, 0, charCount);
|
595 |
} |
|
596 |
else |
|
597 |
{ |
|
598 |
14 |
int lastFullCharStop = GetLastFullCharStop(byteCount - 1);
|
599 |
||
600 |
14 |
if (builder == null)
|
601 |
5 |
builder = new StringBuilder(length);
|
602 |
||
603 |
14 |
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
|
604 |
14 |
builder.Append(_charBuffer, 0, charCount);
|
605 |
||
606 |
14 |
if (lastFullCharStop < byteCount - 1)
|
607 |
{ |
|
608 |
2 |
offset = byteCount - lastFullCharStop - 1;
|
609 |
// copy left over multi byte characters to beginning of buffer for next iteration |
|
610 |
2 |
Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset);
|
611 |
} |
|
612 |
else |
|
613 |
{ |
|
614 |
12 |
offset = 0;
|
615 |
} |
|
616 |
||
617 |
14 |
totalBytesRead += (byteCount - offset);
|
618 |
} |
|
619 |
} |
|
620 |
14 |
while (totalBytesRead < length);
|
621 |
||
622 |
5 |
return builder.ToString();
|
623 |
40064 |
}
|
624 |
||
625 |
private int GetLastFullCharStop(int start) |
|
626 |
{ |
|
627 |
28 |
int lookbackPos = start;
|
628 |
28 |
int bis = 0;
|
629 |
46 |
while (lookbackPos >= 0)
|
630 |
{ |
|
631 |
46 |
bis = BytesInSequence(_byteBuffer[lookbackPos]);
|
632 |
46 |
if (bis == 0)
|
633 |
{ |
|
634 |
18 |
lookbackPos--;
|
635 |
18 |
continue;
|
636 |
} |
|
637 |
28 |
else if (bis == 1)
|
638 |
{ |
|
639 |
16 |
break;
|
640 |
} |
|
641 |
else |
|
642 |
{ |
|
643 |
12 |
lookbackPos--;
|
644 |
12 |
break;
|
645 |
} |
|
646 |
} |
|
647 |
28 |
if (bis == start - lookbackPos)
|
648 |
{ |
|
649 |
//Full character. |
|
650 |
8 |
return start;
|
651 |
} |
|
652 |
else |
|
653 |
{ |
|
654 |
20 |
return lookbackPos;
|
655 |
} |
|
656 |
28 |
}
|
657 |
||
658 |
private int BytesInSequence(byte b) |
|
659 |
{ |
|
660 |
16 |
if (b <= _seqRange1[1]) return 1;
|
661 |
0 |
if (b >= _seqRange2[0] && b <= _seqRange2[1]) return 2; |
662 |
12 |
if (b >= _seqRange3[0] && b <= _seqRange3[1]) return 3;
|
663 |
0 |
if (b >= _seqRange4[0] && b <= _seqRange4[1]) return 4; |
664 |
18 |
return 0;
|
665 |
46 |
}
|
666 |
||
667 |
private void EnsureBuffers() |
|
668 |
{ |
|
669 |
160287 |
if (_byteBuffer == null)
|
670 |
{ |
|
671 |
5048 |
_byteBuffer = new byte[MaxCharBytesSize];
|
672 |
} |
|
673 |
160287 |
if (_charBuffer == null)
|
674 |
{ |
|
675 |
5048 |
int charBufferSize = Encoding.UTF8.GetMaxCharCount(MaxCharBytesSize);
|
676 |
5048 |
_charBuffer = new char[charBufferSize];
|
677 |
} |
|
678 |
160287 |
}
|
679 |
||
680 |
private double ReadDouble() |
|
681 |
{ |
|
682 |
5046 |
MovePosition(8);
|
683 |
5046 |
return _reader.ReadDouble();
|
684 |
5046 |
}
|
685 |
||
686 |
private int ReadInt32() |
|
687 |
{ |
|
688 |
90172 |
MovePosition(4);
|
689 |
90172 |
return _reader.ReadInt32();
|
690 |
90171 |
}
|
691 |
||
692 |
private long ReadInt64() |
|
693 |
{ |
|
694 |
20018 |
MovePosition(8);
|
695 |
20018 |
return _reader.ReadInt64();
|
696 |
20018 |
}
|
697 |
||
698 |
private BsonType ReadType() |
|
699 |
{ |
|
700 |
120214 |
MovePosition(1);
|
701 |
120214 |
return (BsonType)_reader.ReadSByte();
|
702 |
120214 |
}
|
703 |
||
704 |
private void MovePosition(int count) |
|
705 |
{ |
|
706 |
460858 |
_currentContext.Position += count;
|
707 |
460858 |
}
|
708 |
||
709 |
private byte[] ReadBytes(int count) |
|
710 |
{ |
|
711 |
10 |
MovePosition(count);
|
712 |
10 |
return _reader.ReadBytes(count);
|
713 |
10 |
}
|
714 |
} |
|
715 |
} |