@@ -9,7 +9,6 @@ package bsoncore
99import (
1010 "fmt"
1111 "io"
12- "math"
1312 "strconv"
1413 "strings"
1514)
@@ -83,55 +82,79 @@ func (a Array) DebugString() string {
8382// String outputs an ExtendedJSON version of Array. If the Array is not valid, this method
8483// returns an empty string.
8584func (a Array ) String () string {
86- return a .StringN (math .MaxInt )
85+ str , _ := a .StringN (- 1 )
86+ return str
8787}
8888
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
93102 }
94103
95104 var buf strings.Builder
96105 buf .WriteByte ('[' )
97106
98- length , rem , _ := ReadLength (a ) // We know we have enough bytes to read the length
99- length -= 4
100-
107+ var truncated bool
101108 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
119120 }
121+ needStrLen = n - buf .Len ()
122+ }
120123
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+ }
123134 }
124135 }
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
127142 }
143+
144+ // Delegate to StringN() on the element.
145+ str , truncated = elem .Value ().StringN (needStrLen )
146+ buf .WriteString (str )
147+
148+ first = false
128149 }
129150
130- if buf .Len ()+ 1 <= n {
151+ if n <= 0 || ( buf .Len () < n && ! truncated ) {
131152 buf .WriteByte (']' )
153+ } else {
154+ truncated = true
132155 }
133156
134- return buf .String ()
157+ return buf .String (), truncated
135158}
136159
137160// Values returns this array as a slice of values. The returned slice will contain valid values.
0 commit comments