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 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 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 bool _jsonNet35BinaryCompatibility; 59 private DateTimeKind _dateTimeKindHandling; 60 61 private enum BsonReaderState 62 { 63 Normal, 64 ReferenceStart, 65 ReferenceRef, 66 ReferenceId, 67 CodeWScopeStart, 68 CodeWScopeCode, 69 CodeWScopeScope, 70 CodeWScopeScopeObject, 71 CodeWScopeScopeEnd 72 } 73 74 private class ContainerContext 75 { 76 public readonly BsonType Type; 77 public int Length; 78 public int Position; 79 ContainerContext(BsonType type)80 public ContainerContext(BsonType type) 81 { 82 Type = type; 83 } 84 } 85 86 /// <summary> 87 /// Gets or sets a value indicating whether binary data reading should compatible with incorrect Json.NET 3.5 written binary. 88 /// </summary> 89 /// <value> 90 /// <c>true</c> if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, <c>false</c>. 91 /// </value> 92 public bool JsonNet35BinaryCompatibility 93 { 94 get { return _jsonNet35BinaryCompatibility; } 95 set { _jsonNet35BinaryCompatibility = value; } 96 } 97 98 /// <summary> 99 /// Gets or sets a value indicating whether the root object will be read as a JSON array. 100 /// </summary> 101 /// <value> 102 /// <c>true</c> if the root object will be read as a JSON array; otherwise, <c>false</c>. 103 /// </value> 104 public bool ReadRootValueAsArray 105 { 106 get { return _readRootValueAsArray; } 107 set { _readRootValueAsArray = value; } 108 } 109 110 /// <summary> 111 /// Gets or sets the <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON. 112 /// </summary> 113 /// <value>The <see cref="DateTimeKind" /> used when reading <see cref="DateTime"/> values from BSON.</value> 114 public DateTimeKind DateTimeKindHandling 115 { 116 get { return _dateTimeKindHandling; } 117 set { _dateTimeKindHandling = value; } 118 } 119 120 /// <summary> 121 /// Initializes a new instance of the <see cref="BsonReader"/> class. 122 /// </summary> 123 /// <param name="stream">The stream.</param> BsonReader(Stream stream)124 public BsonReader(Stream stream) 125 : this(stream, false, DateTimeKind.Local) 126 { 127 } 128 129 /// <summary> 130 /// Initializes a new instance of the <see cref="BsonReader"/> class. 131 /// </summary> 132 /// <param name="reader">The reader.</param> BsonReader(BinaryReader reader)133 public BsonReader(BinaryReader reader) 134 : this(reader, false, DateTimeKind.Local) 135 { 136 } 137 138 /// <summary> 139 /// Initializes a new instance of the <see cref="BsonReader"/> class. 140 /// </summary> 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> BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)144 public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling) 145 { 146 ValidationUtils.ArgumentNotNull(stream, "stream"); 147 _reader = new BinaryReader(stream); 148 _stack = new List<ContainerContext>(); 149 _readRootValueAsArray = readRootValueAsArray; 150 _dateTimeKindHandling = dateTimeKindHandling; 151 } 152 153 /// <summary> 154 /// Initializes a new instance of the <see cref="BsonReader"/> class. 155 /// </summary> 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> BsonReader(BinaryReader reader, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)159 public BsonReader(BinaryReader reader, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling) 160 { 161 ValidationUtils.ArgumentNotNull(reader, "reader"); 162 _reader = reader; 163 _stack = new List<ContainerContext>(); 164 _readRootValueAsArray = readRootValueAsArray; 165 _dateTimeKindHandling = dateTimeKindHandling; 166 } 167 ReadElement()168 private string ReadElement() 169 { 170 _currentElementType = ReadType(); 171 string elementName = ReadString(); 172 return elementName; 173 } 174 175 /// <summary> 176 /// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>. 177 /// </summary> 178 /// <returns> 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. 180 /// </returns> ReadAsBytes()181 public override byte[] ReadAsBytes() 182 { 183 return ReadAsBytesInternal(); 184 } 185 186 /// <summary> 187 /// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>. 188 /// </summary> 189 /// <returns>A <see cref="Nullable{Decimal}"/>. This method will return <c>null</c> at the end of an array.</returns> ReadAsDecimal()190 public override decimal? ReadAsDecimal() 191 { 192 return ReadAsDecimalInternal(); 193 } 194 195 /// <summary> 196 /// Reads the next JSON token from the stream as a <see cref="Nullable{Int32}"/>. 197 /// </summary> 198 /// <returns>A <see cref="Nullable{Int32}"/>. This method will return <c>null</c> at the end of an array.</returns> ReadAsInt32()199 public override int? ReadAsInt32() 200 { 201 return ReadAsInt32Internal(); 202 } 203 204 /// <summary> 205 /// Reads the next JSON token from the stream as a <see cref="String"/>. 206 /// </summary> 207 /// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns> ReadAsString()208 public override string ReadAsString() 209 { 210 return ReadAsStringInternal(); 211 } 212 213 /// <summary> 214 /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>. 215 /// </summary> 216 /// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns> ReadAsDateTime()217 public override DateTime? ReadAsDateTime() 218 { 219 return ReadAsDateTimeInternal(); 220 } 221 222 #if !NET20 223 /// <summary> 224 /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>. 225 /// </summary> 226 /// <returns> 227 /// A <see cref="Nullable{DateTimeOffset}"/>. This method will return <c>null</c> at the end of an array. 228 /// </returns> ReadAsDateTimeOffset()229 public override DateTimeOffset? ReadAsDateTimeOffset() 230 { 231 return ReadAsDateTimeOffsetInternal(); 232 } 233 #endif 234 235 /// <summary> 236 /// Reads the next JSON token from the stream. 237 /// </summary> 238 /// <returns> 239 /// true if the next token was read successfully; false if there are no more tokens to read. 240 /// </returns> Read()241 public override bool Read() 242 { 243 _readType = Json.ReadType.Read; 244 245 return ReadInternal(); 246 } 247 ReadInternal()248 internal override bool ReadInternal() 249 { 250 try 251 { 252 bool success; 253 254 switch (_bsonReaderState) 255 { 256 case BsonReaderState.Normal: 257 success = ReadNormal(); 258 break; 259 case BsonReaderState.ReferenceStart: 260 case BsonReaderState.ReferenceRef: 261 case BsonReaderState.ReferenceId: 262 success = ReadReference(); 263 break; 264 case BsonReaderState.CodeWScopeStart: 265 case BsonReaderState.CodeWScopeCode: 266 case BsonReaderState.CodeWScopeScope: 267 case BsonReaderState.CodeWScopeScopeObject: 268 case BsonReaderState.CodeWScopeScopeEnd: 269 success = ReadCodeWScope(); 270 break; 271 default: 272 throw JsonReaderException.Create(this, "Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState)); 273 } 274 275 if (!success) 276 { 277 SetToken(JsonToken.None); 278 return false; 279 } 280 281 return true; 282 } 283 catch (EndOfStreamException) 284 { 285 SetToken(JsonToken.None); 286 return false; 287 } 288 } 289 290 /// <summary> 291 /// Changes the <see cref="JsonReader.State"/> to Closed. 292 /// </summary> Close()293 public override void Close() 294 { 295 base.Close(); 296 297 if (CloseInput && _reader != null) 298 #if !(NETFX_CORE || PORTABLE) 299 _reader.Close(); 300 #else 301 _reader.Dispose(); 302 #endif 303 } 304 ReadCodeWScope()305 private bool ReadCodeWScope() 306 { 307 switch (_bsonReaderState) 308 { 309 case BsonReaderState.CodeWScopeStart: 310 SetToken(JsonToken.PropertyName, "$code"); 311 _bsonReaderState = BsonReaderState.CodeWScopeCode; 312 return true; 313 case BsonReaderState.CodeWScopeCode: 314 // total CodeWScope size - not used 315 ReadInt32(); 316 317 SetToken(JsonToken.String, ReadLengthString()); 318 _bsonReaderState = BsonReaderState.CodeWScopeScope; 319 return true; 320 case BsonReaderState.CodeWScopeScope: 321 if (CurrentState == State.PostValue) 322 { 323 SetToken(JsonToken.PropertyName, "$scope"); 324 return true; 325 } 326 else 327 { 328 SetToken(JsonToken.StartObject); 329 _bsonReaderState = BsonReaderState.CodeWScopeScopeObject; 330 331 ContainerContext newContext = new ContainerContext(BsonType.Object); 332 PushContext(newContext); 333 newContext.Length = ReadInt32(); 334 335 return true; 336 } 337 case BsonReaderState.CodeWScopeScopeObject: 338 bool result = ReadNormal(); 339 if (result && TokenType == JsonToken.EndObject) 340 _bsonReaderState = BsonReaderState.CodeWScopeScopeEnd; 341 342 return result; 343 case BsonReaderState.CodeWScopeScopeEnd: 344 SetToken(JsonToken.EndObject); 345 _bsonReaderState = BsonReaderState.Normal; 346 return true; 347 default: 348 throw new ArgumentOutOfRangeException(); 349 } 350 } 351 ReadReference()352 private bool ReadReference() 353 { 354 switch (CurrentState) 355 { 356 case State.ObjectStart: 357 { 358 SetToken(JsonToken.PropertyName, "$ref"); 359 _bsonReaderState = BsonReaderState.ReferenceRef; 360 return true; 361 } 362 case State.Property: 363 { 364 if (_bsonReaderState == BsonReaderState.ReferenceRef) 365 { 366 SetToken(JsonToken.String, ReadLengthString()); 367 return true; 368 } 369 else if (_bsonReaderState == BsonReaderState.ReferenceId) 370 { 371 SetToken(JsonToken.Bytes, ReadBytes(12)); 372 return true; 373 } 374 else 375 { 376 throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState); 377 } 378 } 379 case State.PostValue: 380 { 381 if (_bsonReaderState == BsonReaderState.ReferenceRef) 382 { 383 SetToken(JsonToken.PropertyName, "$id"); 384 _bsonReaderState = BsonReaderState.ReferenceId; 385 return true; 386 } 387 else if (_bsonReaderState == BsonReaderState.ReferenceId) 388 { 389 SetToken(JsonToken.EndObject); 390 _bsonReaderState = BsonReaderState.Normal; 391 return true; 392 } 393 else 394 { 395 throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + _bsonReaderState); 396 } 397 } 398 default: 399 throw JsonReaderException.Create(this, "Unexpected state when reading BSON reference: " + CurrentState); 400 } 401 } 402 ReadNormal()403 private bool ReadNormal() 404 { 405 switch (CurrentState) 406 { 407 case State.Start: 408 { 409 JsonToken token = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray; 410 BsonType type = (!_readRootValueAsArray) ? BsonType.Object : BsonType.Array; 411 412 SetToken(token); 413 ContainerContext newContext = new ContainerContext(type); 414 PushContext(newContext); 415 newContext.Length = ReadInt32(); 416 return true; 417 } 418 case State.Complete: 419 case State.Closed: 420 return false; 421 case State.Property: 422 { 423 ReadType(_currentElementType); 424 return true; 425 } 426 case State.ObjectStart: 427 case State.ArrayStart: 428 case State.PostValue: 429 ContainerContext context = _currentContext; 430 if (context == null) 431 return false; 432 433 int lengthMinusEnd = context.Length - 1; 434 435 if (context.Position < lengthMinusEnd) 436 { 437 if (context.Type == BsonType.Array) 438 { 439 ReadElement(); 440 ReadType(_currentElementType); 441 return true; 442 } 443 else 444 { 445 SetToken(JsonToken.PropertyName, ReadElement()); 446 return true; 447 } 448 } 449 else if (context.Position == lengthMinusEnd) 450 { 451 if (ReadByte() != 0) 452 throw JsonReaderException.Create(this, "Unexpected end of object byte value."); 453 454 PopContext(); 455 if (_currentContext != null) 456 MovePosition(context.Length); 457 458 JsonToken endToken = (context.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray; 459 SetToken(endToken); 460 return true; 461 } 462 else 463 { 464 throw JsonReaderException.Create(this, "Read past end of current container context."); 465 } 466 case State.ConstructorStart: 467 break; 468 case State.Constructor: 469 break; 470 case State.Error: 471 break; 472 case State.Finished: 473 break; 474 default: 475 throw new ArgumentOutOfRangeException(); 476 } 477 478 return false; 479 } 480 PopContext()481 private void PopContext() 482 { 483 _stack.RemoveAt(_stack.Count - 1); 484 if (_stack.Count == 0) 485 _currentContext = null; 486 else 487 _currentContext = _stack[_stack.Count - 1]; 488 } 489 PushContext(ContainerContext newContext)490 private void PushContext(ContainerContext newContext) 491 { 492 _stack.Add(newContext); 493 _currentContext = newContext; 494 } 495 ReadByte()496 private byte ReadByte() 497 { 498 MovePosition(1); 499 return _reader.ReadByte(); 500 } 501 ReadType(BsonType type)502 private void ReadType(BsonType type) 503 { 504 switch (type) 505 { 506 case BsonType.Number: 507 SetToken(JsonToken.Float, ReadDouble()); 508 break; 509 case BsonType.String: 510 case BsonType.Symbol: 511 SetToken(JsonToken.String, ReadLengthString()); 512 break; 513 case BsonType.Object: 514 { 515 SetToken(JsonToken.StartObject); 516 517 ContainerContext newContext = new ContainerContext(BsonType.Object); 518 PushContext(newContext); 519 newContext.Length = ReadInt32(); 520 break; 521 } 522 case BsonType.Array: 523 { 524 SetToken(JsonToken.StartArray); 525 526 ContainerContext newContext = new ContainerContext(BsonType.Array); 527 PushContext(newContext); 528 newContext.Length = ReadInt32(); 529 break; 530 } 531 case BsonType.Binary: 532 SetToken(JsonToken.Bytes, ReadBinary()); 533 break; 534 case BsonType.Undefined: 535 SetToken(JsonToken.Undefined); 536 break; 537 case BsonType.Oid: 538 byte[] oid = ReadBytes(12); 539 SetToken(JsonToken.Bytes, oid); 540 break; 541 case BsonType.Boolean: 542 bool b = Convert.ToBoolean(ReadByte()); 543 SetToken(JsonToken.Boolean, b); 544 break; 545 case BsonType.Date: 546 long ticks = ReadInt64(); 547 DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks); 548 549 DateTime dateTime; 550 switch (DateTimeKindHandling) 551 { 552 case DateTimeKind.Unspecified: 553 dateTime = DateTime.SpecifyKind(utcDateTime, DateTimeKind.Unspecified); 554 break; 555 case DateTimeKind.Local: 556 dateTime = utcDateTime.ToLocalTime(); 557 break; 558 default: 559 dateTime = utcDateTime; 560 break; 561 } 562 563 SetToken(JsonToken.Date, dateTime); 564 break; 565 case BsonType.Null: 566 SetToken(JsonToken.Null); 567 break; 568 case BsonType.Regex: 569 string expression = ReadString(); 570 string modifiers = ReadString(); 571 572 string regex = @"/" + expression + @"/" + modifiers; 573 SetToken(JsonToken.String, regex); 574 break; 575 case BsonType.Reference: 576 SetToken(JsonToken.StartObject); 577 _bsonReaderState = BsonReaderState.ReferenceStart; 578 break; 579 case BsonType.Code: 580 SetToken(JsonToken.String, ReadLengthString()); 581 break; 582 case BsonType.CodeWScope: 583 SetToken(JsonToken.StartObject); 584 _bsonReaderState = BsonReaderState.CodeWScopeStart; 585 break; 586 case BsonType.Integer: 587 SetToken(JsonToken.Integer, (long) ReadInt32()); 588 break; 589 case BsonType.TimeStamp: 590 case BsonType.Long: 591 SetToken(JsonToken.Integer, ReadInt64()); 592 break; 593 default: 594 throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + type); 595 } 596 } 597 ReadBinary()598 private byte[] ReadBinary() 599 { 600 int dataLength = ReadInt32(); 601 602 BsonBinaryType binaryType = (BsonBinaryType) ReadByte(); 603 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) 607 { 608 dataLength = ReadInt32(); 609 } 610 #pragma warning restore 612,618 611 612 return ReadBytes(dataLength); 613 } 614 ReadString()615 private string ReadString() 616 { 617 EnsureBuffers(); 618 619 StringBuilder builder = null; 620 621 int totalBytesRead = 0; 622 // used in case of left over multibyte characters in the buffer 623 int offset = 0; 624 do 625 { 626 int count = offset; 627 byte b; 628 while (count < MaxCharBytesSize && (b = _reader.ReadByte()) > 0) 629 { 630 _byteBuffer[count++] = b; 631 } 632 int byteCount = count - offset; 633 totalBytesRead += byteCount; 634 635 if (count < MaxCharBytesSize && builder == null) 636 { 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); 640 641 MovePosition(totalBytesRead + 1); 642 return new string(_charBuffer, 0, length); 643 } 644 else 645 { 646 // calculate the index of the end of the last full character in the buffer 647 int lastFullCharStop = GetLastFullCharStop(count - 1); 648 649 int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0); 650 651 if (builder == null) 652 builder = new StringBuilder(MaxCharBytesSize*2); 653 654 builder.Append(_charBuffer, 0, charCount); 655 656 if (lastFullCharStop < byteCount - 1) 657 { 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); 661 } 662 else 663 { 664 // reached end of string 665 if (count < MaxCharBytesSize) 666 { 667 MovePosition(totalBytesRead + 1); 668 return builder.ToString(); 669 } 670 671 offset = 0; 672 } 673 } 674 } while (true); 675 } 676 ReadLengthString()677 private string ReadLengthString() 678 { 679 int length = ReadInt32(); 680 681 MovePosition(length); 682 683 string s = GetString(length - 1); 684 _reader.ReadByte(); 685 686 return s; 687 } 688 GetString(int length)689 private string GetString(int length) 690 { 691 if (length == 0) 692 return string.Empty; 693 694 EnsureBuffers(); 695 696 StringBuilder builder = null; 697 698 int totalBytesRead = 0; 699 700 // used in case of left over multibyte characters in the buffer 701 int offset = 0; 702 do 703 { 704 int count = ((length - totalBytesRead) > MaxCharBytesSize - offset) 705 ? MaxCharBytesSize - offset 706 : length - totalBytesRead; 707 708 int byteCount = _reader.Read(_byteBuffer, offset, count); 709 710 if (byteCount == 0) 711 throw new EndOfStreamException("Unable to read beyond the end of the stream."); 712 713 totalBytesRead += byteCount; 714 715 // Above, byteCount is how many bytes we read this time. 716 // Below, byteCount is how many bytes are in the _byteBuffer. 717 byteCount += offset; 718 719 if (byteCount == length) 720 { 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); 725 } 726 else 727 { 728 int lastFullCharStop = GetLastFullCharStop(byteCount - 1); 729 730 if (builder == null) 731 builder = new StringBuilder(length); 732 733 int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0); 734 builder.Append(_charBuffer, 0, charCount); 735 736 if (lastFullCharStop < byteCount - 1) 737 { 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); 741 } 742 else 743 { 744 offset = 0; 745 } 746 } 747 } while (totalBytesRead < length); 748 749 return builder.ToString(); 750 } 751 GetLastFullCharStop(int start)752 private int GetLastFullCharStop(int start) 753 { 754 int lookbackPos = start; 755 int bis = 0; 756 while (lookbackPos >= 0) 757 { 758 bis = BytesInSequence(_byteBuffer[lookbackPos]); 759 if (bis == 0) 760 { 761 lookbackPos--; 762 continue; 763 } 764 else if (bis == 1) 765 { 766 break; 767 } 768 else 769 { 770 lookbackPos--; 771 break; 772 } 773 } 774 if (bis == start - lookbackPos) 775 { 776 //Full character. 777 return start; 778 } 779 else 780 { 781 return lookbackPos; 782 } 783 } 784 BytesInSequence(byte b)785 private int BytesInSequence(byte b) 786 { 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; 791 return 0; 792 } 793 EnsureBuffers()794 private void EnsureBuffers() 795 { 796 if (_byteBuffer == null) 797 { 798 _byteBuffer = new byte[MaxCharBytesSize]; 799 } 800 if (_charBuffer == null) 801 { 802 int charBufferSize = Encoding.UTF8.GetMaxCharCount(MaxCharBytesSize); 803 _charBuffer = new char[charBufferSize]; 804 } 805 } 806 ReadDouble()807 private double ReadDouble() 808 { 809 MovePosition(8); 810 return _reader.ReadDouble(); 811 } 812 ReadInt32()813 private int ReadInt32() 814 { 815 MovePosition(4); 816 return _reader.ReadInt32(); 817 } 818 ReadInt64()819 private long ReadInt64() 820 { 821 MovePosition(8); 822 return _reader.ReadInt64(); 823 } 824 ReadType()825 private BsonType ReadType() 826 { 827 MovePosition(1); 828 return (BsonType) _reader.ReadSByte(); 829 } 830 MovePosition(int count)831 private void MovePosition(int count) 832 { 833 _currentContext.Position += count; 834 } 835 ReadBytes(int count)836 private byte[] ReadBytes(int count) 837 { 838 MovePosition(count); 839 return _reader.ReadBytes(count); 840 } 841 } 842 }