@@ -533,31 +533,7 @@ public string ReadString()
533
533
{
534
534
throw new FileFormatException ( "String is missing null terminator." ) ;
535
535
}
536
-
537
- switch ( length )
538
- {
539
- // special case empty strings
540
- case 1 :
541
- value = string . Empty ;
542
- break ;
543
-
544
- // special case single character strings
545
- case 2 :
546
- var c1 = _chunk [ _chunkOffset ] ;
547
- if ( c1 < 128 )
548
- {
549
- value = __asciiStringTable [ c1 ] ;
550
- }
551
- else
552
- {
553
- value = __utf8Encoding . GetString ( _chunk , _chunkOffset , 1 ) ; // let GetString throw a DecoderFallbackException
554
- }
555
- break ;
556
-
557
- default :
558
- value = __utf8Encoding . GetString ( _chunk , _chunkOffset , length - 1 ) ; // don't decode the null terminator
559
- break ;
560
- }
536
+ value = DecodeUtf8String ( _chunk , _chunkOffset , length - 1 ) ; // don't decode the null terminator
561
537
Position += length ;
562
538
}
563
539
else
@@ -577,10 +553,26 @@ public string ReadString()
577
553
/// <summary>
578
554
/// Reads a BSON CString from the reader (a null terminated string).
579
555
/// </summary>
580
- /// <returns>A String .</returns>
556
+ /// <returns>A string .</returns>
581
557
public string ReadCString ( )
558
+ {
559
+ bool found ;
560
+ object value ;
561
+ return ReadCString ( null , out found , out value ) ;
562
+ }
563
+
564
+ /// <summary>
565
+ /// Reads a BSON CString from the reader (a null terminated string).
566
+ /// </summary>
567
+ /// <param name="bsonTrie">An optional BsonTrie to use during decoding.</param>
568
+ /// <param name="found">Set to true if the string was found in the trie.</param>
569
+ /// <param name="value">Set to the value found in the trie; otherwise, null.</param>
570
+ /// <returns>A string.</returns>
571
+ public string ReadCString < TValue > ( BsonTrie < TValue > bsonTrie , out bool found , out TValue value )
582
572
{
583
573
if ( _disposed ) { throw new ObjectDisposedException ( "BsonBuffer" ) ; }
574
+ found = false ;
575
+ value = default ( TValue ) ;
584
576
// optimize for the case where the null terminator is on the same chunk
585
577
int partialCount ;
586
578
if ( _chunkIndex < _chunks . Count - 1 )
@@ -592,44 +584,24 @@ public string ReadCString()
592
584
partialCount = _length - _position ; // populated part of last chunk
593
585
}
594
586
595
- if ( partialCount > 0 )
587
+ var bsonTrieNode = bsonTrie != null ? bsonTrie . Root : null ;
588
+ var index = IndexOfNull ( _chunk , _chunkOffset , partialCount , ref bsonTrieNode ) ;
589
+ if ( index != - 1 )
596
590
{
597
- var c1 = _chunk [ _chunkOffset ] ;
598
-
599
- // special case empty strings
600
- if ( c1 == 0 )
591
+ var stringLength = index - _chunkOffset ;
592
+ string cstring ;
593
+ if ( bsonTrieNode != null && bsonTrieNode . HasValue )
601
594
{
602
- Position += 1 ;
603
- return string . Empty ;
595
+ cstring = bsonTrieNode . ElementName ;
596
+ value = bsonTrieNode . Value ;
597
+ found = true ;
604
598
}
605
-
606
- if ( partialCount > 1 )
599
+ else
607
600
{
608
- // special case single character strings
609
- if ( _chunk [ _chunkOffset + 1 ] == 0 )
610
- {
611
- string value ;
612
- if ( c1 < 128 )
613
- {
614
- value = __asciiStringTable [ c1 ] ;
615
- }
616
- else
617
- {
618
- value = __utf8Encoding . GetString ( _chunk , _chunkOffset , 1 ) ; // let GetString throw a DecoderFallbackException
619
- }
620
- Position += 2 ;
621
- return value ;
622
- }
623
-
624
- var index = Array . IndexOf < byte > ( _chunk , 0 , _chunkOffset + 2 , partialCount - 2 ) ;
625
- if ( index != - 1 )
626
- {
627
- var stringLength = index - _chunkOffset ;
628
- var value = __utf8Encoding . GetString ( _chunk , _chunkOffset , stringLength ) ;
629
- Position += stringLength + 1 ;
630
- return value ;
631
- }
601
+ cstring = DecodeUtf8String ( _chunk , _chunkOffset , stringLength ) ;
632
602
}
603
+ Position += stringLength + 1 ;
604
+ return cstring ;
633
605
}
634
606
635
607
// the null terminator is not on the same chunk so keep looking starting with the next chunk
@@ -646,14 +618,25 @@ public string ReadCString()
646
618
{
647
619
partialCount = _length - localPosition ; // populated part of last chunk
648
620
}
649
- var index = Array . IndexOf < byte > ( localChunk , 0 , 0 , partialCount ) ;
621
+ index = IndexOfNull ( localChunk , 0 , partialCount , ref bsonTrieNode ) ;
650
622
if ( index != - 1 )
651
623
{
652
624
localPosition += index ;
653
625
var stringLength = localPosition - _position ;
654
- var value = __utf8Encoding . GetString ( ReadBytes ( stringLength ) ) ; // ReadBytes advances over string
655
- Position += 1 ; // skip over null byte at end
656
- return value ;
626
+ string cstring ;
627
+ if ( bsonTrieNode != null && bsonTrieNode . HasValue )
628
+ {
629
+ cstring = bsonTrieNode . ElementName ;
630
+ value = bsonTrieNode . Value ;
631
+ found = true ;
632
+ Position += stringLength + 1 ;
633
+ }
634
+ else
635
+ {
636
+ cstring = __utf8Encoding . GetString ( ReadBytes ( stringLength ) ) ; // ReadBytes advances over string
637
+ Position += 1 ; // skip over null byte at end
638
+ }
639
+ return cstring ;
657
640
}
658
641
localChunkIndex ++ ;
659
642
localPosition += __chunkSize ;
@@ -978,6 +961,27 @@ public void WriteZero()
978
961
}
979
962
980
963
// private methods
964
+ private string DecodeUtf8String ( byte [ ] buffer , int index , int count )
965
+ {
966
+ switch ( count )
967
+ {
968
+ // special case empty strings
969
+ case 0 :
970
+ return "" ;
971
+
972
+ // special case single character strings
973
+ case 1 :
974
+ var byte1 = ( int ) buffer [ index ] ;
975
+ if ( byte1 < __asciiStringTable . Length )
976
+ {
977
+ return __asciiStringTable [ byte1 ] ;
978
+ }
979
+ break ;
980
+ }
981
+
982
+ return __utf8Encoding . GetString ( buffer , index , count ) ;
983
+ }
984
+
981
985
private void EnsureDataAvailable ( int needed )
982
986
{
983
987
if ( _length - _position < needed )
@@ -1009,5 +1013,31 @@ private void EnsureSpaceAvailable(int needed)
1009
1013
}
1010
1014
}
1011
1015
}
1016
+
1017
+ private static int IndexOfNull < TValue > (
1018
+ byte [ ] buffer ,
1019
+ int index ,
1020
+ int count ,
1021
+ ref BsonTrieNode < TValue > bsonTrieNode )
1022
+ {
1023
+ for ( ; count > 0 ; index ++ , count -- )
1024
+ {
1025
+ // bsonTrieNode might be null on entry or it might become null while navigating the trie
1026
+ if ( bsonTrieNode == null )
1027
+ {
1028
+ return Array . IndexOf < byte > ( buffer , 0 , index , count ) ;
1029
+ }
1030
+
1031
+ var keyByte = buffer [ index ] ;
1032
+ if ( keyByte == 0 )
1033
+ {
1034
+ return index ;
1035
+ }
1036
+
1037
+ bsonTrieNode = bsonTrieNode . GetChild ( keyByte ) ; // might return null
1038
+ }
1039
+
1040
+ return - 1 ;
1041
+ }
1012
1042
}
1013
1043
}
0 commit comments