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 ! ( DOTNET || 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
+ {
146
+ dateTime = dateTime . ToUniversalTime ( ) ;
147
+ }
148
+ else if ( DateTimeKindHandling == DateTimeKind . Local )
149
+ {
150
+ dateTime = dateTime . ToLocalTime ( ) ;
151
+ }
152
+
153
+ ticks = DateTimeUtils . ConvertDateTimeToJavaScriptTicks ( dateTime , false ) ;
154
+ }
155
+ #if ! NET20
156
+ else
157
+ {
158
+ DateTimeOffset dateTimeOffset = ( DateTimeOffset ) value . Value ;
159
+ ticks = DateTimeUtils . ConvertDateTimeToJavaScriptTicks ( dateTimeOffset . UtcDateTime , dateTimeOffset . Offset ) ;
160
+ }
161
+ #endif
162
+
163
+ _writer . Write ( ticks ) ;
164
+ }
165
+ break ;
166
+ case BsonType . Binary :
167
+ {
168
+ BsonBinary value = ( BsonBinary ) t ;
169
+
170
+ byte [ ] data = ( byte [ ] ) value . Value ;
171
+ _writer . Write ( data . Length ) ;
172
+ _writer . Write ( ( byte ) value . BinaryType ) ;
173
+ _writer . Write ( data ) ;
174
+ }
175
+ break ;
176
+ case BsonType . Oid :
177
+ {
178
+ BsonValue value = ( BsonValue ) t ;
179
+
180
+ byte [ ] data = ( byte [ ] ) value . Value ;
181
+ _writer . Write ( data ) ;
182
+ }
183
+ break ;
184
+ case BsonType . Regex :
185
+ {
186
+ BsonRegex value = ( BsonRegex ) t ;
187
+
188
+ WriteString ( ( string ) value . Pattern . Value , value . Pattern . ByteCount , null ) ;
189
+ WriteString ( ( string ) value . Options . Value , value . Options . ByteCount , null ) ;
190
+ }
191
+ break ;
192
+ default :
193
+ throw new ArgumentOutOfRangeException ( nameof ( t ) , "Unexpected token when writing BSON: {0}" . FormatWith ( CultureInfo . InvariantCulture , t . Type ) ) ;
194
+ }
195
+ }
196
+
197
+ private void WriteString ( string s , int byteCount , int ? calculatedlengthPrefix )
198
+ {
199
+ if ( calculatedlengthPrefix != null )
200
+ {
201
+ _writer . Write ( calculatedlengthPrefix . GetValueOrDefault ( ) ) ;
202
+ }
203
+
204
+ WriteUtf8Bytes ( s , byteCount ) ;
205
+
206
+ _writer . Write ( ( byte ) 0 ) ;
207
+ }
208
+
209
+ public void WriteUtf8Bytes ( string s , int byteCount )
210
+ {
211
+ if ( s != null )
212
+ {
213
+ if ( _largeByteBuffer == null )
214
+ {
215
+ _largeByteBuffer = new byte [ 256 ] ;
216
+ }
217
+ if ( byteCount <= 256 )
218
+ {
219
+ Encoding . GetBytes ( s , 0 , s . Length , _largeByteBuffer , 0 ) ;
220
+ _writer . Write ( _largeByteBuffer , 0 , byteCount ) ;
221
+ }
222
+ else
223
+ {
224
+ byte [ ] bytes = Encoding . GetBytes ( s ) ;
225
+ _writer . Write ( bytes ) ;
226
+ }
227
+ }
228
+ }
229
+
230
+ private int CalculateSize ( int stringByteCount )
231
+ {
232
+ return stringByteCount + 1 ;
233
+ }
234
+
235
+ private int CalculateSizeWithLength ( int stringByteCount , bool includeSize )
236
+ {
237
+ int baseSize = ( includeSize )
238
+ ? 5 // size bytes + terminator
239
+ : 1 ; // terminator
240
+
241
+ return baseSize + stringByteCount ;
242
+ }
243
+
244
+ private int CalculateSize ( BsonToken t )
245
+ {
246
+ switch ( t . Type )
247
+ {
248
+ case BsonType . Object :
249
+ {
250
+ BsonObject value = ( BsonObject ) t ;
251
+
252
+ int bases = 4 ;
253
+ foreach ( BsonProperty p in value )
254
+ {
255
+ int size = 1 ;
256
+ size += CalculateSize ( p . Name ) ;
257
+ size += CalculateSize ( p . Value ) ;
258
+
259
+ bases += size ;
260
+ }
261
+ bases += 1 ;
262
+ value . CalculatedSize = bases ;
263
+ return bases ;
264
+ }
265
+ case BsonType . Array :
266
+ {
267
+ BsonArray value = ( BsonArray ) t ;
268
+
269
+ int size = 4 ;
270
+ ulong index = 0 ;
271
+ foreach ( BsonToken c in value )
272
+ {
273
+ size += 1 ;
274
+ size += CalculateSize ( MathUtils . IntLength ( index ) ) ;
275
+ size += CalculateSize ( c ) ;
276
+ index ++ ;
277
+ }
278
+ size += 1 ;
279
+ value . CalculatedSize = size ;
280
+
281
+ return value . CalculatedSize ;
282
+ }
283
+ case BsonType . Integer :
284
+ return 4 ;
285
+ case BsonType . Long :
286
+ return 8 ;
287
+ case BsonType . Number :
288
+ return 8 ;
289
+ case BsonType . String :
290
+ {
291
+ BsonString value = ( BsonString ) t ;
292
+ string s = ( string ) value . Value ;
293
+ value . ByteCount = ( s != null ) ? Encoding . GetByteCount ( s ) : 0 ;
294
+ value . CalculatedSize = CalculateSizeWithLength ( value . ByteCount , value . IncludeLength ) ;
295
+
296
+ return value . CalculatedSize ;
297
+ }
298
+ case BsonType . Boolean :
299
+ return 1 ;
300
+ case BsonType . Null :
301
+ case BsonType . Undefined :
302
+ return 0 ;
303
+ case BsonType . Date :
304
+ return 8 ;
305
+ case BsonType . Binary :
306
+ {
307
+ BsonBinary value = ( BsonBinary ) t ;
308
+
309
+ byte [ ] data = ( byte [ ] ) value . Value ;
310
+ value . CalculatedSize = 4 + 1 + data . Length ;
311
+
312
+ return value . CalculatedSize ;
313
+ }
314
+ case BsonType . Oid :
315
+ return 12 ;
316
+ case BsonType . Regex :
317
+ {
318
+ BsonRegex value = ( BsonRegex ) t ;
319
+ int size = 0 ;
320
+ size += CalculateSize ( value . Pattern ) ;
321
+ size += CalculateSize ( value . Options ) ;
322
+ value . CalculatedSize = size ;
323
+
324
+ return value . CalculatedSize ;
325
+ }
326
+ default :
327
+ throw new ArgumentOutOfRangeException ( nameof ( t ) , "Unexpected token when writing BSON: {0}" . FormatWith ( CultureInfo . InvariantCulture , t . Type ) ) ;
328
+ }
329
+ }
330
+ }
331
+ }
0 commit comments