2
// Copyright (c) 2007 James Newton-King
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
13
// The above copyright notice and this permission notice shall be
14
// included in all copies or substantial portions of the Software.
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.
27
using System.Collections.Generic;
28
using System.Globalization;
31
using Newtonsoft.Json.Utilities;
32
using Newtonsoft.Json.Linq;
34
namespace Newtonsoft.Json.Bson
37
/// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data.
39
public class BsonReader : JsonReader
41
private const int MaxCharBytesSize = 128;
42
private static readonly byte[] _seqRange1 = new byte[] { 0, 127 }; // range of 1-byte sequence
43
private static readonly byte[] _seqRange2 = new byte[] { 194, 223 }; // range of 2-byte sequence
44
private static readonly byte[] _seqRange3 = new byte[] { 224, 239 }; // range of 3-byte sequence
45
private static readonly byte[] _seqRange4 = new byte[] { 240, 244 }; // range of 4-byte sequence
47
private readonly BinaryReader _reader;
48
private readonly List<ContainerContext> _stack;
50
private byte[] _byteBuffer;
51
private char[] _charBuffer;
53
private BsonType _currentElementType;
54
private BsonReaderState _bsonReaderState;
55
private ContainerContext _currentContext;
57
private bool _readRootValueAsArray;
58
private DateTimeKind _dateTimeKindHandling;
60
private enum BsonReaderState
69
CodeWScopeScopeObject,
73
private class ContainerContext
75
public readonly BsonType Type;
79
public ContainerContext(BsonType type)
86
/// Gets or sets a value indicating whether the root object will be read as a JSON array.
89
/// <c>true</c> if the root object will be read as a JSON array; otherwise, <c>false</c>.
91
public bool ReadRootValueAsArray
93
get { return _readRootValueAsArray; }
94
set { _readRootValueAsArray = value; }
98
/// Gets or sets the <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.
100
/// <value>The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</value>
101
public DateTimeKind DateTimeKindHandling
103
get { return _dateTimeKindHandling; }
104
set { _dateTimeKindHandling = value; }
108
/// Initializes a new instance of the <see cref="BsonReader"/> class.
110
/// <param name="stream">The stream.</param>
111
public BsonReader(Stream stream)
112
: this(stream, false, DateTimeKind.Local)
117
/// Initializes a new instance of the <see cref="BsonReader"/> class.
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
public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
124
ValidationUtils.ArgumentNotNull(stream, "stream");
125
_reader = new BinaryReader(stream);
126
_stack = new List<ContainerContext>();
127
_readRootValueAsArray = readRootValueAsArray;
128
_dateTimeKindHandling = dateTimeKindHandling;
131
private string ReadElement()
133
_currentElementType = ReadType();
134
string elementName = ReadString();
139
/// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>.
142
/// A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null.
144
public override byte[] ReadAsBytes()
147
if (TokenType != JsonToken.Bytes)
148
throw new JsonReaderException("Error reading bytes. Expected bytes but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType));
150
return (byte[])Value;
154
/// Reads the next JSON token from the stream.
157
/// true if the next token was read successfully; false if there are no more tokens to read.
159
public override bool Read()
163
switch (_bsonReaderState)
165
case BsonReaderState.Normal:
167
case BsonReaderState.ReferenceStart:
168
case BsonReaderState.ReferenceRef:
169
case BsonReaderState.ReferenceId:
170
return ReadReference();
171
case BsonReaderState.CodeWScopeStart:
172
case BsonReaderState.CodeWScopeCode:
173
case BsonReaderState.CodeWScopeScope:
174
case BsonReaderState.CodeWScopeScopeObject:
175
case BsonReaderState.CodeWScopeScopeEnd:
176
return ReadCodeWScope();
178
throw new JsonReaderException("Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState));
181
catch (EndOfStreamException)
187
private bool ReadCodeWScope()
189
switch (_bsonReaderState)
191
case BsonReaderState.CodeWScopeStart:
192
SetToken(JsonToken.PropertyName, "$code");
193
_bsonReaderState = BsonReaderState.CodeWScopeCode;
195
case BsonReaderState.CodeWScopeCode:
196
// total CodeWScope size - not used
199
SetToken(JsonToken.String, ReadLengthString());
200
_bsonReaderState = BsonReaderState.CodeWScopeScope;
202
case BsonReaderState.CodeWScopeScope:
203
if (CurrentState == State.PostValue)
205
SetToken(JsonToken.PropertyName, "$scope");
210
SetToken(JsonToken.StartObject);
211
_bsonReaderState = BsonReaderState.CodeWScopeScopeObject;
213
ContainerContext newContext = new ContainerContext(BsonType.Object);
214
PushContext(newContext);
215
newContext.Length = ReadInt32();
219
case BsonReaderState.CodeWScopeScopeObject:
220
bool result = ReadNormal();
221
if (result && TokenType == JsonToken.EndObject)
222
_bsonReaderState = BsonReaderState.CodeWScopeScopeEnd;
225
case BsonReaderState.CodeWScopeScopeEnd:
226
SetToken(JsonToken.EndObject);
227
_bsonReaderState = BsonReaderState.Normal;
230
throw new ArgumentOutOfRangeException();
234
private bool ReadReference()
236
switch (CurrentState)
238
case State.ObjectStart:
240
SetToken(JsonToken.PropertyName, "$ref");
241
_bsonReaderState = BsonReaderState.ReferenceRef;
246
if (_bsonReaderState == BsonReaderState.ReferenceRef)
248
SetToken(JsonToken.String, ReadLengthString());
251
else if (_bsonReaderState == BsonReaderState.ReferenceId)
253
SetToken(JsonToken.Bytes, ReadBytes(12));
258
throw new JsonReaderException("Unexpected state when reading BSON reference: " + _bsonReaderState);
261
case State.PostValue:
263
if (_bsonReaderState == BsonReaderState.ReferenceRef)
265
SetToken(JsonToken.PropertyName, "$id");
266
_bsonReaderState = BsonReaderState.ReferenceId;
269
else if (_bsonReaderState == BsonReaderState.ReferenceId)
271
SetToken(JsonToken.EndObject);
272
_bsonReaderState = BsonReaderState.Normal;
277
throw new JsonReaderException("Unexpected state when reading BSON reference: " + _bsonReaderState);
281
throw new JsonReaderException("Unexpected state when reading BSON reference: " + CurrentState);
285
private bool ReadNormal()
287
switch (CurrentState)
291
JsonToken token = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray;
292
BsonType type = (!_readRootValueAsArray) ? BsonType.Object : BsonType.Array;
295
ContainerContext newContext = new ContainerContext(type);
296
PushContext(newContext);
297
newContext.Length = ReadInt32();
305
ReadType(_currentElementType);
308
case State.ObjectStart:
309
case State.ArrayStart:
310
case State.PostValue:
311
ContainerContext context = _currentContext;
315
int lengthMinusEnd = context.Length - 1;
317
if (context.Position < lengthMinusEnd)
319
if (context.Type == BsonType.Array)
322
ReadType(_currentElementType);
327
SetToken(JsonToken.PropertyName, ReadElement());
331
else if (context.Position == lengthMinusEnd)
334
throw new JsonReaderException("Unexpected end of object byte value.");
337
if (_currentContext != null)
338
MovePosition(context.Length);
340
JsonToken endToken = (context.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray;
346
throw new JsonReaderException("Read past end of current container context.");
348
case State.ConstructorStart:
350
case State.Constructor:
357
throw new ArgumentOutOfRangeException();
363
private void PopContext()
365
_stack.RemoveAt(_stack.Count - 1);
366
if (_stack.Count == 0)
367
_currentContext = null;
369
_currentContext = _stack[_stack.Count - 1];
372
private void PushContext(ContainerContext newContext)
374
_stack.Add(newContext);
375
_currentContext = newContext;
378
private byte ReadByte()
381
return _reader.ReadByte();
384
private void ReadType(BsonType type)
388
case BsonType.Number:
389
SetToken(JsonToken.Float, ReadDouble());
391
case BsonType.String:
392
case BsonType.Symbol:
393
SetToken(JsonToken.String, ReadLengthString());
395
case BsonType.Object:
397
SetToken(JsonToken.StartObject);
399
ContainerContext newContext = new ContainerContext(BsonType.Object);
400
PushContext(newContext);
401
newContext.Length = ReadInt32();
406
SetToken(JsonToken.StartArray);
408
ContainerContext newContext = new ContainerContext(BsonType.Array);
409
PushContext(newContext);
410
newContext.Length = ReadInt32();
413
case BsonType.Binary:
414
SetToken(JsonToken.Bytes, ReadBinary());
416
case BsonType.Undefined:
417
SetToken(JsonToken.Undefined);
420
byte[] oid = ReadBytes(12);
421
SetToken(JsonToken.Bytes, oid);
423
case BsonType.Boolean:
424
bool b = Convert.ToBoolean(ReadByte());
425
SetToken(JsonToken.Boolean, b);
428
long ticks = ReadInt64();
429
DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks);
432
switch (DateTimeKindHandling)
434
case DateTimeKind.Unspecified:
435
dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
437
case DateTimeKind.Local:
438
dateTime = utcDateTime.ToLocalTime();
441
dateTime = utcDateTime;
445
SetToken(JsonToken.Date, dateTime);
448
SetToken(JsonToken.Null);
451
string expression = ReadString();
452
string modifiers = ReadString();
454
string regex = @"/" + expression + @"/" + modifiers;
455
SetToken(JsonToken.String, regex);
457
case BsonType.Reference:
458
SetToken(JsonToken.StartObject);
459
_bsonReaderState = BsonReaderState.ReferenceStart;
462
SetToken(JsonToken.String, ReadLengthString());
464
case BsonType.CodeWScope:
465
SetToken(JsonToken.StartObject);
466
_bsonReaderState = BsonReaderState.CodeWScopeStart;
468
case BsonType.Integer:
469
SetToken(JsonToken.Integer, (long)ReadInt32());
471
case BsonType.TimeStamp:
473
SetToken(JsonToken.Integer, ReadInt64());
476
throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + type);
480
private byte[] ReadBinary()
482
int dataLength = ReadInt32();
484
// BsonBinaryType not used
487
return ReadBytes(dataLength);
490
private string ReadString()
494
StringBuilder builder = null;
496
int totalBytesRead = 0;
497
// used in case of left over multibyte characters in the buffer
503
while (count < MaxCharBytesSize && (b = _reader.ReadByte()) > 0)
505
_byteBuffer[count++] = b;
507
int byteCount = count - offset;
508
totalBytesRead += byteCount;
510
if (count < MaxCharBytesSize && builder == null)
512
// pref optimization to avoid reading into a string builder
513
// if string is smaller than the buffer then return it directly
514
int length = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0);
516
MovePosition(totalBytesRead + 1);
517
return new string(_charBuffer, 0, length);
521
// calculate the index of the end of the last full character in the buffer
522
int lastFullCharStop = GetLastFullCharStop(count - 1);
524
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
527
builder = new StringBuilder(MaxCharBytesSize * 2);
529
builder.Append(_charBuffer, 0, charCount);
531
if (lastFullCharStop < byteCount - 1)
533
offset = byteCount - lastFullCharStop - 1;
534
// copy left over multi byte characters to beginning of buffer for next iteration
535
Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset);
539
// reached end of string
540
if (count < MaxCharBytesSize)
542
MovePosition(totalBytesRead + 1);
543
return builder.ToString();
553
private string ReadLengthString()
555
int length = ReadInt32();
557
MovePosition(length);
559
string s = GetString(length - 1);
565
private string GetString(int length)
572
StringBuilder builder = null;
574
int totalBytesRead = 0;
576
// used in case of left over multibyte characters in the buffer
580
int count = ((length - totalBytesRead) > MaxCharBytesSize - offset)
581
? MaxCharBytesSize - offset
582
: length - totalBytesRead;
584
int byteCount = _reader.BaseStream.Read(_byteBuffer, offset, count);
587
throw new EndOfStreamException("Unable to read beyond the end of the stream.");
589
totalBytesRead += byteCount;
591
// Above, byteCount is how many bytes we read this time.
592
// Below, byteCount is how many bytes are in the _byteBuffer.
595
if (byteCount == length)
597
// pref optimization to avoid reading into a string builder
598
// first iteration and all bytes read then return string directly
599
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0);
600
return new string(_charBuffer, 0, charCount);
604
int lastFullCharStop = GetLastFullCharStop(byteCount - 1);
607
builder = new StringBuilder(length);
609
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
610
builder.Append(_charBuffer, 0, charCount);
612
if (lastFullCharStop < byteCount - 1)
614
offset = byteCount - lastFullCharStop - 1;
615
// copy left over multi byte characters to beginning of buffer for next iteration
616
Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset);
624
while (totalBytesRead < length);
626
return builder.ToString();
629
private int GetLastFullCharStop(int start)
631
int lookbackPos = start;
633
while (lookbackPos >= 0)
635
bis = BytesInSequence(_byteBuffer[lookbackPos]);
651
if (bis == start - lookbackPos)
662
private int BytesInSequence(byte b)
664
if (b <= _seqRange1[1]) return 1;
665
if (b >= _seqRange2[0] && b <= _seqRange2[1]) return 2;
666
if (b >= _seqRange3[0] && b <= _seqRange3[1]) return 3;
667
if (b >= _seqRange4[0] && b <= _seqRange4[1]) return 4;
671
private void EnsureBuffers()
673
if (_byteBuffer == null)
675
_byteBuffer = new byte[MaxCharBytesSize];
677
if (_charBuffer == null)
679
int charBufferSize = Encoding.UTF8.GetMaxCharCount(MaxCharBytesSize);
680
_charBuffer = new char[charBufferSize];
684
private double ReadDouble()
687
return _reader.ReadDouble();
690
private int ReadInt32()
693
return _reader.ReadInt32();
696
private long ReadInt64()
699
return _reader.ReadInt64();
702
private BsonType ReadType()
705
return (BsonType)_reader.ReadSByte();
708
private void MovePosition(int count)
710
_currentContext.Position += count;
713
private byte[] ReadBytes(int count)
716
return _reader.ReadBytes(count);
2
// Copyright (c) 2007 James Newton-King
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
13
// The above copyright notice and this permission notice shall be
14
// included in all copies or substantial portions of the Software.
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.
27
using System.Collections.Generic;
28
using System.Globalization;
31
using Newtonsoft.Json.Utilities;
32
using Newtonsoft.Json.Linq;
34
namespace Newtonsoft.Json.Bson
37
/// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data.
39
public class BsonReader : JsonReader
41
private const int MaxCharBytesSize = 128;
42
private static readonly byte[] SeqRange1 = new byte[] {0, 127}; // range of 1-byte sequence
43
private static readonly byte[] SeqRange2 = new byte[] {194, 223}; // range of 2-byte sequence
44
private static readonly byte[] SeqRange3 = new byte[] {224, 239}; // range of 3-byte sequence
45
private static readonly byte[] SeqRange4 = new byte[] {240, 244}; // range of 4-byte sequence
47
private readonly BinaryReader _reader;
48
private readonly List<ContainerContext> _stack;
50
private byte[] _byteBuffer;
51
private char[] _charBuffer;
53
private BsonType _currentElementType;
54
private BsonReaderState _bsonReaderState;
55
private ContainerContext _currentContext;
57
private bool _readRootValueAsArray;
58
private bool _jsonNet35BinaryCompatibility;
59
private DateTimeKind _dateTimeKindHandling;
61
private enum BsonReaderState
70
CodeWScopeScopeObject,
74
private class ContainerContext
76
public readonly BsonType Type;
80
public ContainerContext(BsonType type)
87
/// Gets or sets a value indicating whether binary data reading should compatible with incorrect Json.NET 3.5 written binary.
90
/// <c>true</c> if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, <c>false</c>.
92
public bool JsonNet35BinaryCompatibility
94
get { return _jsonNet35BinaryCompatibility; }
95
set { _jsonNet35BinaryCompatibility = value; }
99
/// Gets or sets a value indicating whether the root object will be read as a JSON array.
102
/// <c>true</c> if the root object will be read as a JSON array; otherwise, <c>false</c>.
104
public bool ReadRootValueAsArray
106
get { return _readRootValueAsArray; }
107
set { _readRootValueAsArray = value; }
111
/// Gets or sets the <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.
113
/// <value>The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</value>
114
public DateTimeKind DateTimeKindHandling
116
get { return _dateTimeKindHandling; }
117
set { _dateTimeKindHandling = value; }
121
/// Initializes a new instance of the <see cref="BsonReader"/> class.
123
/// <param name="stream">The stream.</param>
124
public BsonReader(Stream stream)
125
: this(stream, false, DateTimeKind.Local)
130
/// Initializes a new instance of the <see cref="BsonReader"/> class.
132
/// <param name="reader">The reader.</param>
133
public BsonReader(BinaryReader reader)
134
: this(reader, false, DateTimeKind.Local)
139
/// Initializes a new instance of the <see cref="BsonReader"/> class.
141
/// <param name="stream">The stream.</param>
142
/// <param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
143
/// <param name="dateTimeKindHandling">The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</param>
144
public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
146
ValidationUtils.ArgumentNotNull(stream, "stream");
147
_reader = new BinaryReader(stream);
148
_stack = new List<ContainerContext>();
149
_readRootValueAsArray = readRootValueAsArray;
150
_dateTimeKindHandling = dateTimeKindHandling;
154
/// Initializes a new instance of the <see cref="BsonReader"/> class.
156
/// <param name="reader">The reader.</param>
157
/// <param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
158
/// <param name="dateTimeKindHandling">The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</param>
159
public BsonReader(BinaryReader reader, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
161
ValidationUtils.ArgumentNotNull(reader, "reader");
163
_stack = new List<ContainerContext>();
164
_readRootValueAsArray = readRootValueAsArray;
165
_dateTimeKindHandling = dateTimeKindHandling;
168
private string ReadElement()
170
_currentElementType = ReadType();
171
string elementName = ReadString();
176
/// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>.
179
/// A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
181
public override byte[] ReadAsBytes()
183
return ReadAsBytesInternal();
187
/// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
189
/// <returns>A <see cref="Nullable{Decimal}"/>. This method will return <c>null</c> at the end of an array.</returns>
190
public override decimal? ReadAsDecimal()
192
return ReadAsDecimalInternal();
196
/// Reads the next JSON token from the stream as a <see cref="Nullable{Int32}"/>.
198
/// <returns>A <see cref="Nullable{Int32}"/>. This method will return <c>null</c> at the end of an array.</returns>
199
public override int? ReadAsInt32()
201
return ReadAsInt32Internal();
205
/// Reads the next JSON token from the stream as a <see cref="String"/>.
207
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
208
public override string ReadAsString()
210
return ReadAsStringInternal();
214
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
216
/// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
217
public override DateTime? ReadAsDateTime()
219
return ReadAsDateTimeInternal();
224
/// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>.
227
/// A <see cref="Nullable{DateTimeOffset}"/>. This method will return <c>null</c> at the end of an array.
229
public override DateTimeOffset? ReadAsDateTimeOffset()
231
return ReadAsDateTimeOffsetInternal();
236
/// Reads the next JSON token from the stream.
239
/// true if the next token was read successfully; false if there are no more tokens to read.
241
public override bool Read()
243
_readType = Json.ReadType.Read;
245
return ReadInternal();
248
internal override bool ReadInternal()
254
switch (_bsonReaderState)
256
case BsonReaderState.Normal:
257
success = ReadNormal();
259
case BsonReaderState.ReferenceStart:
260
case BsonReaderState.ReferenceRef:
261
case BsonReaderState.ReferenceId:
262
success = ReadReference();
264
case BsonReaderState.CodeWScopeStart:
265
case BsonReaderState.CodeWScopeCode:
266
case BsonReaderState.CodeWScopeScope:
267
case BsonReaderState.CodeWScopeScopeObject:
268
case BsonReaderState.CodeWScopeScopeEnd:
269
success = ReadCodeWScope();
272
throw JsonReaderException.Create(this, "Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState));
277
SetToken(JsonToken.None);
283
catch (EndOfStreamException)
285
SetToken(JsonToken.None);
291
/// Changes the <see cref="JsonReader.State"/> to Closed.
293
public override void Close()
297
if (CloseInput && _reader != null)
298
#if !(NETFX_CORE || PORTABLE)
305
private bool ReadCodeWScope()
307
switch (_bsonReaderState)
309
case BsonReaderState.CodeWScopeStart:
310
SetToken(JsonToken.PropertyName, "$code");
311
_bsonReaderState = BsonReaderState.CodeWScopeCode;
313
case BsonReaderState.CodeWScopeCode:
314
// total CodeWScope size - not used
317
SetToken(JsonToken.String, ReadLengthString());
318
_bsonReaderState = BsonReaderState.CodeWScopeScope;
320
case BsonReaderState.CodeWScopeScope:
321
if (CurrentState == State.PostValue)
323
SetToken(JsonToken.PropertyName, "$scope");
328
SetToken(JsonToken.StartObject);
329
_bsonReaderState = BsonReaderState.CodeWScopeScopeObject;
331
ContainerContext newContext = new ContainerContext(BsonType.Object);
332
PushContext(newContext);
333
newContext.Length = ReadInt32();
337
case BsonReaderState.CodeWScopeScopeObject:
338
bool result = ReadNormal();
339
if (result && TokenType == JsonToken.EndObject)
340
_bsonReaderState = BsonReaderState.CodeWScopeScopeEnd;
343
case BsonReaderState.CodeWScopeScopeEnd:
344
SetToken(JsonToken.EndObject);
345
_bsonReaderState = BsonReaderState.Normal;
348
throw new ArgumentOutOfRangeException();
352
private bool ReadReference()
354
switch (CurrentState)
356
case State.ObjectStart:
358
SetToken(JsonToken.PropertyName, "$ref");
359
_bsonReaderState = BsonReaderState.ReferenceRef;
364
if (_bsonReaderState == BsonReaderState.ReferenceRef)
366
SetToken(JsonToken.String, ReadLengthString());
369
else if (_bsonReaderState == BsonReaderState.ReferenceId)
371
SetToken(JsonToken.Bytes, ReadBytes(12));
376
throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState);
379
case State.PostValue:
381
if (_bsonReaderState == BsonReaderState.ReferenceRef)
383
SetToken(JsonToken.PropertyName, "$id");
384
_bsonReaderState = BsonReaderState.ReferenceId;
387
else if (_bsonReaderState == BsonReaderState.ReferenceId)
389
SetToken(JsonToken.EndObject);
390
_bsonReaderState = BsonReaderState.Normal;
395
throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState);
399
throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + CurrentState);
403
private bool ReadNormal()
405
switch (CurrentState)
409
JsonToken token = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray;
410
BsonType type = (!_readRootValueAsArray) ? BsonType.Object : BsonType.Array;
413
ContainerContext newContext = new ContainerContext(type);
414
PushContext(newContext);
415
newContext.Length = ReadInt32();
423
ReadType(_currentElementType);
426
case State.ObjectStart:
427
case State.ArrayStart:
428
case State.PostValue:
429
ContainerContext context = _currentContext;
433
int lengthMinusEnd = context.Length - 1;
435
if (context.Position < lengthMinusEnd)
437
if (context.Type == BsonType.Array)
440
ReadType(_currentElementType);
445
SetToken(JsonToken.PropertyName, ReadElement());
449
else if (context.Position == lengthMinusEnd)
452
throw JsonReaderException.Create(this, "Unexpected end of object byte value.");
455
if (_currentContext != null)
456
MovePosition(context.Length);
458
JsonToken endToken = (context.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray;
464
throw JsonReaderException.Create(this, "Read past end of current container context.");
466
case State.ConstructorStart:
468
case State.Constructor:
475
throw new ArgumentOutOfRangeException();
481
private void PopContext()
483
_stack.RemoveAt(_stack.Count - 1);
484
if (_stack.Count == 0)
485
_currentContext = null;
487
_currentContext = _stack[_stack.Count - 1];
490
private void PushContext(ContainerContext newContext)
492
_stack.Add(newContext);
493
_currentContext = newContext;
496
private byte ReadByte()
499
return _reader.ReadByte();
502
private void ReadType(BsonType type)
506
case BsonType.Number:
507
SetToken(JsonToken.Float, ReadDouble());
509
case BsonType.String:
510
case BsonType.Symbol:
511
SetToken(JsonToken.String, ReadLengthString());
513
case BsonType.Object:
515
SetToken(JsonToken.StartObject);
517
ContainerContext newContext = new ContainerContext(BsonType.Object);
518
PushContext(newContext);
519
newContext.Length = ReadInt32();
524
SetToken(JsonToken.StartArray);
526
ContainerContext newContext = new ContainerContext(BsonType.Array);
527
PushContext(newContext);
528
newContext.Length = ReadInt32();
531
case BsonType.Binary:
532
SetToken(JsonToken.Bytes, ReadBinary());
534
case BsonType.Undefined:
535
SetToken(JsonToken.Undefined);
538
byte[] oid = ReadBytes(12);
539
SetToken(JsonToken.Bytes, oid);
541
case BsonType.Boolean:
542
bool b = Convert.ToBoolean(ReadByte());
543
SetToken(JsonToken.Boolean, b);
546
long ticks = ReadInt64();
547
DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks);
550
switch (DateTimeKindHandling)
552
case DateTimeKind.Unspecified:
553
dateTime = DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified);
555
case DateTimeKind.Local:
556
dateTime = utcDateTime.ToLocalTime();
559
dateTime = utcDateTime;
563
SetToken(JsonToken.Date, dateTime);
566
SetToken(JsonToken.Null);
569
string expression = ReadString();
570
string modifiers = ReadString();
572
string regex = @"/" + expression + @"/" + modifiers;
573
SetToken(JsonToken.String, regex);
575
case BsonType.Reference:
576
SetToken(JsonToken.StartObject);
577
_bsonReaderState = BsonReaderState.ReferenceStart;
580
SetToken(JsonToken.String, ReadLengthString());
582
case BsonType.CodeWScope:
583
SetToken(JsonToken.StartObject);
584
_bsonReaderState = BsonReaderState.CodeWScopeStart;
586
case BsonType.Integer:
587
SetToken(JsonToken.Integer, (long) ReadInt32());
589
case BsonType.TimeStamp:
591
SetToken(JsonToken.Integer, ReadInt64());
594
throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + type);
598
private byte[] ReadBinary()
600
int dataLength = ReadInt32();
602
BsonBinaryType binaryType = (BsonBinaryType) ReadByte();
604
#pragma warning disable 612,618
605
// the old binary type has the data length repeated in the data for some reason
606
if (binaryType == BsonBinaryType.Data && !_jsonNet35BinaryCompatibility)
608
dataLength = ReadInt32();
610
#pragma warning restore 612,618
612
return ReadBytes(dataLength);
615
private string ReadString()
619
StringBuilder builder = null;
621
int totalBytesRead = 0;
622
// used in case of left over multibyte characters in the buffer
628
while (count < MaxCharBytesSize && (b = _reader.ReadByte()) > 0)
630
_byteBuffer[count++] = b;
632
int byteCount = count - offset;
633
totalBytesRead += byteCount;
635
if (count < MaxCharBytesSize && builder == null)
637
// pref optimization to avoid reading into a string builder
638
// if string is smaller than the buffer then return it directly
639
int length = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0);
641
MovePosition(totalBytesRead + 1);
642
return new string(_charBuffer, 0, length);
646
// calculate the index of the end of the last full character in the buffer
647
int lastFullCharStop = GetLastFullCharStop(count - 1);
649
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
652
builder = new StringBuilder(MaxCharBytesSize*2);
654
builder.Append(_charBuffer, 0, charCount);
656
if (lastFullCharStop < byteCount - 1)
658
offset = byteCount - lastFullCharStop - 1;
659
// copy left over multi byte characters to beginning of buffer for next iteration
660
Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset);
664
// reached end of string
665
if (count < MaxCharBytesSize)
667
MovePosition(totalBytesRead + 1);
668
return builder.ToString();
677
private string ReadLengthString()
679
int length = ReadInt32();
681
MovePosition(length);
683
string s = GetString(length - 1);
689
private string GetString(int length)
696
StringBuilder builder = null;
698
int totalBytesRead = 0;
700
// used in case of left over multibyte characters in the buffer
704
int count = ((length - totalBytesRead) > MaxCharBytesSize - offset)
705
? MaxCharBytesSize - offset
706
: length - totalBytesRead;
708
int byteCount = _reader.Read(_byteBuffer, offset, count);
711
throw new EndOfStreamException("Unable to read beyond the end of the stream.");
713
totalBytesRead += byteCount;
715
// Above, byteCount is how many bytes we read this time.
716
// Below, byteCount is how many bytes are in the _byteBuffer.
719
if (byteCount == length)
721
// pref optimization to avoid reading into a string builder
722
// first iteration and all bytes read then return string directly
723
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0);
724
return new string(_charBuffer, 0, charCount);
728
int lastFullCharStop = GetLastFullCharStop(byteCount - 1);
731
builder = new StringBuilder(length);
733
int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0);
734
builder.Append(_charBuffer, 0, charCount);
736
if (lastFullCharStop < byteCount - 1)
738
offset = byteCount - lastFullCharStop - 1;
739
// copy left over multi byte characters to beginning of buffer for next iteration
740
Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset);
747
} while (totalBytesRead < length);
749
return builder.ToString();
752
private int GetLastFullCharStop(int start)
754
int lookbackPos = start;
756
while (lookbackPos >= 0)
758
bis = BytesInSequence(_byteBuffer[lookbackPos]);
774
if (bis == start - lookbackPos)
785
private int BytesInSequence(byte b)
787
if (b <= SeqRange1[1]) return 1;
788
if (b >= SeqRange2[0] && b <= SeqRange2[1]) return 2;
789
if (b >= SeqRange3[0] && b <= SeqRange3[1]) return 3;
790
if (b >= SeqRange4[0] && b <= SeqRange4[1]) return 4;
794
private void EnsureBuffers()
796
if (_byteBuffer == null)
798
_byteBuffer = new byte[MaxCharBytesSize];
800
if (_charBuffer == null)
802
int charBufferSize = Encoding.UTF8.GetMaxCharCount(MaxCharBytesSize);
803
_charBuffer = new char[charBufferSize];
807
private double ReadDouble()
810
return _reader.ReadDouble();
813
private int ReadInt32()
816
return _reader.ReadInt32();
819
private long ReadInt64()
822
return _reader.ReadInt64();
825
private BsonType ReadType()
828
return (BsonType) _reader.ReadSByte();
831
private void MovePosition(int count)
833
_currentContext.Position += count;
836
private byte[] ReadBytes(int count)
839
return _reader.ReadBytes(count);
b'\\ No newline at end of file'