Skip to content

Commit a709a1d

Browse files
authored
GODRIVER-3605 Refactor StringN (#2128)
1 parent 4ee9c12 commit a709a1d

File tree

10 files changed

+629
-621
lines changed

10 files changed

+629
-621
lines changed

bson/raw_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ func BenchmarkRawString(b *testing.B) {
450450
b.ReportAllocs()
451451
b.ResetTimer()
452452
for i := 0; i < b.N; i++ {
453-
_ = bsoncore.Document(bs).StringN(1024) // Assuming you want to limit to 1024 bytes for this benchmark
453+
_, _ = bsoncore.Document(bs).StringN(1024) // Assuming you want to limit to 1024 bytes for this benchmark
454454
}
455455
})
456456
}
@@ -473,7 +473,7 @@ func TestComplexDocuments_StringN(t *testing.T) {
473473
bson, _ := Marshal(tc.doc)
474474
bsonDoc := bsoncore.Document(bson)
475475

476-
got := bsonDoc.StringN(tc.n)
476+
got, _ := bsonDoc.StringN(tc.n)
477477
assert.Equal(t, tc.n, len(got))
478478
})
479479
}
@@ -518,7 +518,7 @@ func createMassiveArraysDocument(arraySize int) D {
518518
func createUniqueVoluminousDocument(t *testing.T, size int) bsoncore.Document {
519519
t.Helper()
520520

521-
docs := make(D, size)
521+
docs := make(D, 0, size)
522522

523523
for i := 0; i < size; i++ {
524524
docs = append(docs, E{
@@ -561,7 +561,7 @@ func createLargeSingleDoc(t *testing.T) bsoncore.Document {
561561
func createVoluminousArrayDocuments(t *testing.T, size int) bsoncore.Document {
562562
t.Helper()
563563

564-
docs := make(D, size)
564+
docs := make(D, 0, size)
565565

566566
for i := 0; i < size; i++ {
567567
docs = append(docs, E{

internal/bsoncoreutil/bsoncoreutil.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package bsoncoreutil
88

99
// Truncate truncates a given string for a certain width
1010
func Truncate(str string, width int) string {
11-
if width == 0 {
11+
if width <= 0 {
1212
return ""
1313
}
1414

internal/logger/logger.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,9 @@ func FormatDocument(msg bson.Raw, width uint) string {
241241
return "{}"
242242
}
243243

244-
str := bsoncore.Document(msg).StringN(int(width))
244+
str, truncated := bsoncore.Document(msg).StringN(int(width))
245245

246-
// If the last byte is not a closing bracket, then the document was truncated
247-
if len(str) > 0 && str[len(str)-1] != '}' {
246+
if truncated {
248247
str += TruncationSuffix
249248
}
250249

x/bsonx/bsoncore/array.go

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package bsoncore
99
import (
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.
8584
func (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

Comments
 (0)