@Override public void write(Buffer source, long byteCount) throws IOException { if (!channel.isOpen()) throw new IllegalStateException("closed"); if (byteCount == 0) return; long remaining = byteCount; while (remaining > 0) { timeout.throwIfReached(); try (Buffer.UnsafeCursor ignored = source.readUnsafe(cursor)) { cursor.seek(0); int length = (int) Math.min(cursor.end - cursor.start, remaining); int written = channel.write(ByteBuffer.wrap(cursor.data, cursor.start, length)); remaining -= written; source.skip(written); } } }
/** * Reads a message body into across one or more frames. Control frames that occur between * fragments will be processed. If the message payload is masked this will unmask as it's being * processed. */ private void readMessage() throws IOException { while (true) { if (closed) throw new IOException("closed"); if (frameLength > 0) { source.readFully(messageFrameBuffer, frameLength); if (!isClient) { messageFrameBuffer.readAndWriteUnsafe(maskCursor); maskCursor.seek(messageFrameBuffer.size() - frameLength); toggleMask(maskCursor, maskKey); maskCursor.close(); } } if (isFinalFrame) break; // We are exhausted and have no continuations. readUntilNonControlFrame(); if (opcode != OPCODE_CONTINUATION) { throw new ProtocolException("Expected continuation opcode. Got: " + toHexString(opcode)); } } } }
@Override public void write(Buffer source, long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (source.size() < byteCount) { throw new IllegalArgumentException("size=" + source.size() + " byteCount=" + byteCount); } if (byteCount == 0) return; source.readUnsafe(cursor); try { long remaining = byteCount; for (int length = cursor.seek(0); remaining > 0 && length > 0; length = cursor.next()) { int toIntercept = (int) Math.min(length, remaining); intercept(cursor.data, cursor.start, toIntercept); remaining -= toIntercept; } } finally { cursor.close(); } super.write(source, byteCount); }
/** * Reads a message body into across one or more frames. Control frames that occur between * fragments will be processed. If the message payload is masked this will unmask as it's being * processed. */ private void readMessage() throws IOException { while (true) { if (closed) throw new IOException("closed"); if (frameLength > 0) { source.readFully(messageFrameBuffer, frameLength); if (!isClient) { messageFrameBuffer.readAndWriteUnsafe(maskCursor); maskCursor.seek(messageFrameBuffer.size() - frameLength); toggleMask(maskCursor, maskKey); maskCursor.close(); } } if (isFinalFrame) break; // We are exhausted and have no continuations. readUntilNonControlFrame(); if (opcode != OPCODE_CONTINUATION) { throw new ProtocolException("Expected continuation opcode. Got: " + toHexString(opcode)); } } } }
@Override public long read(Buffer sink, long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount == 0) return 0; long result = super.read(sink, byteCount); if (result == -1L) return result; sink.readUnsafe(cursor); try { long remaining = result; for (int length = cursor.seek(sink.size() - result); remaining > 0 && length > 0; length = cursor.next()) { int toIntercept = (int) Math.min(length, remaining); intercept(cursor.data, cursor.start, toIntercept); remaining -= toIntercept; } } finally { cursor.close(); } return result; }
@Test public void resizeEnlargeMovesCursorToOldSize() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); Buffer expected = deepCopy(buffer); expected.writeUtf8("a"); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() / 2); assertEquals(originalSize, buffer.size()); cursor.resizeBuffer(originalSize + 1); assertEquals(originalSize, cursor.offset); assertNotNull(cursor.data); assertNotEquals(-1, cursor.start); assertEquals(cursor.start + 1, cursor.end); cursor.data[cursor.start] = 'a'; } assertEquals(expected, buffer); }
@Test public void expandSameSegment() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); assumeTrue(originalSize > 0); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(originalSize - 1); int originalEnd = cursor.end; assumeTrue(originalEnd < SEGMENT_SIZE); long addedByteCount = cursor.expandBuffer(1); assertEquals(SEGMENT_SIZE - originalEnd, addedByteCount); assertEquals(originalSize + addedByteCount, buffer.size()); assertEquals(originalSize, cursor.offset); assertEquals(originalEnd, cursor.start); assertEquals(SEGMENT_SIZE, cursor.end); } }
@Test public void enlarge() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); Buffer expected = deepCopy(buffer); expected.writeUtf8("abc"); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { assertEquals(originalSize, cursor.resizeBuffer(originalSize + 3)); cursor.seek(originalSize); cursor.data[cursor.start] = 'a'; cursor.seek(originalSize + 1); cursor.data[cursor.start] = 'b'; cursor.seek(originalSize + 2); cursor.data[cursor.start] = 'c'; } assertEquals(expected, buffer); }
maskCursor.seek(bufferStart); toggleMask(maskCursor, maskKey); maskCursor.close();
maskCursor.seek(0); toggleMask(maskCursor, maskKey); maskCursor.close();
@Test public void seekWithinSegment() throws Exception { assumeTrue(bufferFactory == BufferFactory.SMALL_SEGMENTED_BUFFER); Buffer buffer = bufferFactory.newBuffer(); assertEquals("abcdefghijkl", buffer.clone().readUtf8()); // Seek to the 'f' in the "defg" segment. try (UnsafeCursor cursor = buffer.readUnsafe()) { assertEquals(2, cursor.seek(5)); // 2 for 2 bytes left in the segment: "fg". assertEquals(5, cursor.offset); assertEquals(2, cursor.end - cursor.start); assertEquals('d', (char) cursor.data[cursor.start - 2]); // Out of bounds! assertEquals('e', (char) cursor.data[cursor.start - 1]); // Out of bounds! assertEquals('f', (char) cursor.data[cursor.start]); assertEquals('g', (char) cursor.data[cursor.start + 1]); } }
@Test public void resizeShrinkMovesCursorToEnd() throws Exception { Buffer buffer = bufferFactory.newBuffer(); assumeTrue(buffer.size() > 0); long originalSize = buffer.size(); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() / 2); assertEquals(originalSize, buffer.size()); cursor.resizeBuffer(originalSize - 1); assertEquals(originalSize - 1, cursor.offset); assertNull(cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); } }
@Test public void resizeToSameSizeSeeksToEnd() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() / 2); assertEquals(originalSize, buffer.size()); cursor.resizeBuffer(originalSize); assertEquals(originalSize, buffer.size()); assertEquals(originalSize, cursor.offset); assertNull(cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); } }
@Test public void expandMovesOffsetToOldSize() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() / 2); assertEquals(originalSize, buffer.size()); long addedByteCount = cursor.expandBuffer(5); assertEquals(originalSize + addedByteCount, buffer.size()); assertEquals(originalSize, cursor.offset); } } }
@Test public void accessByteByByteReverse() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { byte[] actual = new byte[(int) buffer.size()]; for (int i = (int) (buffer.size() - 1); i >= 0; i--) { cursor.seek(i); actual[i] = cursor.data[cursor.start]; } assertEquals(ByteString.of(actual), buffer.snapshot()); } }
private void writeControlFrame(int opcode, ByteString payload) throws IOException { if (writerClosed) throw new IOException("closed"); int length = payload.size(); if (length > PAYLOAD_BYTE_MAX) { throw new IllegalArgumentException( "Payload size must be less than or equal to " + PAYLOAD_BYTE_MAX); } int b0 = B0_FLAG_FIN | opcode; sinkBuffer.writeByte(b0); int b1 = length; if (isClient) { b1 |= B1_FLAG_MASK; sinkBuffer.writeByte(b1); random.nextBytes(maskKey); sinkBuffer.write(maskKey); if (length > 0) { long payloadStart = sinkBuffer.size(); sinkBuffer.write(payload); sinkBuffer.readAndWriteUnsafe(maskCursor); maskCursor.seek(payloadStart); toggleMask(maskCursor, maskKey); maskCursor.close(); } } else { sinkBuffer.writeByte(b1); sinkBuffer.write(payload); } sink.flush(); }
@Test public void accessByteByByteAlwaysResettingToZero() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { byte[] actual = new byte[(int) buffer.size()]; for (int i = 0; i < buffer.size(); i++) { cursor.seek(i); actual[i] = cursor.data[cursor.start]; cursor.seek(0L); } assertEquals(ByteString.of(actual), buffer.snapshot()); } }
@Test public void seekToNegativeOneSeeksBeforeFirstSegment() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { cursor.seek(-1L); assertEquals(-1, cursor.offset); assertEquals(null, cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); cursor.next(); assertEquals(0, cursor.offset); } }
@Test public void shrinkAdjustOffset() throws Exception { Buffer buffer = bufferFactory.newBuffer(); assumeTrue(buffer.size() > 4); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() - 1); cursor.resizeBuffer(3); assertEquals(3, cursor.offset); assertEquals(null, cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); } }
@Test public void accessByteByByte() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { byte[] actual = new byte[(int) buffer.size()]; for (int i = 0; i < buffer.size(); i++) { cursor.seek(i); actual[i] = cursor.data[cursor.start]; } assertEquals(ByteString.of(actual), buffer.snapshot()); } }