|
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