1
2
3
4
5
6
7 package bsoncore
8
9 import (
10 "bytes"
11 "fmt"
12 "math"
13 "strconv"
14 "strings"
15 "time"
16
17 "go.mongodb.org/mongo-driver/bson/bsontype"
18 "go.mongodb.org/mongo-driver/bson/primitive"
19 )
20
21 const (
22
23 EmptyDocumentLength = 5
24
25 nullTerminator = string(byte(0))
26 invalidKeyPanicMsg = "BSON element keys cannot contain null bytes"
27 invalidRegexPanicMsg = "BSON regex values cannot contain null bytes"
28 )
29
30
31 func AppendType(dst []byte, t bsontype.Type) []byte { return append(dst, byte(t)) }
32
33
34 func AppendKey(dst []byte, key string) []byte { return append(dst, key+nullTerminator...) }
35
36
37
38 func AppendHeader(dst []byte, t bsontype.Type, key string) []byte {
39 if !isValidCString(key) {
40 panic(invalidKeyPanicMsg)
41 }
42
43 dst = AppendType(dst, t)
44 dst = append(dst, key...)
45 return append(dst, 0x00)
46
47 }
48
49
50
51
52
53 func ReadType(src []byte) (bsontype.Type, []byte, bool) {
54 if len(src) < 1 {
55 return 0, src, false
56 }
57 return bsontype.Type(src[0]), src[1:], true
58 }
59
60
61
62
63 func ReadKey(src []byte) (string, []byte, bool) { return readcstring(src) }
64
65
66
67
68 func ReadKeyBytes(src []byte) ([]byte, []byte, bool) { return readcstringbytes(src) }
69
70
71
72 func ReadHeader(src []byte) (t bsontype.Type, key string, rem []byte, ok bool) {
73 t, rem, ok = ReadType(src)
74 if !ok {
75 return 0, "", src, false
76 }
77 key, rem, ok = ReadKey(rem)
78 if !ok {
79 return 0, "", src, false
80 }
81
82 return t, key, rem, true
83 }
84
85
86
87 func ReadHeaderBytes(src []byte) (header []byte, rem []byte, ok bool) {
88 if len(src) < 1 {
89 return nil, src, false
90 }
91 idx := bytes.IndexByte(src[1:], 0x00)
92 if idx == -1 {
93 return nil, src, false
94 }
95 return src[:idx], src[idx+1:], true
96 }
97
98
99
100 func ReadElement(src []byte) (Element, []byte, bool) {
101 if len(src) < 1 {
102 return nil, src, false
103 }
104 t := bsontype.Type(src[0])
105 idx := bytes.IndexByte(src[1:], 0x00)
106 if idx == -1 {
107 return nil, src, false
108 }
109 length, ok := valueLength(src[idx+2:], t)
110 if !ok {
111 return nil, src, false
112 }
113 elemLength := 1 + idx + 1 + int(length)
114 if elemLength > len(src) {
115 return nil, src, false
116 }
117 if elemLength < 0 {
118 return nil, src, false
119 }
120 return src[:elemLength], src[elemLength:], true
121 }
122
123
124 func AppendValueElement(dst []byte, key string, value Value) []byte {
125 dst = AppendHeader(dst, value.Type, key)
126 dst = append(dst, value.Data...)
127 return dst
128 }
129
130
131
132 func ReadValue(src []byte, t bsontype.Type) (Value, []byte, bool) {
133 data, rem, ok := readValue(src, t)
134 if !ok {
135 return Value{}, src, false
136 }
137 return Value{Type: t, Data: data}, rem, true
138 }
139
140
141 func AppendDouble(dst []byte, f float64) []byte {
142 return appendu64(dst, math.Float64bits(f))
143 }
144
145
146
147 func AppendDoubleElement(dst []byte, key string, f float64) []byte {
148 return AppendDouble(AppendHeader(dst, bsontype.Double, key), f)
149 }
150
151
152
153 func ReadDouble(src []byte) (float64, []byte, bool) {
154 bits, src, ok := readu64(src)
155 if !ok {
156 return 0, src, false
157 }
158 return math.Float64frombits(bits), src, true
159 }
160
161
162 func AppendString(dst []byte, s string) []byte {
163 return appendstring(dst, s)
164 }
165
166
167
168 func AppendStringElement(dst []byte, key, val string) []byte {
169 return AppendString(AppendHeader(dst, bsontype.String, key), val)
170 }
171
172
173
174 func ReadString(src []byte) (string, []byte, bool) {
175 return readstring(src)
176 }
177
178
179
180 func AppendDocumentStart(dst []byte) (index int32, b []byte) {
181
182
183
184
185 return ReserveLength(dst)
186 }
187
188
189
190 func AppendDocumentStartInline(dst []byte, index *int32) []byte {
191 idx, doc := AppendDocumentStart(dst)
192 *index = idx
193 return doc
194 }
195
196
197 func AppendDocumentElementStart(dst []byte, key string) (index int32, b []byte) {
198 return AppendDocumentStart(AppendHeader(dst, bsontype.EmbeddedDocument, key))
199 }
200
201
202
203 func AppendDocumentEnd(dst []byte, index int32) ([]byte, error) {
204 if int(index) > len(dst)-4 {
205 return dst, fmt.Errorf("not enough bytes available after index to write length")
206 }
207 dst = append(dst, 0x00)
208 dst = UpdateLength(dst, index, int32(len(dst[index:])))
209 return dst, nil
210 }
211
212
213 func AppendDocument(dst []byte, doc []byte) []byte { return append(dst, doc...) }
214
215
216
217 func AppendDocumentElement(dst []byte, key string, doc []byte) []byte {
218 return AppendDocument(AppendHeader(dst, bsontype.EmbeddedDocument, key), doc)
219 }
220
221
222
223 func BuildDocument(dst []byte, elems ...[]byte) []byte {
224 idx, dst := ReserveLength(dst)
225 for _, elem := range elems {
226 dst = append(dst, elem...)
227 }
228 dst = append(dst, 0x00)
229 dst = UpdateLength(dst, idx, int32(len(dst[idx:])))
230 return dst
231 }
232
233
234 func BuildDocumentValue(elems ...[]byte) Value {
235 return Value{Type: bsontype.EmbeddedDocument, Data: BuildDocument(nil, elems...)}
236 }
237
238
239
240 func BuildDocumentElement(dst []byte, key string, elems ...[]byte) []byte {
241 return BuildDocument(AppendHeader(dst, bsontype.EmbeddedDocument, key), elems...)
242 }
243
244
245 var BuildDocumentFromElements = BuildDocument
246
247
248
249 func ReadDocument(src []byte) (doc Document, rem []byte, ok bool) { return readLengthBytes(src) }
250
251
252
253 func AppendArrayStart(dst []byte) (index int32, b []byte) { return ReserveLength(dst) }
254
255
256
257 func AppendArrayElementStart(dst []byte, key string) (index int32, b []byte) {
258 return AppendArrayStart(AppendHeader(dst, bsontype.Array, key))
259 }
260
261
262
263 func AppendArrayEnd(dst []byte, index int32) ([]byte, error) { return AppendDocumentEnd(dst, index) }
264
265
266 func AppendArray(dst []byte, arr []byte) []byte { return append(dst, arr...) }
267
268
269
270 func AppendArrayElement(dst []byte, key string, arr []byte) []byte {
271 return AppendArray(AppendHeader(dst, bsontype.Array, key), arr)
272 }
273
274
275 func BuildArray(dst []byte, values ...Value) []byte {
276 idx, dst := ReserveLength(dst)
277 for pos, val := range values {
278 dst = AppendValueElement(dst, strconv.Itoa(pos), val)
279 }
280 dst = append(dst, 0x00)
281 dst = UpdateLength(dst, idx, int32(len(dst[idx:])))
282 return dst
283 }
284
285
286 func BuildArrayElement(dst []byte, key string, values ...Value) []byte {
287 return BuildArray(AppendHeader(dst, bsontype.Array, key), values...)
288 }
289
290
291
292 func ReadArray(src []byte) (arr Array, rem []byte, ok bool) { return readLengthBytes(src) }
293
294
295 func AppendBinary(dst []byte, subtype byte, b []byte) []byte {
296 if subtype == 0x02 {
297 return appendBinarySubtype2(dst, subtype, b)
298 }
299 dst = append(appendLength(dst, int32(len(b))), subtype)
300 return append(dst, b...)
301 }
302
303
304
305 func AppendBinaryElement(dst []byte, key string, subtype byte, b []byte) []byte {
306 return AppendBinary(AppendHeader(dst, bsontype.Binary, key), subtype, b)
307 }
308
309
310
311 func ReadBinary(src []byte) (subtype byte, bin []byte, rem []byte, ok bool) {
312 length, rem, ok := ReadLength(src)
313 if !ok {
314 return 0x00, nil, src, false
315 }
316 if len(rem) < 1 {
317 return 0x00, nil, src, false
318 }
319 subtype, rem = rem[0], rem[1:]
320
321 if len(rem) < int(length) {
322 return 0x00, nil, src, false
323 }
324
325 if subtype == 0x02 {
326 length, rem, ok = ReadLength(rem)
327 if !ok || len(rem) < int(length) {
328 return 0x00, nil, src, false
329 }
330 }
331
332 return subtype, rem[:length], rem[length:], true
333 }
334
335
336
337 func AppendUndefinedElement(dst []byte, key string) []byte {
338 return AppendHeader(dst, bsontype.Undefined, key)
339 }
340
341
342 func AppendObjectID(dst []byte, oid primitive.ObjectID) []byte { return append(dst, oid[:]...) }
343
344
345
346 func AppendObjectIDElement(dst []byte, key string, oid primitive.ObjectID) []byte {
347 return AppendObjectID(AppendHeader(dst, bsontype.ObjectID, key), oid)
348 }
349
350
351
352 func ReadObjectID(src []byte) (primitive.ObjectID, []byte, bool) {
353 if len(src) < 12 {
354 return primitive.ObjectID{}, src, false
355 }
356 var oid primitive.ObjectID
357 copy(oid[:], src[0:12])
358 return oid, src[12:], true
359 }
360
361
362 func AppendBoolean(dst []byte, b bool) []byte {
363 if b {
364 return append(dst, 0x01)
365 }
366 return append(dst, 0x00)
367 }
368
369
370
371 func AppendBooleanElement(dst []byte, key string, b bool) []byte {
372 return AppendBoolean(AppendHeader(dst, bsontype.Boolean, key), b)
373 }
374
375
376
377 func ReadBoolean(src []byte) (bool, []byte, bool) {
378 if len(src) < 1 {
379 return false, src, false
380 }
381
382 return src[0] == 0x01, src[1:], true
383 }
384
385
386 func AppendDateTime(dst []byte, dt int64) []byte { return appendi64(dst, dt) }
387
388
389
390 func AppendDateTimeElement(dst []byte, key string, dt int64) []byte {
391 return AppendDateTime(AppendHeader(dst, bsontype.DateTime, key), dt)
392 }
393
394
395
396 func ReadDateTime(src []byte) (int64, []byte, bool) { return readi64(src) }
397
398
399 func AppendTime(dst []byte, t time.Time) []byte {
400 return AppendDateTime(dst, t.Unix()*1000+int64(t.Nanosecond()/1e6))
401 }
402
403
404
405 func AppendTimeElement(dst []byte, key string, t time.Time) []byte {
406 return AppendTime(AppendHeader(dst, bsontype.DateTime, key), t)
407 }
408
409
410
411 func ReadTime(src []byte) (time.Time, []byte, bool) {
412 dt, rem, ok := readi64(src)
413 return time.Unix(dt/1e3, dt%1e3*1e6), rem, ok
414 }
415
416
417
418 func AppendNullElement(dst []byte, key string) []byte { return AppendHeader(dst, bsontype.Null, key) }
419
420
421 func AppendRegex(dst []byte, pattern, options string) []byte {
422 if !isValidCString(pattern) || !isValidCString(options) {
423 panic(invalidRegexPanicMsg)
424 }
425
426 return append(dst, pattern+nullTerminator+options+nullTerminator...)
427 }
428
429
430
431 func AppendRegexElement(dst []byte, key, pattern, options string) []byte {
432 return AppendRegex(AppendHeader(dst, bsontype.Regex, key), pattern, options)
433 }
434
435
436
437 func ReadRegex(src []byte) (pattern, options string, rem []byte, ok bool) {
438 pattern, rem, ok = readcstring(src)
439 if !ok {
440 return "", "", src, false
441 }
442 options, rem, ok = readcstring(rem)
443 if !ok {
444 return "", "", src, false
445 }
446 return pattern, options, rem, true
447 }
448
449
450 func AppendDBPointer(dst []byte, ns string, oid primitive.ObjectID) []byte {
451 return append(appendstring(dst, ns), oid[:]...)
452 }
453
454
455
456 func AppendDBPointerElement(dst []byte, key, ns string, oid primitive.ObjectID) []byte {
457 return AppendDBPointer(AppendHeader(dst, bsontype.DBPointer, key), ns, oid)
458 }
459
460
461
462 func ReadDBPointer(src []byte) (ns string, oid primitive.ObjectID, rem []byte, ok bool) {
463 ns, rem, ok = readstring(src)
464 if !ok {
465 return "", primitive.ObjectID{}, src, false
466 }
467 oid, rem, ok = ReadObjectID(rem)
468 if !ok {
469 return "", primitive.ObjectID{}, src, false
470 }
471 return ns, oid, rem, true
472 }
473
474
475 func AppendJavaScript(dst []byte, js string) []byte { return appendstring(dst, js) }
476
477
478
479 func AppendJavaScriptElement(dst []byte, key, js string) []byte {
480 return AppendJavaScript(AppendHeader(dst, bsontype.JavaScript, key), js)
481 }
482
483
484
485 func ReadJavaScript(src []byte) (js string, rem []byte, ok bool) { return readstring(src) }
486
487
488 func AppendSymbol(dst []byte, symbol string) []byte { return appendstring(dst, symbol) }
489
490
491
492 func AppendSymbolElement(dst []byte, key, symbol string) []byte {
493 return AppendSymbol(AppendHeader(dst, bsontype.Symbol, key), symbol)
494 }
495
496
497
498 func ReadSymbol(src []byte) (symbol string, rem []byte, ok bool) { return readstring(src) }
499
500
501 func AppendCodeWithScope(dst []byte, code string, scope []byte) []byte {
502 length := int32(4 + 4 + len(code) + 1 + len(scope))
503 dst = appendLength(dst, length)
504
505 return append(appendstring(dst, code), scope...)
506 }
507
508
509
510
511 func AppendCodeWithScopeElement(dst []byte, key, code string, scope []byte) []byte {
512 return AppendCodeWithScope(AppendHeader(dst, bsontype.CodeWithScope, key), code, scope)
513 }
514
515
516
517 func ReadCodeWithScope(src []byte) (code string, scope []byte, rem []byte, ok bool) {
518 length, rem, ok := ReadLength(src)
519 if !ok || len(src) < int(length) {
520 return "", nil, src, false
521 }
522
523 code, rem, ok = readstring(rem)
524 if !ok {
525 return "", nil, src, false
526 }
527
528 scope, rem, ok = ReadDocument(rem)
529 if !ok {
530 return "", nil, src, false
531 }
532 return code, scope, rem, true
533 }
534
535
536 func AppendInt32(dst []byte, i32 int32) []byte { return appendi32(dst, i32) }
537
538
539
540 func AppendInt32Element(dst []byte, key string, i32 int32) []byte {
541 return AppendInt32(AppendHeader(dst, bsontype.Int32, key), i32)
542 }
543
544
545
546 func ReadInt32(src []byte) (int32, []byte, bool) { return readi32(src) }
547
548
549 func AppendTimestamp(dst []byte, t, i uint32) []byte {
550 return appendu32(appendu32(dst, i), t)
551 }
552
553
554
555 func AppendTimestampElement(dst []byte, key string, t, i uint32) []byte {
556 return AppendTimestamp(AppendHeader(dst, bsontype.Timestamp, key), t, i)
557 }
558
559
560
561 func ReadTimestamp(src []byte) (t, i uint32, rem []byte, ok bool) {
562 i, rem, ok = readu32(src)
563 if !ok {
564 return 0, 0, src, false
565 }
566 t, rem, ok = readu32(rem)
567 if !ok {
568 return 0, 0, src, false
569 }
570 return t, i, rem, true
571 }
572
573
574 func AppendInt64(dst []byte, i64 int64) []byte { return appendi64(dst, i64) }
575
576
577
578 func AppendInt64Element(dst []byte, key string, i64 int64) []byte {
579 return AppendInt64(AppendHeader(dst, bsontype.Int64, key), i64)
580 }
581
582
583
584 func ReadInt64(src []byte) (int64, []byte, bool) { return readi64(src) }
585
586
587 func AppendDecimal128(dst []byte, d128 primitive.Decimal128) []byte {
588 high, low := d128.GetBytes()
589 return appendu64(appendu64(dst, low), high)
590 }
591
592
593
594 func AppendDecimal128Element(dst []byte, key string, d128 primitive.Decimal128) []byte {
595 return AppendDecimal128(AppendHeader(dst, bsontype.Decimal128, key), d128)
596 }
597
598
599
600 func ReadDecimal128(src []byte) (primitive.Decimal128, []byte, bool) {
601 l, rem, ok := readu64(src)
602 if !ok {
603 return primitive.Decimal128{}, src, false
604 }
605
606 h, rem, ok := readu64(rem)
607 if !ok {
608 return primitive.Decimal128{}, src, false
609 }
610
611 return primitive.NewDecimal128(h, l), rem, true
612 }
613
614
615
616 func AppendMaxKeyElement(dst []byte, key string) []byte {
617 return AppendHeader(dst, bsontype.MaxKey, key)
618 }
619
620
621
622 func AppendMinKeyElement(dst []byte, key string) []byte {
623 return AppendHeader(dst, bsontype.MinKey, key)
624 }
625
626
627 func EqualValue(t1, t2 bsontype.Type, v1, v2 []byte) bool {
628 if t1 != t2 {
629 return false
630 }
631 v1, _, ok := readValue(v1, t1)
632 if !ok {
633 return false
634 }
635 v2, _, ok = readValue(v2, t2)
636 if !ok {
637 return false
638 }
639 return bytes.Equal(v1, v2)
640 }
641
642
643
644
645 func valueLength(src []byte, t bsontype.Type) (int32, bool) {
646 var length int32
647 ok := true
648 switch t {
649 case bsontype.Array, bsontype.EmbeddedDocument, bsontype.CodeWithScope:
650 length, _, ok = ReadLength(src)
651 case bsontype.Binary:
652 length, _, ok = ReadLength(src)
653 length += 4 + 1
654 case bsontype.Boolean:
655 length = 1
656 case bsontype.DBPointer:
657 length, _, ok = ReadLength(src)
658 length += 4 + 12
659 case bsontype.DateTime, bsontype.Double, bsontype.Int64, bsontype.Timestamp:
660 length = 8
661 case bsontype.Decimal128:
662 length = 16
663 case bsontype.Int32:
664 length = 4
665 case bsontype.JavaScript, bsontype.String, bsontype.Symbol:
666 length, _, ok = ReadLength(src)
667 length += 4
668 case bsontype.MaxKey, bsontype.MinKey, bsontype.Null, bsontype.Undefined:
669 length = 0
670 case bsontype.ObjectID:
671 length = 12
672 case bsontype.Regex:
673 regex := bytes.IndexByte(src, 0x00)
674 if regex < 0 {
675 ok = false
676 break
677 }
678 pattern := bytes.IndexByte(src[regex+1:], 0x00)
679 if pattern < 0 {
680 ok = false
681 break
682 }
683 length = int32(int64(regex) + 1 + int64(pattern) + 1)
684 default:
685 ok = false
686 }
687
688 return length, ok
689 }
690
691 func readValue(src []byte, t bsontype.Type) ([]byte, []byte, bool) {
692 length, ok := valueLength(src, t)
693 if !ok || int(length) > len(src) {
694 return nil, src, false
695 }
696
697 return src[:length], src[length:], true
698 }
699
700
701
702 func ReserveLength(dst []byte) (int32, []byte) {
703 index := len(dst)
704 return int32(index), append(dst, 0x00, 0x00, 0x00, 0x00)
705 }
706
707
708 func UpdateLength(dst []byte, index, length int32) []byte {
709 dst[index] = byte(length)
710 dst[index+1] = byte(length >> 8)
711 dst[index+2] = byte(length >> 16)
712 dst[index+3] = byte(length >> 24)
713 return dst
714 }
715
716 func appendLength(dst []byte, l int32) []byte { return appendi32(dst, l) }
717
718 func appendi32(dst []byte, i32 int32) []byte {
719 return append(dst, byte(i32), byte(i32>>8), byte(i32>>16), byte(i32>>24))
720 }
721
722
723
724
725 func ReadLength(src []byte) (int32, []byte, bool) {
726 ln, src, ok := readi32(src)
727 if ln < 0 {
728 return ln, src, false
729 }
730 return ln, src, ok
731 }
732
733 func readi32(src []byte) (int32, []byte, bool) {
734 if len(src) < 4 {
735 return 0, src, false
736 }
737 return (int32(src[0]) | int32(src[1])<<8 | int32(src[2])<<16 | int32(src[3])<<24), src[4:], true
738 }
739
740 func appendi64(dst []byte, i64 int64) []byte {
741 return append(dst,
742 byte(i64), byte(i64>>8), byte(i64>>16), byte(i64>>24),
743 byte(i64>>32), byte(i64>>40), byte(i64>>48), byte(i64>>56),
744 )
745 }
746
747 func readi64(src []byte) (int64, []byte, bool) {
748 if len(src) < 8 {
749 return 0, src, false
750 }
751 i64 := (int64(src[0]) | int64(src[1])<<8 | int64(src[2])<<16 | int64(src[3])<<24 |
752 int64(src[4])<<32 | int64(src[5])<<40 | int64(src[6])<<48 | int64(src[7])<<56)
753 return i64, src[8:], true
754 }
755
756 func appendu32(dst []byte, u32 uint32) []byte {
757 return append(dst, byte(u32), byte(u32>>8), byte(u32>>16), byte(u32>>24))
758 }
759
760 func readu32(src []byte) (uint32, []byte, bool) {
761 if len(src) < 4 {
762 return 0, src, false
763 }
764
765 return (uint32(src[0]) | uint32(src[1])<<8 | uint32(src[2])<<16 | uint32(src[3])<<24), src[4:], true
766 }
767
768 func appendu64(dst []byte, u64 uint64) []byte {
769 return append(dst,
770 byte(u64), byte(u64>>8), byte(u64>>16), byte(u64>>24),
771 byte(u64>>32), byte(u64>>40), byte(u64>>48), byte(u64>>56),
772 )
773 }
774
775 func readu64(src []byte) (uint64, []byte, bool) {
776 if len(src) < 8 {
777 return 0, src, false
778 }
779 u64 := (uint64(src[0]) | uint64(src[1])<<8 | uint64(src[2])<<16 | uint64(src[3])<<24 |
780 uint64(src[4])<<32 | uint64(src[5])<<40 | uint64(src[6])<<48 | uint64(src[7])<<56)
781 return u64, src[8:], true
782 }
783
784
785 func readcstring(src []byte) (string, []byte, bool) {
786 idx := bytes.IndexByte(src, 0x00)
787 if idx < 0 {
788 return "", src, false
789 }
790 return string(src[:idx]), src[idx+1:], true
791 }
792
793
794 func readcstringbytes(src []byte) ([]byte, []byte, bool) {
795 idx := bytes.IndexByte(src, 0x00)
796 if idx < 0 {
797 return nil, src, false
798 }
799 return src[:idx], src[idx+1:], true
800 }
801
802 func appendstring(dst []byte, s string) []byte {
803 l := int32(len(s) + 1)
804 dst = appendLength(dst, l)
805 dst = append(dst, s...)
806 return append(dst, 0x00)
807 }
808
809 func readstring(src []byte) (string, []byte, bool) {
810 l, rem, ok := ReadLength(src)
811 if !ok {
812 return "", src, false
813 }
814 if len(src[4:]) < int(l) || l == 0 {
815 return "", src, false
816 }
817
818 return string(rem[:l-1]), rem[l:], true
819 }
820
821
822
823 func readLengthBytes(src []byte) ([]byte, []byte, bool) {
824 l, _, ok := ReadLength(src)
825 if !ok {
826 return nil, src, false
827 }
828 if l < 4 {
829 return nil, src, false
830 }
831 if len(src) < int(l) {
832 return nil, src, false
833 }
834 return src[:l], src[l:], true
835 }
836
837 func appendBinarySubtype2(dst []byte, subtype byte, b []byte) []byte {
838 dst = appendLength(dst, int32(len(b)+4))
839 dst = append(dst, subtype)
840 dst = appendLength(dst, int32(len(b)))
841 return append(dst, b...)
842 }
843
844 func isValidCString(cs string) bool {
845 return !strings.ContainsRune(cs, '\x00')
846 }
847
View as plain text