|
23 | 23 |
|
24 | 24 | import java.io.IOException;
|
25 | 25 | import java.io.OutputStream;
|
| 26 | +import java.nio.ByteBuffer; |
26 | 27 | import java.nio.ByteOrder;
|
27 | 28 | import java.util.ArrayList;
|
28 | 29 | import java.util.List;
|
@@ -284,120 +285,54 @@ protected int writeCharacters(final String str, final boolean checkForNullCharac
|
284 | 285 | if (buf.hasArray()) {
|
285 | 286 | return writeCharactersOnArray(str, checkForNullCharacters, buf);
|
286 | 287 | } else if (buf instanceof NettyByteBuf) {
|
287 |
| - return writeCharactersOnNettyByteBuf(str, checkForNullCharacters, buf); |
| 288 | + io.netty.buffer.ByteBuf nettyBuffer = ((NettyByteBuf) buf).asByteBuf(); |
| 289 | + if (nettyBuffer.nioBufferCount() == 1) { |
| 290 | + return writeCharactersOnInternalNioNettyByteBuf(str, checkForNullCharacters, buf, nettyBuffer); |
| 291 | + } |
288 | 292 | }
|
289 | 293 | }
|
290 | 294 | return super.writeCharacters(str, 0, checkForNullCharacters);
|
291 | 295 | }
|
292 | 296 |
|
293 |
| - private static void validateNoNullSingleByteChars(String str, long chars, int i) { |
294 |
| - long tmp = (chars & 0x7F7F7F7F7F7F7F7FL) + 0x7F7F7F7F7F7F7F7FL; |
295 |
| - tmp = ~(tmp | chars | 0x7F7F7F7F7F7F7F7FL); |
296 |
| - if (tmp != 0) { |
297 |
| - int firstZero = Long.numberOfTrailingZeros(tmp) >>> 3; |
298 |
| - throw new BsonSerializationException(format("BSON cstring '%s' is not valid because it contains a null character " |
299 |
| - + "at index %d", str, i + firstZero)); |
300 |
| - } |
301 |
| - } |
302 |
| - |
303 |
| - private static void validateNoNullAsciiCharacters(String str, long asciiChars, int i) { |
304 |
| - // simplified Hacker's delight search for zero with ASCII chars i.e. which doesn't use the MSB |
305 |
| - long tmp = asciiChars + 0x7F7F7F7F7F7F7F7FL; |
306 |
| - // MSB is 0 iff the byte is 0x00, 1 otherwise |
307 |
| - tmp = ~tmp & 0x8080808080808080L; |
308 |
| - // MSB is 1 iff the byte is 0x00, 0 otherwise |
309 |
| - if (tmp != 0) { |
310 |
| - // there's some 0x00 in the word |
311 |
| - int firstZero = Long.numberOfTrailingZeros(tmp) >> 3; |
312 |
| - throw new BsonSerializationException(format("BSON cstring '%s' is not valid because it contains a null character " |
313 |
| - + "at index %d", str, i + firstZero)); |
314 |
| - } |
315 |
| - } |
316 |
| - |
317 |
| - private int writeCharactersOnNettyByteBuf(String str, boolean checkForNullCharacters, ByteBuf buf) { |
| 297 | + private int writeCharactersOnInternalNioNettyByteBuf(String str, boolean checkForNullCharacters, ByteBuf buf, io.netty.buffer.ByteBuf nettyBuffer) { |
| 298 | + int len = str.length(); |
| 299 | + final ByteBuffer nioBuffer = internalNioBufferOf(nettyBuffer, len + 1); |
318 | 300 | int i = 0;
|
319 |
| - io.netty.buffer.ByteBuf nettyBuffer = ((NettyByteBuf) buf).asByteBuf(); |
320 |
| - // readonly buffers, netty buffers and off-heap NIO ByteBuffer |
321 |
| - boolean slowPath = false; |
322 |
| - int batches = str.length() / 8; |
323 |
| - final int writerIndex = nettyBuffer.writerIndex(); |
324 |
| - // this would avoid resizing the buffer while appending: ASCII length + delimiter required space |
325 |
| - nettyBuffer.ensureWritable(str.length() + 1); |
326 |
| - for (int b = 0; b < batches; b++) { |
327 |
| - i = b * 8; |
328 |
| - // read 4 chars at time to preserve the 0x0100 cases |
329 |
| - long evenChars = str.charAt(i) | |
330 |
| - str.charAt(i + 2) << 16 | |
331 |
| - (long) str.charAt(i + 4) << 32 | |
332 |
| - (long) str.charAt(i + 6) << 48; |
333 |
| - long oddChars = str.charAt(i + 1) | |
334 |
| - str.charAt(i + 3) << 16 | |
335 |
| - (long) str.charAt(i + 5) << 32 | |
336 |
| - (long) str.charAt(i + 7) << 48; |
337 |
| - // check that both the second byte and the MSB of the first byte of each pair is 0 |
338 |
| - // needed for cases like \u0100 and \u0080 |
339 |
| - long mergedChars = evenChars | oddChars; |
340 |
| - if ((mergedChars & 0xFF80FF80FF80FF80L) != 0) { |
341 |
| - if (allSingleByteChars(mergedChars)) { |
342 |
| - i = tryWriteAsciiChars(str, checkForNullCharacters, oddChars, evenChars, nettyBuffer, writerIndex, i); |
343 |
| - } |
344 |
| - slowPath = true; |
345 |
| - break; |
| 301 | + int pos = nioBuffer.position(); |
| 302 | + for (; i < len; i++) { |
| 303 | + char c = str.charAt(i); |
| 304 | + if (checkForNullCharacters && c == 0x0) { |
| 305 | + throw new BsonSerializationException(format("BSON cstring '%s' is not valid because it contains a null character " |
| 306 | + + "at index %d", str, i)); |
346 | 307 | }
|
347 |
| - // all ASCII - compose them into a single long |
348 |
| - long asciiChars = oddChars << 8 | evenChars; |
349 |
| - if (checkForNullCharacters) { |
350 |
| - validateNoNullAsciiCharacters(str, asciiChars, i); |
| 308 | + if (c >= 0x80) { |
| 309 | + break; |
351 | 310 | }
|
352 |
| - nettyBuffer.setLongLE(writerIndex + i, asciiChars); |
| 311 | + nioBuffer.put(pos + i, (byte) c); |
353 | 312 | }
|
354 |
| - if (!slowPath) { |
355 |
| - i = batches * 8; |
356 |
| - // do the rest, if any |
357 |
| - for (; i < str.length(); i++) { |
358 |
| - char c = str.charAt(i); |
359 |
| - if (checkForNullCharacters && c == 0x0) { |
360 |
| - throw new BsonSerializationException(format("BSON cstring '%s' is not valid because it contains a null character " |
361 |
| - + "at index %d", str, i)); |
362 |
| - } |
363 |
| - if (c >= 0x80) { |
364 |
| - slowPath = true; |
365 |
| - break; |
366 |
| - } |
367 |
| - nettyBuffer.setByte(writerIndex + i, c); |
368 |
| - } |
| 313 | + if (i == len) { |
| 314 | + int total = len + 1; |
| 315 | + nioBuffer.put(pos + len, (byte) 0); |
| 316 | + position += total; |
| 317 | + buf.position(buf.position() + total); |
| 318 | + return len + 1; |
369 | 319 | }
|
370 |
| - if (slowPath) { |
371 |
| - // ith char is not ASCII: |
| 320 | + // ith character is not ASCII |
| 321 | + if (i > 0) { |
372 | 322 | position += i;
|
373 |
| - buf.position(writerIndex + i); |
374 |
| - return i + super.writeCharacters(str, i, checkForNullCharacters); |
375 |
| - } else { |
376 |
| - nettyBuffer.setByte(writerIndex + str.length(), 0); |
377 |
| - int totalWritten = str.length() + 1; |
378 |
| - position += totalWritten; |
379 |
| - buf.position(writerIndex + totalWritten); |
380 |
| - return totalWritten; |
| 323 | + buf.position(buf.position() + i); |
381 | 324 | }
|
| 325 | + return i + super.writeCharacters(str, i, checkForNullCharacters); |
382 | 326 | }
|
383 | 327 |
|
384 |
| - private static boolean allSingleByteChars(long fourChars) { |
385 |
| - return (fourChars & 0xFF00FF00FF00FF00L) == 0; |
386 |
| - } |
387 |
| - |
388 |
| - private static int tryWriteAsciiChars(String str, boolean checkForNullCharacters, |
389 |
| - long oddChars, long evenChars, io.netty.buffer.ByteBuf nettyByteBuf, int writerIndex, int i) { |
390 |
| - // all single byte chars |
391 |
| - long latinChars = oddChars << 8 | evenChars; |
392 |
| - if (checkForNullCharacters) { |
393 |
| - validateNoNullSingleByteChars(str, latinChars, i); |
| 328 | + private static ByteBuffer internalNioBufferOf(io.netty.buffer.ByteBuf buf, int minCapacity) { |
| 329 | + io.netty.buffer.ByteBuf unwrap; |
| 330 | + while ((unwrap = buf.unwrap()) != null) { |
| 331 | + buf = unwrap; |
394 | 332 | }
|
395 |
| - long msbSetForNonAscii = latinChars & 0x8080808080808080L; |
396 |
| - int firstNonAsciiOffset = Long.numberOfTrailingZeros(msbSetForNonAscii) >> 3; |
397 |
| - // that's a bit cheating :P but later phases will patch the wrongly encoded ones |
398 |
| - nettyByteBuf.setLongLE(writerIndex + i, latinChars); |
399 |
| - i += firstNonAsciiOffset; |
400 |
| - return i; |
| 333 | + assert buf.unwrap() == null; |
| 334 | + buf.ensureWritable(minCapacity); |
| 335 | + return buf.internalNioBuffer(buf.writerIndex(), buf.writableBytes()); |
401 | 336 | }
|
402 | 337 |
|
403 | 338 | private int writeCharactersOnArray(String str, boolean checkForNullCharacters, ByteBuf buf) {
|
|
0 commit comments