@@ -28,6 +28,13 @@ var vwPool = sync.Pool{
28
28
},
29
29
}
30
30
31
+ func putValueWriter (vw * valueWriter ) {
32
+ if vw != nil {
33
+ vw .w = nil // don't leak the writer
34
+ vwPool .Put (vw )
35
+ }
36
+ }
37
+
31
38
// BSONValueWriterPool is a pool for BSON ValueWriters.
32
39
//
33
40
// Deprecated: BSONValueWriterPool will not be supported in Go Driver 2.0.
@@ -149,32 +156,21 @@ type valueWriter struct {
149
156
}
150
157
151
158
func (vw * valueWriter ) advanceFrame () {
152
- if vw .frame + 1 >= int64 (len (vw .stack )) { // We need to grow the stack
153
- length := len (vw .stack )
154
- if length + 1 >= cap (vw .stack ) {
155
- // double it
156
- buf := make ([]vwState , 2 * cap (vw .stack )+ 1 )
157
- copy (buf , vw .stack )
158
- vw .stack = buf
159
- }
160
- vw .stack = vw .stack [:length + 1 ]
161
- }
162
159
vw .frame ++
160
+ if vw .frame >= int64 (len (vw .stack )) {
161
+ vw .stack = append (vw .stack , vwState {})
162
+ }
163
163
}
164
164
165
165
func (vw * valueWriter ) push (m mode ) {
166
166
vw .advanceFrame ()
167
167
168
168
// Clean the stack
169
- vw .stack [vw .frame ].mode = m
170
- vw .stack [vw .frame ].key = ""
171
- vw .stack [vw .frame ].arrkey = 0
172
- vw .stack [vw .frame ].start = 0
169
+ vw .stack [vw .frame ] = vwState {mode : m }
173
170
174
- vw .stack [vw .frame ].mode = m
175
171
switch m {
176
172
case mDocument , mArray , mCodeWithScope :
177
- vw .reserveLength ()
173
+ vw .reserveLength () // WARN: this is not needed
178
174
}
179
175
}
180
176
@@ -213,6 +209,7 @@ func newValueWriter(w io.Writer) *valueWriter {
213
209
return vw
214
210
}
215
211
212
+ // TODO: only used in tests
216
213
func newValueWriterFromSlice (buf []byte ) * valueWriter {
217
214
vw := new (valueWriter )
218
215
stack := make ([]vwState , 1 , 5 )
@@ -249,17 +246,16 @@ func (vw *valueWriter) invalidTransitionError(destination mode, name string, mod
249
246
}
250
247
251
248
func (vw * valueWriter ) writeElementHeader (t bsontype.Type , destination mode , callerName string , addmodes ... mode ) error {
252
- switch vw .stack [vw .frame ].mode {
249
+ frame := & vw .stack [vw .frame ]
250
+ switch frame .mode {
253
251
case mElement :
254
- key := vw . stack [ vw . frame ] .key
252
+ key := frame .key
255
253
if ! isValidCString (key ) {
256
254
return errors .New ("BSON element key cannot contain null bytes" )
257
255
}
258
-
259
- vw .buf = bsoncore .AppendHeader (vw .buf , t , key )
256
+ vw .appendHeader (t , key )
260
257
case mValue :
261
- // TODO: Do this with a cache of the first 1000 or so array keys.
262
- vw .buf = bsoncore .AppendHeader (vw .buf , t , strconv .Itoa (vw .stack [vw .frame ].arrkey ))
258
+ vw .appendIntHeader (t , frame .arrkey )
263
259
default :
264
260
modes := []mode {mElement , mValue }
265
261
if addmodes != nil {
@@ -601,9 +597,11 @@ func (vw *valueWriter) writeLength() error {
601
597
if length > maxSize {
602
598
return errMaxDocumentSizeExceeded {size : int64 (len (vw .buf ))}
603
599
}
604
- length = length - int (vw .stack [vw .frame ].start )
605
- start := vw .stack [vw .frame ].start
600
+ frame := & vw .stack [vw .frame ]
601
+ length = length - int (frame .start )
602
+ start := frame .start
606
603
604
+ _ = vw .buf [start + 3 ] // BCE
607
605
vw .buf [start + 0 ] = byte (length )
608
606
vw .buf [start + 1 ] = byte (length >> 8 )
609
607
vw .buf [start + 2 ] = byte (length >> 16 )
@@ -612,5 +610,31 @@ func (vw *valueWriter) writeLength() error {
612
610
}
613
611
614
612
func isValidCString (cs string ) bool {
615
- return ! strings .ContainsRune (cs , '\x00' )
613
+ // Disallow the zero byte in a cstring because the zero byte is used as the
614
+ // terminating character.
615
+ //
616
+ // It's safe to check bytes instead of runes because all multibyte UTF-8
617
+ // code points start with (binary) 11xxxxxx or 10xxxxxx, so 00000000 (i.e.
618
+ // 0) will never be part of a multibyte UTF-8 code point. This logic is the
619
+ // same as the "r < utf8.RuneSelf" case in strings.IndexRune but can be
620
+ // inlined.
621
+ //
622
+ // https://cs.opensource.google/go/go/+/refs/tags/go1.21.1:src/strings/strings.go;l=127
623
+ return strings .IndexByte (cs , 0 ) == - 1
624
+ }
625
+
626
+ // appendHeader is the same as bsoncore.AppendHeader but does not check if the
627
+ // key is a valid C string since the caller has already checked for that.
628
+ //
629
+ // The caller of this function must check if key is a valid C string.
630
+ func (vw * valueWriter ) appendHeader (t bsontype.Type , key string ) {
631
+ vw .buf = bsoncore .AppendType (vw .buf , t )
632
+ vw .buf = append (vw .buf , key ... )
633
+ vw .buf = append (vw .buf , 0x00 )
634
+ }
635
+
636
+ func (vw * valueWriter ) appendIntHeader (t bsontype.Type , key int ) {
637
+ vw .buf = bsoncore .AppendType (vw .buf , t )
638
+ vw .buf = strconv .AppendInt (vw .buf , int64 (key ), 10 )
639
+ vw .buf = append (vw .buf , 0x00 )
616
640
}
0 commit comments