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 . Globalization ;
28+ using System . IO ;
29+ using System . Text ;
30+ using Newtonsoft . Json . Utilities ;
31+
32+ namespace Newtonsoft . Json . Bson
33+ {
34+ internal class BsonBinaryWriter
35+ {
36+ private static readonly Encoding Encoding = new UTF8Encoding ( false ) ;
37+
38+ private readonly BinaryWriter _writer ;
39+
40+ private byte [ ] _largeByteBuffer ;
41+
42+ public DateTimeKind DateTimeKindHandling { get ; set ; }
43+
44+ public BsonBinaryWriter ( BinaryWriter writer )
45+ {
46+ DateTimeKindHandling = DateTimeKind . Utc ;
47+ _writer = writer ;
48+ }
49+
50+ public void Flush ( )
51+ {
52+ _writer . Flush ( ) ;
53+ }
54+
55+ public void Close ( )
56+ {
57+ #if ! ( NETFX_CORE || PORTABLE40 || PORTABLE )
58+ _writer . Close ( ) ;
59+ #else
60+ _writer . Dispose ( ) ;
61+ #endif
62+ }
63+
64+ public void WriteToken ( BsonToken t )
65+ {
66+ CalculateSize ( t ) ;
67+ WriteTokenInternal ( t ) ;
68+ }
69+
70+ private void WriteTokenInternal ( BsonToken t )
71+ {
72+ switch ( t . Type )
73+ {
74+ case BsonType . Object :
75+ {
76+ BsonObject value = ( BsonObject ) t ;
77+ _writer . Write ( value . CalculatedSize ) ;
78+ foreach ( BsonProperty property in value )
79+ {
80+ _writer . Write ( ( sbyte ) property . Value . Type ) ;
81+ WriteString ( ( string ) property . Name . Value , property . Name . ByteCount , null ) ;
82+ WriteTokenInternal ( property . Value ) ;
83+ }
84+ _writer . Write ( ( byte ) 0 ) ;
85+ }
86+ break ;
87+ case BsonType . Array :
88+ {
89+ BsonArray value = ( BsonArray ) t ;
90+ _writer . Write ( value . CalculatedSize ) ;
91+ ulong index = 0 ;
92+ foreach ( BsonToken c in value )
93+ {
94+ _writer . Write ( ( sbyte ) c . Type ) ;
95+ WriteString ( index . ToString ( CultureInfo . InvariantCulture ) , MathUtils . IntLength ( index ) , null ) ;
96+ WriteTokenInternal ( c ) ;
97+ index ++ ;
98+ }
99+ _writer . Write ( ( byte ) 0 ) ;
100+ }
101+ break ;
102+ case BsonType . Integer :
103+ {
104+ BsonValue value = ( BsonValue ) t ;
105+ _writer . Write ( Convert . ToInt32 ( value . Value , CultureInfo . InvariantCulture ) ) ;
106+ }
107+ break ;
108+ case BsonType . Long :
109+ {
110+ BsonValue value = ( BsonValue ) t ;
111+ _writer . Write ( Convert . ToInt64 ( value . Value , CultureInfo . InvariantCulture ) ) ;
112+ }
113+ break ;
114+ case BsonType . Number :
115+ {
116+ BsonValue value = ( BsonValue ) t ;
117+ _writer . Write ( Convert . ToDouble ( value . Value , CultureInfo . InvariantCulture ) ) ;
118+ }
119+ break ;
120+ case BsonType . String :
121+ {
122+ BsonString value = ( BsonString ) t ;
123+ WriteString ( ( string ) value . Value , value . ByteCount , value . CalculatedSize - 4 ) ;
124+ }
125+ break ;
126+ case BsonType . Boolean :
127+ {
128+ BsonValue value = ( BsonValue ) t ;
129+ _writer . Write ( ( bool ) value . Value ) ;
130+ }
131+ break ;
132+ case BsonType . Null :
133+ case BsonType . Undefined :
134+ break ;
135+ case BsonType . Date :
136+ {
137+ BsonValue value = ( BsonValue ) t ;
138+
139+ long ticks = 0 ;
140+
141+ if ( value . Value is DateTime )
142+ {
143+ DateTime dateTime = ( DateTime ) value . Value ;
144+ if ( DateTimeKindHandling == DateTimeKind . Utc )
145+ dateTime = dateTime . ToUniversalTime ( ) ;
146+ else if ( DateTimeKindHandling == DateTimeKind . Local )
147+ dateTime = dateTime . ToLocalTime ( ) ;
148+
149+ ticks = DateTimeUtils . ConvertDateTimeToJavaScriptTicks ( dateTime , false ) ;
150+ }
151+ #if ! NET20
152+ else
153+ {
154+ DateTimeOffset dateTimeOffset = ( DateTimeOffset ) value . Value ;
155+ ticks = DateTimeUtils . ConvertDateTimeToJavaScriptTicks ( dateTimeOffset . UtcDateTime , dateTimeOffset . Offset ) ;
156+ }
157+ #endif
158+
159+ _writer . Write ( ticks ) ;
160+ }
161+ break ;
162+ case BsonType . Binary :
163+ {
164+ BsonBinary value = ( BsonBinary ) t ;
165+
166+ byte [ ] data = ( byte [ ] ) value . Value ;
167+ _writer . Write ( data . Length ) ;
168+ _writer . Write ( ( byte ) value . BinaryType ) ;
169+ _writer . Write ( data ) ;
170+ }
171+ break ;
172+ case BsonType . Oid :
173+ {
174+ BsonValue value = ( BsonValue ) t ;
175+
176+ byte [ ] data = ( byte [ ] ) value . Value ;
177+ _writer . Write ( data ) ;
178+ }
179+ break ;
180+ case BsonType . Regex :
181+ {
182+ BsonRegex value = ( BsonRegex ) t ;
183+
184+ WriteString ( ( string ) value . Pattern . Value , value . Pattern . ByteCount , null ) ;
185+ WriteString ( ( string ) value . Options . Value , value . Options . ByteCount , null ) ;
186+ }
187+ break ;
188+ default :
189+ throw new ArgumentOutOfRangeException ( "t" , "Unexpected token when writing BSON: {0}" . FormatWith ( CultureInfo . InvariantCulture , t . Type ) ) ;
190+ }
191+ }
192+
193+ private void WriteString ( string s , int byteCount , int ? calculatedlengthPrefix )
194+ {
195+ if ( calculatedlengthPrefix != null )
196+ _writer . Write ( calculatedlengthPrefix . Value ) ;
197+
198+ WriteUtf8Bytes ( s , byteCount ) ;
199+
200+ _writer . Write ( ( byte ) 0 ) ;
201+ }
202+
203+ public void WriteUtf8Bytes ( string s , int byteCount )
204+ {
205+ if ( s != null )
206+ {
207+ if ( _largeByteBuffer == null )
208+ {
209+ _largeByteBuffer = new byte [ 256 ] ;
210+ }
211+ if ( byteCount <= 256 )
212+ {
213+ Encoding . GetBytes ( s , 0 , s . Length , _largeByteBuffer , 0 ) ;
214+ _writer . Write ( _largeByteBuffer , 0 , byteCount ) ;
215+ }
216+ else
217+ {
218+ byte [ ] bytes = Encoding . GetBytes ( s ) ;
219+ _writer . Write ( bytes ) ;
220+ }
221+ }
222+ }
223+
224+ private int CalculateSize ( int stringByteCount )
225+ {
226+ return stringByteCount + 1 ;
227+ }
228+
229+ private int CalculateSizeWithLength ( int stringByteCount , bool includeSize )
230+ {
231+ int baseSize = ( includeSize )
232+ ? 5 // size bytes + terminator
233+ : 1 ; // terminator
234+
235+ return baseSize + stringByteCount ;
236+ }
237+
238+ private int CalculateSize ( BsonToken t )
239+ {
240+ switch ( t . Type )
241+ {
242+ case BsonType . Object :
243+ {
244+ BsonObject value = ( BsonObject ) t ;
245+
246+ int bases = 4 ;
247+ foreach ( BsonProperty p in value )
248+ {
249+ int size = 1 ;
250+ size += CalculateSize ( p . Name ) ;
251+ size += CalculateSize ( p . Value ) ;
252+
253+ bases += size ;
254+ }
255+ bases += 1 ;
256+ value . CalculatedSize = bases ;
257+ return bases ;
258+ }
259+ case BsonType . Array :
260+ {
261+ BsonArray value = ( BsonArray ) t ;
262+
263+ int size = 4 ;
264+ ulong index = 0 ;
265+ foreach ( BsonToken c in value )
266+ {
267+ size += 1 ;
268+ size += CalculateSize ( MathUtils . IntLength ( index ) ) ;
269+ size += CalculateSize ( c ) ;
270+ index ++ ;
271+ }
272+ size += 1 ;
273+ value . CalculatedSize = size ;
274+
275+ return value . CalculatedSize ;
276+ }
277+ case BsonType . Integer :
278+ return 4 ;
279+ case BsonType . Long :
280+ return 8 ;
281+ case BsonType . Number :
282+ return 8 ;
283+ case BsonType . String :
284+ {
285+ BsonString value = ( BsonString ) t ;
286+ string s = ( string ) value . Value ;
287+ value . ByteCount = ( s != null ) ? Encoding . GetByteCount ( s ) : 0 ;
288+ value . CalculatedSize = CalculateSizeWithLength ( value . ByteCount , value . IncludeLength ) ;
289+
290+ return value . CalculatedSize ;
291+ }
292+ case BsonType . Boolean :
293+ return 1 ;
294+ case BsonType . Null :
295+ case BsonType . Undefined :
296+ return 0 ;
297+ case BsonType . Date :
298+ return 8 ;
299+ case BsonType . Binary :
300+ {
301+ BsonBinary value = ( BsonBinary ) t ;
302+
303+ byte [ ] data = ( byte [ ] ) value . Value ;
304+ value . CalculatedSize = 4 + 1 + data . Length ;
305+
306+ return value . CalculatedSize ;
307+ }
308+ case BsonType . Oid :
309+ return 12 ;
310+ case BsonType . Regex :
311+ {
312+ BsonRegex value = ( BsonRegex ) t ;
313+ int size = 0 ;
314+ size += CalculateSize ( value . Pattern ) ;
315+ size += CalculateSize ( value . Options ) ;
316+ value . CalculatedSize = size ;
317+
318+ return value . CalculatedSize ;
319+ }
320+ default :
321+ throw new ArgumentOutOfRangeException ( "t" , "Unexpected token when writing BSON: {0}" . FormatWith ( CultureInfo . InvariantCulture , t . Type ) ) ;
322+ }
323+ }
324+ }
325+ }
0 commit comments