@@ -9,7 +9,6 @@ package bsoncore
9
9
import (
10
10
"fmt"
11
11
"io"
12
- "math"
13
12
"strconv"
14
13
"strings"
15
14
)
@@ -83,55 +82,79 @@ func (a Array) DebugString() string {
83
82
// String outputs an ExtendedJSON version of Array. If the Array is not valid, this method
84
83
// returns an empty string.
85
84
func (a Array ) String () string {
86
- return a .StringN (math .MaxInt )
85
+ str , _ := a .StringN (- 1 )
86
+ return str
87
87
}
88
88
89
- // StringN stringifies an array upto N bytes
90
- func (a Array ) StringN (n int ) string {
91
- if lens , _ , _ := ReadLength (a ); lens < 5 || n <= 0 {
92
- return ""
89
+ // StringN stringifies an array. If N is non-negative, it will truncate the string to N bytes.
90
+ // Otherwise, it will return the full string representation. The second return value indicates
91
+ // whether the string was truncated or not.
92
+ func (a Array ) StringN (n int ) (string , bool ) {
93
+ length , rem , ok := ReadLength (a )
94
+ if ! ok || length < 5 {
95
+ return "" , false
96
+ }
97
+ length -= 4 // length bytes
98
+ length -- // final null byte
99
+
100
+ if n == 0 {
101
+ return "" , true
93
102
}
94
103
95
104
var buf strings.Builder
96
105
buf .WriteByte ('[' )
97
106
98
- length , rem , _ := ReadLength (a ) // We know we have enough bytes to read the length
99
- length -= 4
100
-
107
+ var truncated bool
101
108
var elem Element
102
- var ok bool
103
-
104
- if n > 0 {
105
- for length > 1 {
106
- elem , rem , ok = ReadElement (rem )
107
-
108
- length -= int32 (len (elem ))
109
- if ! ok {
110
- return ""
111
- }
112
-
113
- str := elem .Value ().StringN (n - buf .Len ())
114
-
115
- buf .WriteString (str )
116
-
117
- if buf .Len () == n {
118
- return buf .String ()
109
+ var str string
110
+ first := true
111
+ for length > 0 && ! truncated {
112
+ needStrLen := - 1
113
+ // Set needStrLen if n is positive, meaning we want to limit the string length.
114
+ if n > 0 {
115
+ // Stop stringifying if we reach the limit, that also ensures needStrLen is
116
+ // greater than 0 if we need to limit the length.
117
+ if buf .Len () >= n {
118
+ truncated = true
119
+ break
119
120
}
121
+ needStrLen = n - buf .Len ()
122
+ }
120
123
121
- if length > 1 {
122
- buf .WriteByte (',' )
124
+ // Append a comma if this is not the first element.
125
+ if ! first {
126
+ buf .WriteByte (',' )
127
+ // If we are truncating, we need to account for the comma in the length.
128
+ if needStrLen > 0 {
129
+ needStrLen --
130
+ if needStrLen == 0 {
131
+ truncated = true
132
+ break
133
+ }
123
134
}
124
135
}
125
- if length != 1 { // Missing final null byte or inaccurate length
126
- return ""
136
+
137
+ elem , rem , ok = ReadElement (rem )
138
+ length -= int32 (len (elem ))
139
+ // Exit on malformed element.
140
+ if ! ok || length < 0 {
141
+ return "" , false
127
142
}
143
+
144
+ // Delegate to StringN() on the element.
145
+ str , truncated = elem .Value ().StringN (needStrLen )
146
+ buf .WriteString (str )
147
+
148
+ first = false
128
149
}
129
150
130
- if buf .Len ()+ 1 <= n {
151
+ if n <= 0 || ( buf .Len () < n && ! truncated ) {
131
152
buf .WriteByte (']' )
153
+ } else {
154
+ truncated = true
132
155
}
133
156
134
- return buf .String ()
157
+ return buf .String (), truncated
135
158
}
136
159
137
160
// Values returns this array as a slice of values. The returned slice will contain valid values.
0 commit comments