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'