@@ -74,6 +74,9 @@ final class ByteBufferBsonOutputTest {
74
74
private static final List <Integer > ALL_SURROGATE_CODE_POINTS = Stream .concat (
75
75
range (MIN_LOW_SURROGATE , MAX_LOW_SURROGATE ).boxed (),
76
76
range (MIN_HIGH_SURROGATE , MAX_HIGH_SURROGATE ).boxed ()).collect (toList ());
77
+ public static final List <Integer > ALL_UTF_16_CODE_POINTS_FORMED_BY_SURROGATE_PAIRS = rangeClosed (0x10000 , MAX_CODE_POINT )
78
+ .boxed ()
79
+ .collect (toList ());
77
80
78
81
static Stream <BufferProvider > bufferProviders () {
79
82
return Stream .of (
@@ -630,10 +633,18 @@ void shouldGrowToMaximumAllowedSizeOfByteBuffer(final boolean useBranch, final B
630
633
try (ByteBufferBsonOutput out = new ByteBufferBsonOutput (bufferProvider )) {
631
634
byte [] v = new byte [0x2000000 ];
632
635
ThreadLocalRandom .current ().nextBytes (v );
633
- Consumer <ByteBufferBsonOutput > assertByteBuffers = effectiveOut -> assertEquals (
634
- asList (1 << 10 , 1 << 11 , 1 << 12 , 1 << 13 , 1 << 14 , 1 << 15 , 1 << 16 , 1 << 17 , 1 << 18 , 1 << 19 , 1 << 20 ,
635
- 1 << 21 , 1 << 22 , 1 << 23 , 1 << 24 , 1 << 24 ),
636
- effectiveOut .getByteBuffers ().stream ().map (ByteBuf ::capacity ).collect (toList ()));
636
+ Consumer <ByteBufferBsonOutput > assertByteBuffers = effectiveOut -> {
637
+ List <ByteBuf > byteBuffers = new ArrayList <>();
638
+ try {
639
+ byteBuffers = effectiveOut .getByteBuffers ();
640
+ assertEquals (
641
+ asList (1 << 10 , 1 << 11 , 1 << 12 , 1 << 13 , 1 << 14 , 1 << 15 , 1 << 16 , 1 << 17 , 1 << 18 , 1 << 19 , 1 << 20 ,
642
+ 1 << 21 , 1 << 22 , 1 << 23 , 1 << 24 , 1 << 24 ),
643
+ byteBuffers .stream ().map (ByteBuf ::capacity ).collect (toList ()));
644
+ } finally {
645
+ byteBuffers .forEach (ByteBuf ::release );
646
+ }
647
+ };
637
648
Consumer <ByteBufferBsonOutput > assertions = effectiveOut -> {
638
649
effectiveOut .writeBytes (v );
639
650
assertEquals (v .length , effectiveOut .size ());
@@ -835,8 +846,8 @@ void shouldWriteInt32AbsoluteValueWithinSpanningBuffers(
835
846
final List <byte []> expectedBuffers ,
836
847
final BufferProvider bufferProvider ) {
837
848
838
- try ( ByteBufferBsonOutput output =
839
- new ByteBufferBsonOutput (size -> bufferProvider .getBuffer (Integer .BYTES ))) {
849
+ List < ByteBuf > buffers = new ArrayList <>();
850
+ try ( ByteBufferBsonOutput output = new ByteBufferBsonOutput (size -> bufferProvider .getBuffer (Integer .BYTES ))) {
840
851
841
852
//given
842
853
initialData .forEach (output ::writeBytes );
@@ -845,9 +856,11 @@ void shouldWriteInt32AbsoluteValueWithinSpanningBuffers(
845
856
output .writeInt32 (absolutePosition , intValue );
846
857
847
858
//then
848
- List < ByteBuf > buffers = output .getByteBuffers ();
859
+ buffers = output .getByteBuffers ();
849
860
assertEquals (expectedBuffers .size (), buffers .size (), "Number of buffers mismatch" );
850
861
assertBufferContents (expectedBuffers , buffers );
862
+ }finally {
863
+ buffers .forEach (ByteBuf ::release );
851
864
}
852
865
}
853
866
@@ -1086,10 +1099,10 @@ class Utf8StringTests {
1086
1099
@ MethodSource ("com.mongodb.internal.connection.ByteBufferBsonOutputTest#bufferProviders" )
1087
1100
void shouldWriteCStringAcrossBuffersUTF8 (final BufferProvider bufferProvider ) throws IOException {
1088
1101
for (Integer codePoint : ALL_CODE_POINTS_EXCLUDING_SURROGATES ) {
1089
- String str = new String (Character .toChars (codePoint )) + "a" ;
1090
- byte [] expectedStringEncoding = str .getBytes (StandardCharsets .UTF_8 );
1102
+ String stringToEncode = new String (Character .toChars (codePoint )) + "a" ;
1103
+ byte [] expectedStringEncoding = stringToEncode .getBytes (StandardCharsets .UTF_8 );
1091
1104
int bufferAllocationSize = expectedStringEncoding .length + "\u0000 " .length ();
1092
- testWriteCStringAcrossBuffers (bufferProvider , codePoint , bufferAllocationSize , str , expectedStringEncoding );
1105
+ testWriteCStringAcrossBuffers (bufferProvider , codePoint , bufferAllocationSize , stringToEncode , expectedStringEncoding );
1093
1106
}
1094
1107
}
1095
1108
@@ -1098,11 +1111,11 @@ void shouldWriteCStringAcrossBuffersUTF8(final BufferProvider bufferProvider) th
1098
1111
@ MethodSource ("com.mongodb.internal.connection.ByteBufferBsonOutputTest#bufferProviders" )
1099
1112
void shouldWriteCStringAcrossBuffersUTF8WithBranch (final BufferProvider bufferProvider ) throws IOException {
1100
1113
for (Integer codePoint : ALL_CODE_POINTS_EXCLUDING_SURROGATES ) {
1101
- String str = new String (Character .toChars (codePoint )) + "a" ;
1102
- int bufferAllocationSize = str .getBytes (StandardCharsets .UTF_8 ).length + "\u0000 " .length ();
1103
- byte [] expectedEncoding = str .getBytes (StandardCharsets .UTF_8 );
1114
+ String stringToEncode = new String (Character .toChars (codePoint )) + "a" ;
1115
+ int bufferAllocationSize = stringToEncode .getBytes (StandardCharsets .UTF_8 ).length + "\u0000 " .length ();
1116
+ byte [] expectedEncoding = stringToEncode .getBytes (StandardCharsets .UTF_8 );
1104
1117
1105
- testWriteCStringAcrossBufferWithBranch (bufferProvider , codePoint , bufferAllocationSize , str , expectedEncoding );
1118
+ testWriteCStringAcrossBufferWithBranch (bufferProvider , codePoint , bufferAllocationSize , stringToEncode , expectedEncoding );
1106
1119
}
1107
1120
}
1108
1121
@@ -1112,11 +1125,15 @@ void shouldWriteCStringAcrossBuffersUTF8WithBranch(final BufferProvider bufferPr
1112
1125
void shouldWriteStringAcrossBuffersUTF8 (final BufferProvider bufferProvider ) throws IOException {
1113
1126
for (Integer codePoint : ALL_CODE_POINTS_EXCLUDING_SURROGATES ) {
1114
1127
// given
1115
- String str = new String (Character .toChars (codePoint )) + "a" ;
1128
+ String stringToEncode = new String (Character .toChars (codePoint )) + "a" ;
1116
1129
//4 bytes for the length prefix, bytes for encoded String, and 1 byte for the null terminator
1117
- int bufferAllocationSize = Integer .BYTES + str .getBytes (StandardCharsets .UTF_8 ).length + "\u0000 " .length ();
1118
- byte [] expectedEncoding = str .getBytes (StandardCharsets .UTF_8 );
1119
- testWriteStringAcrossBuffers (bufferProvider , codePoint , bufferAllocationSize , str , expectedEncoding );
1130
+ int bufferAllocationSize = Integer .BYTES + stringToEncode .getBytes (StandardCharsets .UTF_8 ).length + "\u0000 " .length ();
1131
+ byte [] expectedEncoding = stringToEncode .getBytes (StandardCharsets .UTF_8 );
1132
+ testWriteStringAcrossBuffers (bufferProvider ,
1133
+ codePoint ,
1134
+ bufferAllocationSize ,
1135
+ stringToEncode ,
1136
+ expectedEncoding );
1120
1137
}
1121
1138
}
1122
1139
@@ -1192,6 +1209,78 @@ void shouldWriteCStringWithMalformedSurrogatesWithBranch(final BufferProvider bu
1192
1209
}
1193
1210
}
1194
1211
1212
+ @ DisplayName ("should write surrogate String across buffers" )
1213
+ @ ParameterizedTest
1214
+ @ MethodSource ("com.mongodb.internal.connection.ByteBufferBsonOutputTest#bufferProviders" )
1215
+ void shouldWriteStringWithSurrogatePairs (final BufferProvider bufferProvider ) throws IOException {
1216
+ for (Integer surrogateCodePoint : ALL_UTF_16_CODE_POINTS_FORMED_BY_SURROGATE_PAIRS ) {
1217
+ String stringToEncode = new String (toSurrogatePair (surrogateCodePoint ));
1218
+ byte [] expectedEncoding = stringToEncode .getBytes (StandardCharsets .UTF_8 );
1219
+ int bufferAllocationSize = expectedEncoding .length + "\u0000 " .length ();
1220
+
1221
+ testWriteCStringAcrossBufferWithBranch (
1222
+ bufferProvider ,
1223
+ surrogateCodePoint ,
1224
+ bufferAllocationSize ,
1225
+ stringToEncode ,
1226
+ expectedEncoding );
1227
+ }
1228
+ }
1229
+
1230
+ @ DisplayName ("should write surrogate String across buffers with branch" )
1231
+ @ ParameterizedTest
1232
+ @ MethodSource ("com.mongodb.internal.connection.ByteBufferBsonOutputTest#bufferProviders" )
1233
+ void shouldWriteStringWithSurrogatePairsWithBranch (final BufferProvider bufferProvider ) throws IOException {
1234
+ for (Integer surrogateCodePoint : ALL_UTF_16_CODE_POINTS_FORMED_BY_SURROGATE_PAIRS ) {
1235
+ String stringToEncode = new String (toSurrogatePair (surrogateCodePoint ));
1236
+ byte [] expectedEncoding = stringToEncode .getBytes (StandardCharsets .UTF_8 );
1237
+ int bufferAllocationSize = expectedEncoding .length + "\u0000 " .length ();
1238
+
1239
+ testWriteStringAcrossBuffersWithBranch (
1240
+ bufferProvider ,
1241
+ bufferAllocationSize ,
1242
+ stringToEncode ,
1243
+ surrogateCodePoint ,
1244
+ expectedEncoding );
1245
+ }
1246
+ }
1247
+
1248
+ @ DisplayName ("should write surrogate CString across buffers" )
1249
+ @ ParameterizedTest
1250
+ @ MethodSource ("com.mongodb.internal.connection.ByteBufferBsonOutputTest#bufferProviders" )
1251
+ void shouldWriteCStringWithSurrogatePairs (final BufferProvider bufferProvider ) throws IOException {
1252
+ for (Integer surrogateCodePoint : ALL_UTF_16_CODE_POINTS_FORMED_BY_SURROGATE_PAIRS ) {
1253
+ String stringToEncode = new String (toSurrogatePair (surrogateCodePoint ));
1254
+ byte [] expectedEncoding = stringToEncode .getBytes (StandardCharsets .UTF_8 );
1255
+ int bufferAllocationSize = expectedEncoding .length + "\u0000 " .length ();
1256
+
1257
+ testWriteCStringAcrossBufferWithBranch (
1258
+ bufferProvider ,
1259
+ surrogateCodePoint ,
1260
+ bufferAllocationSize ,
1261
+ stringToEncode ,
1262
+ expectedEncoding );
1263
+ }
1264
+ }
1265
+
1266
+ @ DisplayName ("should write surrogate CString across buffers with branch" )
1267
+ @ ParameterizedTest
1268
+ @ MethodSource ("com.mongodb.internal.connection.ByteBufferBsonOutputTest#bufferProviders" )
1269
+ void shouldWriteCStringWithSurrogatePairsWithBranch (final BufferProvider bufferProvider ) throws IOException {
1270
+ for (Integer surrogateCodePoint : ALL_UTF_16_CODE_POINTS_FORMED_BY_SURROGATE_PAIRS ) {
1271
+ String stringToEncode = new String (toSurrogatePair (surrogateCodePoint ));
1272
+ byte [] expectedEncoding = stringToEncode .getBytes (StandardCharsets .UTF_8 );
1273
+ int bufferAllocationSize = expectedEncoding .length + "\u0000 " .length ();
1274
+
1275
+ testWriteCStringAcrossBufferWithBranch (
1276
+ bufferProvider ,
1277
+ surrogateCodePoint ,
1278
+ bufferAllocationSize ,
1279
+ stringToEncode ,
1280
+ expectedEncoding );
1281
+ }
1282
+ }
1283
+
1195
1284
/*
1196
1285
Tests that malformed surrogate pairs are encoded as-is without substituting any code point.
1197
1286
This known bug and corresponding test remain for backward compatibility.
@@ -1207,14 +1296,14 @@ void shouldWriteStringWithMalformedSurrogates(final BufferProvider bufferProvide
1207
1296
(byte ) (0x80 | ((surrogateCodePoint >> 6 ) & 0x3F )),
1208
1297
(byte ) (0x80 | (surrogateCodePoint & 0x3F ))
1209
1298
};
1210
- String str = new String (Character .toChars (surrogateCodePoint ));
1299
+ String stringToEncode = new String (Character .toChars (surrogateCodePoint ));
1211
1300
int bufferAllocationSize = expectedEncoding .length + "\u0000 " .length ();
1212
1301
1213
- testWriteCStringAcrossBufferWithBranch (
1302
+ testWriteStringAcrossBuffers (
1214
1303
bufferProvider ,
1215
1304
surrogateCodePoint ,
1216
1305
bufferAllocationSize ,
1217
- str ,
1306
+ stringToEncode ,
1218
1307
expectedEncoding );
1219
1308
}
1220
1309
}
@@ -1281,7 +1370,7 @@ private void testWriteCStringAcrossBuffers(final BufferProvider bufferProvider,
1281
1370
private void testWriteStringAcrossBuffers (final BufferProvider bufferProvider ,
1282
1371
final Integer codePoint ,
1283
1372
final int bufferAllocationSize ,
1284
- final String str ,
1373
+ final String stringToEncode ,
1285
1374
final byte [] expectedEncoding ) throws IOException {
1286
1375
for (int startingOffset = 0 ; startingOffset <= bufferAllocationSize ; startingOffset ++) {
1287
1376
//given
@@ -1293,7 +1382,7 @@ private void testWriteStringAcrossBuffers(final BufferProvider bufferProvider,
1293
1382
actualBsonOutput .write (new byte [startingOffset ]);
1294
1383
1295
1384
// when
1296
- actualBsonOutput .writeString (str );
1385
+ actualBsonOutput .writeString (stringToEncode );
1297
1386
1298
1387
// then
1299
1388
actualByteBuffers = actualBsonOutput .getDuplicateByteBuffers ();
@@ -1395,7 +1484,8 @@ private void assertEncodedStringSize(final Integer codePoint,
1395
1484
startingOffset ));
1396
1485
}
1397
1486
1398
- private void testWriteCStringAcrossBufferWithBranch (final BufferProvider bufferProvider , final Integer codePoint ,
1487
+ private void testWriteCStringAcrossBufferWithBranch (final BufferProvider bufferProvider ,
1488
+ final Integer codePoint ,
1399
1489
final int bufferAllocationSize ,
1400
1490
final String str , final byte [] expectedEncoding ) throws IOException {
1401
1491
for (int startingOffset = 0 ; startingOffset <= bufferAllocationSize ; startingOffset ++) {
@@ -1488,6 +1578,17 @@ private void assertEncodedResult(final int codePoint,
1488
1578
codePoint ,
1489
1579
startingOffset ));
1490
1580
}
1581
+
1582
+ public char [] toSurrogatePair (int codePoint ) {
1583
+ if (!Character .isValidCodePoint (codePoint ) || codePoint < 0x10000 ) {
1584
+ throw new IllegalArgumentException ("Invalid code point: " + codePoint );
1585
+ }
1586
+ char [] result = new char [2 ];
1587
+ result [0 ] = Character .highSurrogate (codePoint );
1588
+ result [1 ] = Character .lowSurrogate (codePoint );
1589
+ return result ;
1590
+ }
1591
+
1491
1592
}
1492
1593
1493
1594
private static byte [] getBytes (final OutputBuffer basicOutputBuffer ) throws IOException {
0 commit comments