33
33
public class ByteBufferBsonInput implements BsonInput {
34
34
35
35
private static final String [] ONE_BYTE_ASCII_STRINGS = new String [Byte .MAX_VALUE + 1 ];
36
+ /* A dynamically sized scratch buffer, that is reused across BSON String reads:
37
+ * 1. Reduces garbage collection by avoiding new byte array creation.
38
+ * 2. Improves cache utilization through temporal locality.
39
+ * 3. Avoids JVM allocation and zeroing cost for new memory allocations.
40
+ */
41
+ private byte [] scratchBuffer ;
42
+
36
43
37
44
static {
38
45
for (int b = 0 ; b < ONE_BYTE_ASCII_STRINGS .length ; b ++) {
@@ -127,15 +134,12 @@ public String readString() {
127
134
128
135
@ Override
129
136
public String readCString () {
130
- int mark = buffer .position ();
131
- skipCString ();
132
- int size = buffer .position () - mark ;
133
- buffer .position (mark );
137
+ int size = computeCStringLength (buffer .position ());
134
138
return readString (size );
135
139
}
136
140
137
- private String readString (final int size ) {
138
- if (size == 2 ) {
141
+ private String readString (final int bsonStringSize ) {
142
+ if (bsonStringSize == 2 ) {
139
143
byte asciiByte = buffer .get (); // if only one byte in the string, it must be ascii.
140
144
byte nullByte = buffer .get (); // read null terminator
141
145
if (nullByte != 0 ) {
@@ -146,26 +150,51 @@ private String readString(final int size) {
146
150
}
147
151
return ONE_BYTE_ASCII_STRINGS [asciiByte ]; // this will throw if asciiByte is negative
148
152
} else {
149
- byte [] bytes = new byte [size - 1 ];
150
- buffer .get (bytes );
151
- byte nullByte = buffer .get ();
152
- if (nullByte != 0 ) {
153
- throw new BsonSerializationException ("Found a BSON string that is not null-terminated" );
153
+ if (buffer .isBackedByArray ()) {
154
+ int position = buffer .position ();
155
+ int arrayOffset = buffer .arrayOffset ();
156
+ int newPosition = position + bsonStringSize ;
157
+ buffer .position (newPosition );
158
+
159
+ byte [] array = buffer .array ();
160
+ if (array [arrayOffset + newPosition - 1 ] != 0 ) {
161
+ throw new BsonSerializationException ("Found a BSON string that is not null-terminated" );
162
+ }
163
+ return new String (array , arrayOffset + position , bsonStringSize - 1 , StandardCharsets .UTF_8 );
164
+ } else if (scratchBuffer == null || bsonStringSize > scratchBuffer .length ) {
165
+ int scratchBufferSize = bsonStringSize + (bsonStringSize >>> 1 ); //1.5 times the size
166
+ scratchBuffer = new byte [scratchBufferSize ];
167
+ }
168
+
169
+ buffer .get (scratchBuffer , 0 , bsonStringSize );
170
+ if (scratchBuffer [bsonStringSize - 1 ] != 0 ) {
171
+ throw new BsonSerializationException ("BSON string not null-terminated" );
154
172
}
155
- return new String (bytes , StandardCharsets .UTF_8 );
173
+ return new String (scratchBuffer , 0 , bsonStringSize - 1 , StandardCharsets .UTF_8 );
156
174
}
157
175
}
158
176
159
177
@ Override
160
178
public void skipCString () {
161
179
ensureOpen ();
162
- boolean checkNext = true ;
163
- while (checkNext ) {
164
- if (!buffer .hasRemaining ()) {
165
- throw new BsonSerializationException ("Found a BSON string that is not null-terminated" );
180
+ int pos = buffer .position ();
181
+ int length = computeCStringLength (pos );
182
+ buffer .position (pos + length );
183
+ }
184
+
185
+ private int computeCStringLength (final int prevPos ) {
186
+ ensureOpen ();
187
+ int pos = buffer .position ();
188
+ int limit = buffer .limit ();
189
+
190
+ while (pos < limit ) {
191
+ if (buffer .get (pos ++) == 0 ) {
192
+ return (pos - prevPos );
166
193
}
167
- checkNext = buffer .get () != 0 ;
168
194
}
195
+
196
+ buffer .position (pos );
197
+ throw new BsonSerializationException ("Found a BSON string that is not null-terminated" );
169
198
}
170
199
171
200
@ Override
0 commit comments