From 6a386222a645657cd655d3915735032375c8b1f7 Mon Sep 17 00:00:00 2001 From: Ryland Degnan Date: Wed, 29 Mar 2017 02:13:57 -0700 Subject: [PATCH 1/3] Use ByteBuf instead of DirectBuffer --- build.gradle | 1 + .../client/FailureReactiveSocketTest.java | 20 +- .../client/LoadBalancerTest.java | 16 +- .../client/TimeoutClientTest.java | 14 +- reactivesocket-core/build.gradle | 4 +- .../io/reactivesocket/ReactiveSocketPerf.java | 34 +- .../perfutil/TestDuplexConnection.java | 9 +- .../reactivesocket/ClientReactiveSocket.java | 35 +- .../main/java/io/reactivesocket/Frame.java | 467 ++++++----- .../reactivesocket/ServerReactiveSocket.java | 187 +++-- .../client/SetupProviderImpl.java | 19 +- .../reactivesocket/exceptions/Exceptions.java | 20 +- .../reactivesocket/frame/ByteBufferUtil.java | 53 -- .../frame/ErrorFrameFlyweight.java | 34 +- .../frame/FrameHeaderFlyweight.java | 192 ++--- .../io/reactivesocket/frame/FramePool.java | 38 - .../frame/KeepaliveFrameFlyweight.java | 24 +- .../frame/LeaseFrameFlyweight.java | 35 +- .../reactivesocket/frame/PayloadBuilder.java | 111 +-- .../frame/PayloadFragmenter.java | 26 +- .../frame/PayloadReassembler.java | 11 +- .../frame/RequestFrameFlyweight.java | 48 +- .../frame/RequestNFrameFlyweight.java | 22 +- .../frame/SetupFrameFlyweight.java | 107 ++- .../frame/ThreadLocalFramePool.java | 95 --- .../frame/ThreadSafeFramePool.java | 107 --- .../reactivesocket/frame/UnpooledFrame.java | 55 -- .../ClientServerInputMultiplexer.java | 2 +- .../internal/CollectionUtil.java | 133 +++ .../io/reactivesocket/internal/Hashing.java | 118 +++ .../internal/Int2ObjectHashMap.java | 768 ++++++++++++++++++ .../internal/LimitableRequestPublisher.java | 5 +- .../lease/FairLeaseDistributor.java | 6 +- .../java/io/reactivesocket/lease/Lease.java | 2 +- .../io/reactivesocket/lease/LeaseImpl.java | 6 +- .../server/DefaultReactiveSocketServer.java | 1 + .../java/io/reactivesocket/util/BitUtil.java | 335 ++++++++ .../io/reactivesocket/util/PayloadImpl.java | 47 +- .../java/io/reactivesocket/FrameTest.java | 497 ------------ .../ServerReactiveSocketTest.java | 3 +- .../test/java/io/reactivesocket/TestUtil.java | 87 +- .../client/SetupProviderImplTest.java | 6 +- .../frame/FrameHeaderFlyweightTest.java | 87 +- .../frame/KeepaliveFrameFlyweightTest.java | 17 +- .../frame/LeaseFrameFlyweightTest.java | 11 +- .../frame/SetupFrameFlyweightTest.java | 63 +- .../internal/ReassemblerTest.java | 49 +- .../lease/DefaultLeaseHonoringSocketTest.java | 3 +- .../reactivesocket/util/PayloadImplTest.java | 24 +- reactivesocket-examples/build.gradle | 16 +- .../tcp/channel/ChannelEchoClient.java | 8 +- .../transport/tcp/duplex/DuplexClient.java | 5 +- .../tcp/requestresponse/HelloWorldClient.java | 5 +- .../transport/tcp/stream/StreamingClient.java | 5 +- .../tckdrivers/common/MySubscriber.java | 18 +- .../tckdrivers/server/JavaServerDriver.java | 14 +- .../reactivesocket/test/ClientSetupRule.java | 16 +- .../io/reactivesocket/test/PingClient.java | 14 +- .../io/reactivesocket/test/PingHandler.java | 13 +- .../test/TestReactiveSocket.java | 8 +- .../java/io/reactivesocket/test/TestUtil.java | 131 --- .../aeron/AeronDuplexConnection.java | 5 +- .../local/LocalDuplexConnection.java | 5 +- .../local/LocalClientServerTest.java | 18 +- .../transport/local/LocalPingPong.java | 47 ++ .../netty/NettyDuplexConnection.java | 18 +- .../netty/WebsocketDuplexConnection.java | 18 +- .../transport/netty/TcpPongServer.java | 4 +- .../netty/WebsocketClientServerTest.java | 12 +- 69 files changed, 2314 insertions(+), 2120 deletions(-) delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/frame/ByteBufferUtil.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/frame/FramePool.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/frame/ThreadLocalFramePool.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/frame/ThreadSafeFramePool.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/frame/UnpooledFrame.java create mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/CollectionUtil.java create mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/Hashing.java create mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/Int2ObjectHashMap.java create mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/util/BitUtil.java delete mode 100644 reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java delete mode 100644 reactivesocket-test/src/main/java/io/reactivesocket/test/TestUtil.java create mode 100644 reactivesocket-transport-local/src/test/java/io/reactivesocket/transport/local/LocalPingPong.java diff --git a/build.gradle b/build.gradle index b863fb150..6b77663c5 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,7 @@ subprojects { repositories { jcenter() maven { url 'https://oss.jfrog.org/libs-snapshot' } + maven { url 'http://repo.spring.io/snapshot' } maven { url 'https://dl.bintray.com/reactivesocket/ReactiveSocket' } } diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java index 178582bbb..6f86d462d 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/FailureReactiveSocketTest.java @@ -18,6 +18,7 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.filter.FailureAwareClient; +import io.reactivesocket.util.PayloadImpl; import io.reactivex.subscribers.TestSubscriber; import org.junit.Test; import org.reactivestreams.Publisher; @@ -25,7 +26,6 @@ import org.reactivestreams.Subscription; import reactor.core.publisher.Mono; -import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -35,23 +35,11 @@ public class FailureReactiveSocketTest { - private final Payload dummyPayload = new Payload() { - @Override - public ByteBuffer getData() { - return null; - } - - @Override - public ByteBuffer getMetadata() { - return null; - } - }; - @Test public void testError() throws InterruptedException { testReactiveSocket((latch, socket) -> { assertEquals(1.0, socket.availability(), 0.0); - Publisher payloadPublisher = socket.requestResponse(dummyPayload); + Publisher payloadPublisher = socket.requestResponse(PayloadImpl.EMPTY); TestSubscriber subscriber = new TestSubscriber<>(); payloadPublisher.subscribe(subscriber); @@ -79,7 +67,7 @@ public void testError() throws InterruptedException { public void testWidowReset() throws InterruptedException { testReactiveSocket((latch, socket) -> { assertEquals(1.0, socket.availability(), 0.0); - Publisher payloadPublisher = socket.requestResponse(dummyPayload); + Publisher payloadPublisher = socket.requestResponse(PayloadImpl.EMPTY); TestSubscriber subscriber = new TestSubscriber<>(); payloadPublisher.subscribe(subscriber); @@ -110,7 +98,7 @@ private void testReactiveSocket(BiConsumer f) th AtomicInteger count = new AtomicInteger(0); TestingReactiveSocket socket = new TestingReactiveSocket(input -> { if (count.getAndIncrement() < 1) { - return dummyPayload; + return PayloadImpl.EMPTY; } else { throw new RuntimeException(); } diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java index 318f89496..5393da502 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/LoadBalancerTest.java @@ -18,6 +18,7 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.util.PayloadImpl; import org.junit.Assert; import org.junit.Test; import org.reactivestreams.Publisher; @@ -27,7 +28,6 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -35,18 +35,6 @@ public class LoadBalancerTest { - private Payload dummy = new Payload() { - @Override - public ByteBuffer getData() { - return null; - } - - @Override - public ByteBuffer getMetadata() { - return null; - } - }; - @Test(timeout = 10_000L) public void testNeverSelectFailingFactories() throws InterruptedException { InetSocketAddress local0 = InetSocketAddress.createUnresolved("localhost", 7000); @@ -105,7 +93,7 @@ private void testBalancer(List factories) throws Interrupt private void makeAcall(ReactiveSocket balancer) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); - balancer.requestResponse(dummy).subscribe(new Subscriber() { + balancer.requestResponse(PayloadImpl.EMPTY).subscribe(new Subscriber() { @Override public void onSubscribe(Subscription s) { s.request(1L); diff --git a/reactivesocket-client/src/test/java/io/reactivesocket/client/TimeoutClientTest.java b/reactivesocket-client/src/test/java/io/reactivesocket/client/TimeoutClientTest.java index 75095a23b..ce6edb07a 100644 --- a/reactivesocket-client/src/test/java/io/reactivesocket/client/TimeoutClientTest.java +++ b/reactivesocket-client/src/test/java/io/reactivesocket/client/TimeoutClientTest.java @@ -20,12 +20,12 @@ import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.filter.ReactiveSockets; import io.reactivesocket.exceptions.TimeoutException; +import io.reactivesocket.util.PayloadImpl; import org.hamcrest.MatcherAssert; import org.junit.Test; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; -import java.nio.ByteBuffer; import java.time.Duration; import static org.hamcrest.Matchers.instanceOf; @@ -36,17 +36,7 @@ public void testTimeoutSocket() { TestingReactiveSocket socket = new TestingReactiveSocket((subscriber, payload) -> {return false;}); ReactiveSocket timeout = ReactiveSockets.timeout(Duration.ofMillis(50)).apply(socket); - timeout.requestResponse(new Payload() { - @Override - public ByteBuffer getData() { - return null; - } - - @Override - public ByteBuffer getMetadata() { - return null; - } - }).subscribe(new Subscriber() { + timeout.requestResponse(PayloadImpl.EMPTY).subscribe(new Subscriber() { @Override public void onSubscribe(Subscription s) { s.request(1); diff --git a/reactivesocket-core/build.gradle b/reactivesocket-core/build.gradle index e5dbb17b6..1c9425c63 100644 --- a/reactivesocket-core/build.gradle +++ b/reactivesocket-core/build.gradle @@ -29,8 +29,8 @@ jmh { } dependencies { - compile 'io.projectreactor:reactor-core:3.0.5.RELEASE' - compile 'org.agrona:Agrona:0.5.4' + compile 'io.projectreactor:reactor-core:3.0.6.BUILD-20170323.100806-6' + compile 'io.netty:netty-buffer:4.1.8.Final' jmh 'org.openjdk.jmh:jmh-core:1.15' jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.15' diff --git a/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java b/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java index 71ab2d50a..6d8ecbc1b 100644 --- a/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java +++ b/reactivesocket-core/src/jmh/java/io/reactivesocket/ReactiveSocketPerf.java @@ -16,6 +16,8 @@ package io.reactivesocket; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.reactivesocket.client.KeepAliveProvider; import io.reactivesocket.client.ReactiveSocketClient; import io.reactivesocket.client.SetupProvider; @@ -24,6 +26,7 @@ import io.reactivesocket.perfutil.TestDuplexConnection; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.transport.TransportServer; +import io.reactivesocket.util.PayloadImpl; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -88,37 +91,8 @@ public static class Input { public Blackhole bh; static final ByteBuffer HELLO = ByteBuffer.wrap("HELLO".getBytes(StandardCharsets.UTF_8)); - static final ByteBuffer HELLO_WORLD = ByteBuffer.wrap("HELLO_WORLD".getBytes(StandardCharsets.UTF_8)); - static final ByteBuffer EMPTY = ByteBuffer.allocate(0); - - static final Payload HELLO_PAYLOAD = new Payload() { - - @Override - public ByteBuffer getMetadata() { - return EMPTY; - } - - @Override - public ByteBuffer getData() { - HELLO.position(0); - return HELLO; - } - }; - - static final Payload HELLO_WORLD_PAYLOAD = new Payload() { - - @Override - public ByteBuffer getMetadata() { - return EMPTY; - } - - @Override - public ByteBuffer getData() { - HELLO_WORLD.position(0); - return HELLO_WORLD; - } - }; + static final Payload HELLO_PAYLOAD = new PayloadImpl(HELLO); static final DirectProcessor clientReceive = DirectProcessor.create(); static final DirectProcessor serverReceive = DirectProcessor.create(); diff --git a/reactivesocket-core/src/jmh/java/io/reactivesocket/perfutil/TestDuplexConnection.java b/reactivesocket-core/src/jmh/java/io/reactivesocket/perfutil/TestDuplexConnection.java index ab29eee50..47895207d 100644 --- a/reactivesocket-core/src/jmh/java/io/reactivesocket/perfutil/TestDuplexConnection.java +++ b/reactivesocket-core/src/jmh/java/io/reactivesocket/perfutil/TestDuplexConnection.java @@ -40,8 +40,13 @@ public TestDuplexConnection(DirectProcessor send, DirectProcessor public Mono send(Publisher frame) { return Flux .from(frame) - .doOnNext(f -> send.onNext(f)) - .doOnError(t -> {throw new RuntimeException(t); }) + .doOnNext(f -> { + try { + send.onNext(f); + } finally { + f.release(); + } + }) .then(); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java index 2dbb9c845..e2b1a41b6 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java @@ -16,14 +16,15 @@ package io.reactivesocket; +import io.netty.buffer.Unpooled; import io.reactivesocket.client.KeepAliveProvider; +import io.reactivesocket.internal.Int2ObjectHashMap; import io.reactivesocket.exceptions.Exceptions; -import io.reactivesocket.frame.ByteBufferUtil; import io.reactivesocket.internal.KnownErrorFilter; import io.reactivesocket.internal.LimitableRequestPublisher; import io.reactivesocket.lease.Lease; import io.reactivesocket.lease.LeaseImpl; -import org.agrona.collections.Int2ObjectHashMap; +import io.reactivesocket.util.PayloadImpl; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import reactor.core.Disposable; @@ -33,6 +34,7 @@ import reactor.core.publisher.UnicastProcessor; import java.nio.channels.ClosedChannelException; +import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -51,7 +53,7 @@ public class ClientReactiveSocket implements ReactiveSocket { private final KeepAliveProvider keepAliveProvider; private final MonoProcessor started; private final Int2ObjectHashMap senders; - private final Int2ObjectHashMap> receivers; + private final Int2ObjectHashMap> receivers; private Disposable keepAliveSendSub; private volatile Consumer leaseConsumer; // Provided on start() @@ -124,7 +126,7 @@ public ClientReactiveSocket start(Consumer leaseConsumer) { this.leaseConsumer = leaseConsumer; keepAliveSendSub = connection.send(keepAliveProvider.ticks() - .map(i -> Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, true))) + .map(i -> Frame.Keepalive.from(Unpooled.EMPTY_BUFFER, true))) .subscribe(null, errorConsumer); connection @@ -308,12 +310,16 @@ private synchronized void cleanUpSubscriber(Subscriber subscriber) { } private void handleIncomingFrames(Frame frame) { - int streamId = frame.getStreamId(); - FrameType type = frame.getType(); - if (streamId == 0) { - handleStreamZero(type, frame); - } else { - handleFrame(streamId, type, frame); + try { + int streamId = frame.getStreamId(); + FrameType type = frame.getType(); + if (streamId == 0) { + handleStreamZero(type, frame); + } else { + handleFrame(streamId, type, frame); + } + } finally { + frame.release(); } } @@ -323,6 +329,7 @@ private void handleStreamZero(FrameType type, Frame frame) { throw Exceptions.from(frame); case LEASE: { if (leaseConsumer != null) { + leaseConsumer.accept(new LeaseImpl(frame)); } break; @@ -342,7 +349,7 @@ private void handleStreamZero(FrameType type, Frame frame) { @SuppressWarnings("unchecked") private void handleFrame(int streamId, FrameType type, Frame frame) { - Subscriber receiver; + Subscriber receiver; synchronized (this) { receiver = receivers.get(streamId); } @@ -355,7 +362,7 @@ private void handleFrame(int streamId, FrameType type, Frame frame) { removeReceiver(streamId); break; case NEXT_COMPLETE: - receiver.onNext(frame); + receiver.onNext(new PayloadImpl(frame)); receiver.onComplete(); break; case CANCEL: { @@ -370,7 +377,7 @@ private void handleFrame(int streamId, FrameType type, Frame frame) { break; } case NEXT: - receiver.onNext(frame); + receiver.onNext(new PayloadImpl(frame)); break; case REQUEST_N: { LimitableRequestPublisher sender; @@ -401,7 +408,7 @@ private void handleMissingResponseProcessor(int streamId, FrameType type, Frame if (type == FrameType.ERROR) { // message for stream that has never existed, we have a problem with // the overall connection and must tear down - String errorMessage = ByteBufferUtil.toUtf8String(frame.getData()); + String errorMessage = StandardCharsets.UTF_8.decode(frame.getData()).toString(); throw new IllegalStateException("Client received error for non-existent stream: " + streamId + " Message: " + errorMessage); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java index ce231d750..7242717ab 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/Frame.java @@ -15,234 +15,267 @@ */ package io.reactivesocket; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.ByteBufHolder; +import io.netty.buffer.Unpooled; +import io.netty.util.IllegalReferenceCountException; +import io.netty.util.Recycler; +import io.netty.util.Recycler.Handle; +import io.netty.util.ResourceLeakDetector; import io.reactivesocket.frame.ErrorFrameFlyweight; import io.reactivesocket.frame.FrameHeaderFlyweight; -import io.reactivesocket.frame.FramePool; import io.reactivesocket.frame.KeepaliveFrameFlyweight; import io.reactivesocket.frame.LeaseFrameFlyweight; import io.reactivesocket.frame.RequestFrameFlyweight; import io.reactivesocket.frame.RequestNFrameFlyweight; import io.reactivesocket.frame.SetupFrameFlyweight; -import io.reactivesocket.frame.UnpooledFrame; import io.reactivesocket.frame.VersionFlyweight; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import static java.lang.System.getProperty; - /** * Represents a Frame sent over a {@link DuplexConnection}. *

* This provides encoding, decoding and field accessors. */ -public class Frame implements Payload { +public class Frame implements ByteBufHolder { private static final Logger logger = LoggerFactory.getLogger(Frame.class); - public static final ByteBuffer NULL_BYTEBUFFER = FrameHeaderFlyweight.NULL_BYTEBUFFER; - public static final int DATA_MTU = 32 * 1024; + public static final ByteBuffer NULL_BYTEBUFFER = ByteBuffer.allocateDirect(0); public static final int METADATA_MTU = 32 * 1024; + public static final int DATA_MTU = 32 * 1024; - /* - * ThreadLocal handling in the pool itself. We don't have a per thread pool at this level. - */ - private static final String FRAME_POOLER_CLASS_NAME = - getProperty("io.reactivesocket.FramePool", UnpooledFrame.class.getName()); - //getProperty("io.reactivesocket.FramePool", ThreadLocalFramePool.class.getName()); - protected static final FramePool POOL; - - static { - FramePool tmpPool; - - try { - logger.info("Creating thread pooled named " + FRAME_POOLER_CLASS_NAME); - tmpPool = (FramePool)Class.forName(FRAME_POOLER_CLASS_NAME).newInstance(); - } - catch (final Exception ex) { - logger.error("Error initializing frame pool.", ex); - tmpPool = new UnpooledFrame(); + private static final Recycler RECYCLER = new Recycler() { + protected Frame newObject(Handle handle) { + return new Frame(handle); } + }; + + private final Handle handle; + private ByteBuf content; - POOL = tmpPool; + private Frame(final Handle handle) { + this.handle = handle; } - // not final so we can reuse this object - protected MutableDirectBuffer directBuffer; - protected int offset = 0; - protected int length = 0; + /** + * Clear and recycle this instance. + */ + private void recycle() { + content = null; + handle.recycle(this); + } - protected Frame(final MutableDirectBuffer directBuffer) { - this.directBuffer = directBuffer; + /** + * Return the content which is held by this {@link Frame}. + */ + @Override + public ByteBuf content() { + if (content.refCnt() <= 0) { + throw new IllegalReferenceCountException(content.refCnt()); + } + return content; } /** - * Return underlying {@link ByteBuffer} for frame - * - * @return underlying {@link ByteBuffer} for frame + * Creates a deep copy of this {@link Frame}. */ - public ByteBuffer getByteBuffer() { - return directBuffer.byteBuffer(); + @Override + public Frame copy() { + return replace(content.copy()); } /** - * Return {@link ByteBuffer} that is a {@link ByteBuffer#slice()} for the frame data - * - * If no data is present, the ByteBuffer will have 0 capacity. - * - * @return ByteBuffer containing the data + * Duplicates this {@link Frame}. Be aware that this will not automatically call {@link #retain()}. */ - public ByteBuffer getData() { - return FrameHeaderFlyweight.sliceFrameData(directBuffer, offset, 0); + @Override + public Frame duplicate() { + return replace(content.duplicate()); } /** - * Return {@link ByteBuffer} that is a {@link ByteBuffer#slice()} for the frame metadata - * - * If no metadata is present, the ByteBuffer will have 0 capacity. + * Duplicates this {@link Frame}. This method returns a retained duplicate unlike {@link #duplicate()}. * - * @return ByteBuffer containing the data + * @see ByteBuf#retainedDuplicate() */ - public ByteBuffer getMetadata() { - return FrameHeaderFlyweight.sliceFrameMetadata(directBuffer, offset, 0); + @Override + public Frame retainedDuplicate() { + return replace(content.retainedDuplicate()); } /** - * Return frame stream identifier - * - * @return frame stream identifier + * Returns a new {@link Frame} which contains the specified {@code content}. */ - public int getStreamId() { - return FrameHeaderFlyweight.streamId(directBuffer, offset); + @Override + public Frame replace(ByteBuf content) { + return from(content); } /** - * Return frame {@link FrameType} - * - * @return frame type + * Returns the reference count of this object. If {@code 0}, it means this object has been deallocated. */ - public FrameType getType() { - return FrameHeaderFlyweight.frameType(directBuffer, offset); + @Override + public int refCnt() { + return content.refCnt(); } /** - * Return the offset in the buffer of the frame - * - * @return offset of frame within the buffer + * Increases the reference count by {@code 1}. */ - public int offset() { - return offset; + @Override + public Frame retain() { + content.retain(); + return this; } /** - * Return the encoded length of a frame or the frame length - * - * @return frame length + * Increases the reference count by the specified {@code increment}. */ - public int length() { - return length; + @Override + public Frame retain(int increment) { + content.retain(increment); + return this; } /** - * Return the flags field for the frame - * - * @return frame flags field value + * Records the current access location of this object for debugging purposes. + * If this object is determined to be leaked, the information recorded by this operation will be provided to you + * via {@link ResourceLeakDetector}. This method is a shortcut to {@link #touch(Object) touch(null)}. */ - public int flags() { - return FrameHeaderFlyweight.flags(directBuffer, offset); + @Override + public Frame touch() { + content.touch(); + return this; } /** - * Mutates this Frame to contain the given ByteBuffer - * - * @param byteBuffer to wrap + * Records the current access location of this object with an additonal arbitrary information for debugging + * purposes. If this object is determined to be leaked, the information recorded by this operation will be + * provided to you via {@link ResourceLeakDetector}. */ - public void wrap(final ByteBuffer byteBuffer, final int offset) { - wrap(POOL.acquireMutableDirectBuffer(byteBuffer), offset); + @Override + public Frame touch(Object hint) { + content.touch(hint); + return this; } /** - * Mutates this Frame to contain the given MutableDirectBuffer + * Decreases the reference count by {@code 1} and deallocates this object if the reference count reaches at + * {@code 0}. * - * @param directBuffer to wrap + * @return {@code true} if and only if the reference count became {@code 0} and this object has been deallocated */ - public void wrap(final MutableDirectBuffer directBuffer, final int offset) { - this.directBuffer = directBuffer; - this.offset = offset; + @Override + public boolean release() { + if (content.release()) { + recycle(); + return true; + } + return false; } /** - * Acquire a free Frame backed by given ByteBuffer - * - * @param byteBuffer to wrap - * @return new {@link Frame} + * Decreases the reference count by the specified {@code decrement} and deallocates this object if the reference + * count reaches at {@code 0}. + * + * @return {@code true} if and only if the reference count became {@code 0} and this object has been deallocated */ - public static Frame from(final ByteBuffer byteBuffer) { - return POOL.acquireFrame(byteBuffer); + @Override + public boolean release(int decrement) { + if (content.release(decrement)) { + recycle(); + return true; + } + return false; } /** - * Acquire a free Frame and back with the given {@link DirectBuffer} starting at offset for length bytes + * Return {@link ByteBuffer} that is a {@link ByteBuffer#slice()} for the frame metadata * - * @param directBuffer to use as backing buffer - * @param offset of start of frame - * @param length of frame in bytes - * @return frame + * If no metadata is present, the ByteBuffer will have 0 capacity. + * + * @return ByteBuffer containing the content */ - public static Frame from(final DirectBuffer directBuffer, final int offset, final int length) { - final Frame frame = POOL.acquireFrame((MutableDirectBuffer)directBuffer); - frame.offset = offset; - frame.length = length; - - return frame; + public ByteBuffer getMetadata() { + final ByteBuf metadata = FrameHeaderFlyweight.sliceFrameMetadata(content); + if (metadata.readableBytes() > 0) { + final ByteBuffer buffer = ByteBuffer.allocateDirect(metadata.readableBytes()); + metadata.readBytes(buffer); + buffer.flip(); + return buffer; + } else { + return NULL_BYTEBUFFER; + } } /** - * Construct a new Frame from the given {@link MutableDirectBuffer} + * Return {@link ByteBuffer} that is a {@link ByteBuffer#slice()} for the frame data * - * NOTE: always allocates. Used for pooling. + * If no data is present, the ByteBuffer will have 0 capacity. * - * @param directBuffer to wrap - * @return new {@link Frame} + * @return ByteBuffer containing the data */ - public static Frame allocate(final MutableDirectBuffer directBuffer) { - return new Frame(directBuffer); + public ByteBuffer getData() { + final ByteBuf data = FrameHeaderFlyweight.sliceFrameData(content); + if (data.readableBytes() > 0) { + final ByteBuffer buffer = ByteBuffer.allocateDirect(data.readableBytes()); + data.readBytes(buffer); + buffer.flip(); + return buffer; + } else { + return NULL_BYTEBUFFER; + } } /** - * Release frame for re-use. + * Return frame stream identifier + * + * @return frame stream identifier */ - public void release() { - POOL.release(this.directBuffer); - POOL.release(this); + public int getStreamId() { + return FrameHeaderFlyweight.streamId(content); } /** - * Mutates this Frame to contain the given parameters. + * Return frame {@link FrameType} * - * NOTE: acquires a new backing buffer and releases current backing buffer + * @return frame type + */ + public FrameType getType() { + return FrameHeaderFlyweight.frameType(content); + } + + /** + * Return the flags field for the frame * - * @param streamId to include in frame - * @param type to include in frame - * @param data to include in frame + * @return frame flags field value */ - public void wrap(final int streamId, final FrameType type, final ByteBuffer data) { - POOL.release(this.directBuffer); + public int flags() { + return FrameHeaderFlyweight.flags(content); + } - this.directBuffer = - POOL.acquireMutableDirectBuffer(FrameHeaderFlyweight.computeFrameHeaderLength(type, 0, data.remaining())); + /** + * Acquire a free Frame backed by given ByteBuf + * + * @param content to use as backing buffer + * @return frame + */ + public static Frame from(final ByteBuf content) { + final Frame frame = RECYCLER.get(); + frame.content = content; - this.length = FrameHeaderFlyweight.encode(this.directBuffer, offset, streamId, 0, type, NULL_BYTEBUFFER, data); + return frame; } /* TODO: * * fromRequest(type, id, payload) - * fromKeepalive(ByteBuffer data) + * fromKeepalive(ByteBuf content) * */ @@ -259,47 +292,47 @@ public static Frame from( String dataMimeType, Payload payload) { - final ByteBuffer metadata = payload.getMetadata(); - final ByteBuffer data = payload.getData(); - - final Frame frame = - POOL.acquireFrame(SetupFrameFlyweight.computeFrameLength(flags, metadataMimeType, dataMimeType, metadata.remaining(), data.remaining())); - - frame.length = SetupFrameFlyweight.encode( - frame.directBuffer, frame.offset, flags, keepaliveInterval, maxLifetime, metadataMimeType, dataMimeType, metadata, data); + final ByteBuf metadata = payload.getMetadata() != null ? Unpooled.wrappedBuffer(payload.getMetadata()) : Unpooled.EMPTY_BUFFER; + final ByteBuf data = payload.getData() != null ? Unpooled.wrappedBuffer(payload.getData()) : Unpooled.EMPTY_BUFFER; + + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer( + SetupFrameFlyweight.computeFrameLength(flags, metadataMimeType, dataMimeType, metadata.readableBytes(), data.readableBytes())); + frame.content.writerIndex(SetupFrameFlyweight.encode( + frame.content, flags, keepaliveInterval, maxLifetime, metadataMimeType, dataMimeType, metadata, data)); return frame; } public static int getFlags(final Frame frame) { ensureFrameType(FrameType.SETUP, frame); - final int flags = FrameHeaderFlyweight.flags(frame.directBuffer, frame.offset); + final int flags = FrameHeaderFlyweight.flags(frame.content); return flags & SetupFrameFlyweight.VALID_FLAGS; } public static int version(final Frame frame) { ensureFrameType(FrameType.SETUP, frame); - return SetupFrameFlyweight.version(frame.directBuffer, frame.offset); + return SetupFrameFlyweight.version(frame.content); } public static int keepaliveInterval(final Frame frame) { ensureFrameType(FrameType.SETUP, frame); - return SetupFrameFlyweight.keepaliveInterval(frame.directBuffer, frame.offset); + return SetupFrameFlyweight.keepaliveInterval(frame.content); } public static int maxLifetime(final Frame frame) { ensureFrameType(FrameType.SETUP, frame); - return SetupFrameFlyweight.maxLifetime(frame.directBuffer, frame.offset); + return SetupFrameFlyweight.maxLifetime(frame.content); } public static String metadataMimeType(final Frame frame) { ensureFrameType(FrameType.SETUP, frame); - return SetupFrameFlyweight.metadataMimeType(frame.directBuffer, frame.offset); + return SetupFrameFlyweight.metadataMimeType(frame.content); } public static String dataMimeType(final Frame frame) { ensureFrameType(FrameType.SETUP, frame); - return SetupFrameFlyweight.dataMimeType(frame.directBuffer, frame.offset); + return SetupFrameFlyweight.dataMimeType(frame.content); } } @@ -311,30 +344,29 @@ private Error() {} public static Frame from( int streamId, final Throwable throwable, - ByteBuffer metadata, - ByteBuffer data + ByteBuf metadata, + ByteBuf data ) { - final int code = ErrorFrameFlyweight.errorCodeFromException(throwable); - final Frame frame = POOL.acquireFrame( - ErrorFrameFlyweight.computeFrameLength(metadata.remaining(), data.remaining())); - if (errorLogger.isDebugEnabled()) { errorLogger.debug("an error occurred, creating error frame", throwable); } - frame.length = ErrorFrameFlyweight.encode( - frame.directBuffer, frame.offset, streamId, code, metadata, data); + final int code = ErrorFrameFlyweight.errorCodeFromException(throwable); + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer( + ErrorFrameFlyweight.computeFrameLength(metadata.readableBytes(), data.readableBytes())); + frame.content.writerIndex(ErrorFrameFlyweight.encode(frame.content, streamId, code, metadata, data)); return frame; } public static Frame from( int streamId, final Throwable throwable, - ByteBuffer metadata + ByteBuf metadata ) { String data = throwable.getMessage() == null ? "" : throwable.getMessage(); byte[] bytes = data.getBytes(StandardCharsets.UTF_8); - final ByteBuffer dataBuffer = ByteBuffer.wrap(bytes); + final ByteBuf dataBuffer = Unpooled.wrappedBuffer(bytes); return from(streamId, throwable, metadata, dataBuffer); } @@ -343,33 +375,34 @@ public static Frame from( int streamId, final Throwable throwable ) { - return from(streamId, throwable, NULL_BYTEBUFFER); + return from(streamId, throwable, Unpooled.EMPTY_BUFFER); } public static int errorCode(final Frame frame) { ensureFrameType(FrameType.ERROR, frame); - return ErrorFrameFlyweight.errorCode(frame.directBuffer, frame.offset); + return ErrorFrameFlyweight.errorCode(frame.content); } } public static class Lease { private Lease() {} - public static Frame from(int ttl, int numberOfRequests, ByteBuffer metadata) { - final Frame frame = POOL.acquireFrame(LeaseFrameFlyweight.computeFrameLength(metadata.remaining())); - - frame.length = LeaseFrameFlyweight.encode(frame.directBuffer, frame.offset, ttl, numberOfRequests, metadata); + public static Frame from(int ttl, int numberOfRequests, ByteBuf metadata) { + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer( + LeaseFrameFlyweight.computeFrameLength(metadata.readableBytes())); + frame.content.writerIndex(LeaseFrameFlyweight.encode(frame.content, ttl, numberOfRequests, metadata)); return frame; } public static int ttl(final Frame frame) { ensureFrameType(FrameType.LEASE, frame); - return LeaseFrameFlyweight.ttl(frame.directBuffer, frame.offset); + return LeaseFrameFlyweight.ttl(frame.content); } public static int numberOfRequests(final Frame frame) { ensureFrameType(FrameType.LEASE, frame); - return LeaseFrameFlyweight.numRequests(frame.directBuffer, frame.offset); + return LeaseFrameFlyweight.numRequests(frame.content); } } @@ -386,15 +419,15 @@ public static Frame from(int streamId, int requestN) { throw new IllegalStateException("request n must be greater than 0"); } - final Frame frame = POOL.acquireFrame(RequestNFrameFlyweight.computeFrameLength()); - - frame.length = RequestNFrameFlyweight.encode(frame.directBuffer, frame.offset, streamId, requestN); + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer(RequestNFrameFlyweight.computeFrameLength()); + frame.content.writerIndex(RequestNFrameFlyweight.encode(frame.content, streamId, requestN)); return frame; } public static int requestN(final Frame frame) { ensureFrameType(FrameType.REQUEST_N, frame); - return RequestNFrameFlyweight.requestN(frame.directBuffer, frame.offset); + return RequestNFrameFlyweight.requestN(frame.content); } } @@ -411,34 +444,35 @@ public static Frame from(int streamId, FrameType type, Payload payload, int init throw new IllegalStateException("initial request n must be greater than 0"); } - final ByteBuffer d = payload.getData() != null ? payload.getData() : NULL_BYTEBUFFER; - final ByteBuffer md = payload.getMetadata() != null ? payload.getMetadata() : NULL_BYTEBUFFER; + final ByteBuf metadata = payload.getMetadata() != null ? Unpooled.wrappedBuffer(payload.getMetadata()) : Unpooled.EMPTY_BUFFER; + final ByteBuf data = payload.getData() != null ? Unpooled.wrappedBuffer(payload.getData()) : Unpooled.EMPTY_BUFFER; - final Frame frame = POOL.acquireFrame(RequestFrameFlyweight.computeFrameLength(type, md.remaining(), d.remaining())); + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer( + RequestFrameFlyweight.computeFrameLength(type, metadata.readableBytes(), data.readableBytes())); if (type.hasInitialRequestN()) { - frame.length = RequestFrameFlyweight.encode(frame.directBuffer, frame.offset, streamId, 0, type, initialRequestN, md, d); + frame.content.writerIndex(RequestFrameFlyweight.encode(frame.content, streamId, 0, type, initialRequestN, metadata, data)); } else { - frame.length = RequestFrameFlyweight.encode(frame.directBuffer, frame.offset, streamId, 0, type, md, d); + frame.content.writerIndex(RequestFrameFlyweight.encode(frame.content, streamId, 0, type, metadata, data)); } return frame; } public static Frame from(int streamId, FrameType type, int flags) { - final Frame frame = POOL.acquireFrame(RequestFrameFlyweight.computeFrameLength(type, 0, 0)); - - frame.length = RequestFrameFlyweight.encode(frame.directBuffer, frame.offset, streamId, flags, type, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer(RequestFrameFlyweight.computeFrameLength(type, 0, 0)); + frame.content.writerIndex(RequestFrameFlyweight.encode(frame.content, streamId, flags, type, Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER)); return frame; } - public static Frame from(int streamId, FrameType type, ByteBuffer metadata, ByteBuffer data, int initialRequestN, int flags) { - final Frame frame = POOL.acquireFrame(RequestFrameFlyweight.computeFrameLength(type, metadata.remaining(), data.remaining())); - - frame.length = RequestFrameFlyweight.encode(frame.directBuffer, frame.offset, streamId, flags, type, initialRequestN, metadata, data); + public static Frame from(int streamId, FrameType type, ByteBuf metadata, ByteBuf data, int initialRequestN, int flags) { + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer(RequestFrameFlyweight.computeFrameLength(type, metadata.readableBytes(), data.readableBytes())); + frame.content.writerIndex(RequestFrameFlyweight.encode(frame.content, streamId, flags, type, initialRequestN, metadata, data)); return frame; - } public static int initialRequestN(final Frame frame) { @@ -457,7 +491,7 @@ public static int initialRequestN(final Frame frame) { result = 0; break; default: - result = RequestFrameFlyweight.initialRequestN(frame.directBuffer, frame.offset); + result = RequestFrameFlyweight.initialRequestN(frame.content); break; } @@ -466,7 +500,7 @@ public static int initialRequestN(final Frame frame) { public static boolean isRequestChannelComplete(final Frame frame) { ensureFrameType(FrameType.REQUEST_CHANNEL, frame); - final int flags = FrameHeaderFlyweight.flags(frame.directBuffer, frame.offset); + final int flags = FrameHeaderFlyweight.flags(frame.content); return (flags & FrameHeaderFlyweight.FLAGS_C) == FrameHeaderFlyweight.FLAGS_C; } @@ -476,31 +510,25 @@ public static class PayloadFrame { private PayloadFrame() {} - public static Frame from(int streamId, FrameType type, Payload payload) { - final ByteBuffer data = payload.getData() != null ? payload.getData() : NULL_BYTEBUFFER; - final ByteBuffer metadata = payload.getMetadata() != null ? payload.getMetadata() : NULL_BYTEBUFFER; - - final Frame frame = - POOL.acquireFrame(FrameHeaderFlyweight.computeFrameHeaderLength(type, metadata.remaining(), data.remaining())); - - frame.length = FrameHeaderFlyweight.encode(frame.directBuffer, frame.offset, streamId, 0, type, metadata, data); - return frame; + public static Frame from(int streamId, FrameType type) { + return from(streamId, type, Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER, 0); } - public static Frame from(int streamId, FrameType type, ByteBuffer metadata, ByteBuffer data, int flags) { - final Frame frame = - POOL.acquireFrame(FrameHeaderFlyweight.computeFrameHeaderLength(type, metadata.remaining(), data.remaining())); - - frame.length = FrameHeaderFlyweight.encode(frame.directBuffer, frame.offset, streamId, flags, type, metadata, data); - return frame; + public static Frame from(int streamId, FrameType type, Payload payload) { + return from(streamId, type, payload, 0); } - public static Frame from(int streamId, FrameType type) { - final Frame frame = - POOL.acquireFrame(FrameHeaderFlyweight.computeFrameHeaderLength(type, 0, 0)); + public static Frame from(int streamId, FrameType type, Payload payload, int flags) { + final ByteBuf metadata = payload.getMetadata() != null ? Unpooled.wrappedBuffer(payload.getMetadata()) : Unpooled.EMPTY_BUFFER; + final ByteBuf data = payload.getData() != null ? Unpooled.wrappedBuffer(payload.getData()) : Unpooled.EMPTY_BUFFER; + return from(streamId, type, metadata, data, flags); + } - frame.length = FrameHeaderFlyweight.encode( - frame.directBuffer, frame.offset, streamId, 0, type, Frame.NULL_BYTEBUFFER, Frame.NULL_BYTEBUFFER); + public static Frame from(int streamId, FrameType type, ByteBuf metadata, ByteBuf data, int flags) { + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer( + FrameHeaderFlyweight.computeFrameHeaderLength(type, metadata.readableBytes(), data.readableBytes())); + frame.content.writerIndex(FrameHeaderFlyweight.encode(frame.content, streamId, flags, type, metadata, data)); return frame; } } @@ -510,11 +538,11 @@ public static class Cancel { private Cancel() {} public static Frame from(int streamId) { - final Frame frame = - POOL.acquireFrame(FrameHeaderFlyweight.computeFrameHeaderLength(FrameType.CANCEL, 0, 0)); - - frame.length = FrameHeaderFlyweight.encode( - frame.directBuffer, frame.offset, streamId, 0, FrameType.CANCEL, Frame.NULL_BYTEBUFFER, Frame.NULL_BYTEBUFFER); + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer( + FrameHeaderFlyweight.computeFrameHeaderLength(FrameType.CANCEL, 0, 0)); + frame.content.writerIndex(FrameHeaderFlyweight.encode( + frame.content, streamId, 0, FrameType.CANCEL, Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER)); return frame; } } @@ -523,20 +551,20 @@ public static class Keepalive { private Keepalive() {} - public static Frame from(ByteBuffer data, boolean respond) { - final Frame frame = - POOL.acquireFrame(KeepaliveFrameFlyweight.computeFrameLength(data.remaining())); + public static Frame from(ByteBuf data, boolean respond) { + final Frame frame = RECYCLER.get(); + frame.content = ByteBufAllocator.DEFAULT.buffer( + KeepaliveFrameFlyweight.computeFrameLength(data.readableBytes())); final int flags = respond ? KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R : 0; - - frame.length = KeepaliveFrameFlyweight.encode(frame.directBuffer, frame.offset, flags, data); + frame.content.writerIndex(KeepaliveFrameFlyweight.encode(frame.content, flags, data)); return frame; } public static boolean hasRespondFlag(final Frame frame) { ensureFrameType(FrameType.KEEPALIVE, frame); - final int flags = FrameHeaderFlyweight.flags(frame.directBuffer, frame.offset); + final int flags = FrameHeaderFlyweight.flags(frame.content); return (flags & KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R) == KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R; } @@ -558,26 +586,19 @@ public String toString() { String additionalFlags = ""; try { - type = FrameHeaderFlyweight.frameType(directBuffer, 0); - - ByteBuffer byteBuffer; - byte[] bytes; + type = FrameHeaderFlyweight.frameType(content); - byteBuffer = FrameHeaderFlyweight.sliceFrameMetadata(directBuffer, 0, 0); - if (0 < byteBuffer.remaining()) { - bytes = new byte[byteBuffer.remaining()]; - byteBuffer.get(bytes); - payload.append(String.format("metadata: \"%s\" ", new String(bytes, StandardCharsets.UTF_8))); + ByteBuf metadata = FrameHeaderFlyweight.sliceFrameMetadata(content); + if (0 < metadata.readableBytes()) { + payload.append(String.format("metadata: \"%s\" ", metadata.toString(StandardCharsets.UTF_8))); } - byteBuffer = FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, 0); - if (0 < byteBuffer.remaining()) { - bytes = new byte[byteBuffer.remaining()]; - byteBuffer.get(bytes); - payload.append(String.format("data: \"%s\"", new String(bytes, StandardCharsets.UTF_8))); + ByteBuf data = FrameHeaderFlyweight.sliceFrameData(content); + if (0 < data.readableBytes()) { + payload.append(String.format("data: \"%s\" ", data.toString(StandardCharsets.UTF_8))); } - streamId = FrameHeaderFlyweight.streamId(directBuffer, 0); + streamId = FrameHeaderFlyweight.streamId(content); switch (type) { case LEASE: @@ -608,7 +629,7 @@ public String toString() { } catch (Exception e) { logger.error("Error generating toString, ignored.", e); } - return "Frame[" + offset + "] => Stream ID: " + streamId + " Type: " + type + return "Frame => Stream ID: " + streamId + " Type: " + type + (!additionalFlags.isEmpty() ? additionalFlags : "") + " Payload: " + payload; } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index acd7ac8f6..a0aff489e 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -16,14 +16,17 @@ package io.reactivesocket; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.reactivesocket.Frame.Lease; import io.reactivesocket.Frame.Request; +import io.reactivesocket.internal.Int2ObjectHashMap; import io.reactivesocket.exceptions.ApplicationException; import io.reactivesocket.frame.FrameHeaderFlyweight; import io.reactivesocket.internal.KnownErrorFilter; import io.reactivesocket.internal.LimitableRequestPublisher; import io.reactivesocket.lease.LeaseEnforcingSocket; -import org.agrona.collections.Int2ObjectHashMap; +import io.reactivesocket.util.PayloadImpl; import org.reactivestreams.Publisher; import org.reactivestreams.Subscription; import reactor.core.Disposable; @@ -67,7 +70,8 @@ public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestH if (!clientHonorsLease) { return; } - Frame leaseFrame = Lease.from(lease.getTtl(), lease.getAllowedRequests(), lease.metadata()); + ByteBuf metadata = lease.getMetadata() == null ? Unpooled.EMPTY_BUFFER : Unpooled.wrappedBuffer(lease.getMetadata()); + Frame leaseFrame = Lease.from(lease.getTtl(), lease.getAllowedRequests(), metadata); connection.sendOne(leaseFrame) .doOnError(errorConsumer) .subscribe(); @@ -143,71 +147,75 @@ public ServerReactiveSocket start() { subscribe = connection .receive() .flatMap(frame -> { - int streamId = frame.getStreamId(); - UnicastProcessor receiver; - switch (frame.getType()) { - case FIRE_AND_FORGET: - return handleFireAndForget(streamId, fireAndForget(frame)); - case REQUEST_RESPONSE: - return handleRequestResponse(streamId, requestResponse(frame)); - case CANCEL: - return handleCancelFrame(streamId); - case KEEPALIVE: - return handleKeepAliveFrame(frame); - case REQUEST_N: - return handleRequestN(streamId, frame); - case REQUEST_STREAM: - return handleStream(streamId, requestStream(frame), frame); - case REQUEST_CHANNEL: - return handleChannel(streamId, frame); - case PAYLOAD: - // TODO: Hook in receiving socket. - return Mono.empty(); - case METADATA_PUSH: - return metadataPush(frame); - case LEASE: - // Lease must not be received here as this is the server end of the socket which sends leases. - return Mono.empty(); - case NEXT: - synchronized (ServerReactiveSocket.this) { - receiver = receivers.get(streamId); - } - if (receiver != null) { - receiver.onNext(frame); - } - return Mono.empty(); - case COMPLETE: - synchronized (ServerReactiveSocket.this) { - receiver = receivers.get(streamId); - } - if (receiver != null) { - receiver.onComplete(); - } - return Mono.empty(); - case ERROR: - synchronized (ServerReactiveSocket.this) { - receiver = receivers.get(streamId); - } - if (receiver != null) { - receiver.onError(new ApplicationException(frame)); - } - return Mono.empty(); - case NEXT_COMPLETE: - synchronized (ServerReactiveSocket.this) { - receiver = receivers.get(streamId); - } - if (receiver != null) { - receiver.onNext(frame); - receiver.onComplete(); - } - - return Mono.empty(); - - case SETUP: - return handleError(streamId, new IllegalStateException("Setup frame received post setup.")); - default: - return handleError(streamId, new IllegalStateException("ServerReactiveSocket: Unexpected frame type: " - + frame.getType())); + try { + int streamId = frame.getStreamId(); + UnicastProcessor receiver; + switch (frame.getType()) { + case FIRE_AND_FORGET: + return handleFireAndForget(streamId, fireAndForget(new PayloadImpl(frame))); + case REQUEST_RESPONSE: + return handleRequestResponse(streamId, requestResponse(new PayloadImpl(frame))); + case CANCEL: + return handleCancelFrame(streamId); + case KEEPALIVE: + return handleKeepAliveFrame(frame); + case REQUEST_N: + return handleRequestN(streamId, frame); + case REQUEST_STREAM: + return handleStream(streamId, requestStream(new PayloadImpl(frame)), frame); + case REQUEST_CHANNEL: + return handleChannel(streamId, frame); + case PAYLOAD: + // TODO: Hook in receiving socket. + return Mono.empty(); + case METADATA_PUSH: + return metadataPush(new PayloadImpl(frame)); + case LEASE: + // Lease must not be received here as this is the server end of the socket which sends leases. + return Mono.empty(); + case NEXT: + synchronized (ServerReactiveSocket.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onNext(new PayloadImpl(frame)); + } + return Mono.empty(); + case COMPLETE: + synchronized (ServerReactiveSocket.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onComplete(); + } + return Mono.empty(); + case ERROR: + synchronized (ServerReactiveSocket.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onError(new ApplicationException(new PayloadImpl(frame))); + } + return Mono.empty(); + case NEXT_COMPLETE: + synchronized (ServerReactiveSocket.this) { + receiver = receivers.get(streamId); + } + if (receiver != null) { + receiver.onNext(new PayloadImpl(frame)); + receiver.onComplete(); + } + + return Mono.empty(); + + case SETUP: + return handleError(streamId, new IllegalStateException("Setup frame received post setup.")); + default: + return handleError(streamId, new IllegalStateException("ServerReactiveSocket: Unexpected frame type: " + + frame.getType())); + } + } finally { + frame.release(); } }) .doOnError(t -> { @@ -230,6 +238,7 @@ private void cleanup() { if (subscribe != null) { subscribe.dispose(); } + sendingSubscriptions.values().forEach(this::cleanUpSendingSubscription); receivers.values().forEach(this::cleanUpReceivingSubscription); @@ -266,7 +275,7 @@ private Mono handleRequestResponse(int streamId, Mono response) { response .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) .map(payload -> - Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, payload.getMetadata(), payload.getData(), FrameHeaderFlyweight.FLAGS_C)) + Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, payload, FrameHeaderFlyweight.FLAGS_C)) .doOnCancel(() -> { if (connection.availability() > 0.0) { connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); @@ -321,33 +330,31 @@ private Mono handleStream(int streamId, Flux response, Frame firs } private Mono handleChannel(int streamId, Frame firstFrame) { - return Mono.defer(() -> { - UnicastProcessor frames = UnicastProcessor.create(); - Flux payloads = frames - .doOnCancel(() -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); - } - }) - .doOnError(t -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); - } - }) - .doOnRequest(l -> { - if (connection.availability() > 0.0) { - connection.sendOne(Frame.RequestN.from(streamId, l)).subscribe(null, errorConsumer::accept); - } - }) - .cast(Payload.class); + UnicastProcessor frames = UnicastProcessor.create(); + Flux payloads = frames + .doOnCancel(() -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); + } + }) + .doOnError(t -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + } + }) + .doOnRequest(l -> { + if (connection.availability() > 0.0) { + connection.sendOne(Frame.RequestN.from(streamId, l)).subscribe(null, errorConsumer::accept); + } + }) + .cast(Payload.class); - return handleStream(streamId, requestChannel(payloads), firstFrame); - }); + return handleStream(streamId, requestChannel(payloads), firstFrame); } private Mono handleKeepAliveFrame(Frame frame) { if (Frame.Keepalive.hasRespondFlag(frame)) { - return connection.sendOne(Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, false)) + return connection.sendOne(Frame.Keepalive.from(Unpooled.EMPTY_BUFFER, false)) .doOnError(errorConsumer); } return Mono.empty(); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java index d8eeb26e0..b392cffd1 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/client/SetupProviderImpl.java @@ -59,7 +59,7 @@ final class SetupProviderImpl implements SetupProvider { @Override public Mono accept(DuplexConnection connection, SocketAcceptor acceptor) { - return connection.sendOne(copySetupFrame()) + return connection.sendOne(setupFrame) .then(() -> { ClientServerInputMultiplexer multiplexer = new ClientServerInputMultiplexer(connection); ClientReactiveSocket sendingSocket = @@ -83,7 +83,8 @@ public Mono accept(DuplexConnection connection, SocketAcceptor a @Override public SetupProvider dataMimeType(String dataMimeType) { Frame newSetup = from(getFlags(setupFrame), keepaliveInterval(setupFrame), maxLifetime(setupFrame), - Frame.Setup.metadataMimeType(setupFrame), dataMimeType, setupFrame); + Frame.Setup.metadataMimeType(setupFrame), dataMimeType, new PayloadImpl(setupFrame)); + setupFrame.release(); return new SetupProviderImpl(newSetup, leaseDecorator, keepAliveProvider, errorConsumer); } @@ -91,7 +92,8 @@ public SetupProvider dataMimeType(String dataMimeType) { public SetupProvider metadataMimeType(String metadataMimeType) { Frame newSetup = from(getFlags(setupFrame), keepaliveInterval(setupFrame), maxLifetime(setupFrame), metadataMimeType, Frame.Setup.dataMimeType(setupFrame), - setupFrame); + new PayloadImpl(setupFrame)); + setupFrame.release(); return new SetupProviderImpl(newSetup, leaseDecorator, keepAliveProvider, errorConsumer); } @@ -110,7 +112,8 @@ public SetupProvider disableLease(Function s Frame newSetup = from(getFlags(setupFrame) & ~ConnectionSetupPayload.HONOR_LEASE, keepaliveInterval(setupFrame), maxLifetime(setupFrame), Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), - setupFrame); + new PayloadImpl(setupFrame)); + setupFrame.release(); return new SetupProviderImpl(newSetup, socketFactory, keepAliveProvider, errorConsumer); } @@ -120,14 +123,8 @@ public SetupProvider setupPayload(Payload setupPayload) { keepaliveInterval(setupFrame), maxLifetime(setupFrame), Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), setupPayload); + setupFrame.release(); return new SetupProviderImpl(newSetup, reactiveSocket -> new DisableLeaseSocket(reactiveSocket), keepAliveProvider, errorConsumer); } - - private Frame copySetupFrame() { - Frame newSetup = from(getFlags(setupFrame), keepaliveInterval(setupFrame), maxLifetime(setupFrame), - Frame.Setup.metadataMimeType(setupFrame), Frame.Setup.dataMimeType(setupFrame), - new PayloadImpl(setupFrame.getData().duplicate(), setupFrame.getMetadata().duplicate())); - return newSetup; - } } \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/exceptions/Exceptions.java b/reactivesocket-core/src/main/java/io/reactivesocket/exceptions/Exceptions.java index 57d284386..85427fce4 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/exceptions/Exceptions.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/exceptions/Exceptions.java @@ -16,9 +16,9 @@ package io.reactivesocket.exceptions; import io.reactivesocket.Frame; -import io.reactivesocket.frame.ByteBufferUtil; +import io.reactivesocket.util.PayloadImpl; -import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import static io.reactivesocket.frame.ErrorFrameFlyweight.*; @@ -28,31 +28,29 @@ private Exceptions() {} public static RuntimeException from(Frame frame) { final int errorCode = Frame.Error.errorCode(frame); - ByteBuffer dataBuffer = frame.getData(); - String message = dataBuffer.remaining() == 0 ? "" : ByteBufferUtil.toUtf8String(dataBuffer); RuntimeException ex; switch (errorCode) { case APPLICATION_ERROR: - ex = new ApplicationException(frame); + ex = new ApplicationException(new PayloadImpl(frame)); break; case CONNECTION_ERROR: - ex = new ConnectionException(message); + ex = new ConnectionException(StandardCharsets.UTF_8.decode(frame.getData()).toString()); break; case INVALID: - ex = new InvalidRequestException(message); + ex = new InvalidRequestException(StandardCharsets.UTF_8.decode(frame.getData()).toString()); break; case INVALID_SETUP: - ex = new InvalidSetupException(message); + ex = new InvalidSetupException(StandardCharsets.UTF_8.decode(frame.getData()).toString()); break; case REJECTED: - ex = new RejectedException(message); + ex = new RejectedException(StandardCharsets.UTF_8.decode(frame.getData()).toString()); break; case REJECTED_SETUP: - ex = new RejectedSetupException(message); + ex = new RejectedSetupException(StandardCharsets.UTF_8.decode(frame.getData()).toString()); break; case UNSUPPORTED_SETUP: - ex = new UnsupportedSetupException(message); + ex = new UnsupportedSetupException(StandardCharsets.UTF_8.decode(frame.getData()).toString()); break; default: ex = new InvalidRequestException("Invalid Error frame"); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ByteBufferUtil.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ByteBufferUtil.java deleted file mode 100644 index d7ecc55a6..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ByteBufferUtil.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.frame; - -import java.nio.ByteBuffer; - -import static java.nio.charset.StandardCharsets.UTF_8; - -public class ByteBufferUtil { - - private ByteBufferUtil() {} - - /** - * Slice a portion of the {@link ByteBuffer} while preserving the buffers position and LimitableRequestPublisher.java. - * - * NOTE: Missing functionality from {@link ByteBuffer} - * - * @param byteBuffer to slice off of - * @param position to start slice at - * @param limit to slice to - * @return slice of byteBuffer with passed ByteBuffer preserved position and LimitableRequestPublisher.java. - */ - public static ByteBuffer preservingSlice(final ByteBuffer byteBuffer, final int position, final int limit) { - final int savedPosition = byteBuffer.position(); - final int savedLimit = byteBuffer.limit(); - - byteBuffer.limit(limit).position(position); - - final ByteBuffer result = byteBuffer.slice(); - - byteBuffer.limit(savedLimit).position(savedPosition); - return result; - } - - public static String toUtf8String(ByteBuffer byteBuffer) { - byte[] bytes = new byte[byteBuffer.remaining()]; - byteBuffer.get(bytes); - return new String(bytes, UTF_8); - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java index 639f8b922..101105d83 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ErrorFrameFlyweight.java @@ -15,6 +15,7 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; import io.reactivesocket.FrameType; import io.reactivesocket.exceptions.ApplicationException; import io.reactivesocket.exceptions.CancelException; @@ -24,12 +25,7 @@ import io.reactivesocket.exceptions.RejectedException; import io.reactivesocket.exceptions.RejectedSetupException; import io.reactivesocket.exceptions.UnsupportedSetupException; -import org.agrona.BitUtil; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import io.reactivesocket.util.BitUtil; public class ErrorFrameFlyweight { @@ -61,25 +57,23 @@ public static int computeFrameLength( } public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, final int streamId, final int errorCode, - final ByteBuffer metadata, - final ByteBuffer data + final ByteBuf metadata, + final ByteBuf data ) { - final int frameLength = computeFrameLength(metadata.remaining(), data.remaining()); + final int frameLength = computeFrameLength(metadata.readableBytes(), data.readableBytes()); int length = FrameHeaderFlyweight.encodeFrameHeader( - mutableDirectBuffer, offset, frameLength, 0, FrameType.ERROR, streamId); + byteBuf, frameLength, 0, FrameType.ERROR, streamId); - mutableDirectBuffer.putInt( - offset + ERROR_CODE_FIELD_OFFSET, errorCode, ByteOrder.BIG_ENDIAN); + byteBuf.setInt(ERROR_CODE_FIELD_OFFSET, errorCode); length += BitUtil.SIZE_OF_INT; length += FrameHeaderFlyweight.encodeMetadata( - mutableDirectBuffer, FrameType.ERROR, offset, offset + length, metadata); - length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); + byteBuf, FrameType.ERROR, length, metadata); + length += FrameHeaderFlyweight.encodeData(byteBuf, length, data); return length; } @@ -105,11 +99,11 @@ public static int errorCodeFromException(Throwable ex) { return INVALID; } - public static int errorCode(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + ERROR_CODE_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int errorCode(final ByteBuf byteBuf) { + return byteBuf.getInt(ERROR_CODE_FIELD_OFFSET); } - public static int payloadOffset(final DirectBuffer directBuffer, final int offset) { - return offset + FrameHeaderFlyweight.FRAME_HEADER_LENGTH + BitUtil.SIZE_OF_INT; + public static int payloadOffset(final ByteBuf byteBuf) { + return FrameHeaderFlyweight.FRAME_HEADER_LENGTH + BitUtil.SIZE_OF_INT; } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java index 8721b446a..8fee45261 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FrameHeaderFlyweight.java @@ -15,14 +15,13 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.reactivesocket.Frame; import io.reactivesocket.FrameType; -import org.agrona.BitUtil; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; +import io.reactivesocket.util.BitUtil; import java.nio.ByteBuffer; -import java.nio.ByteOrder; /** * Per connection frame flyweight. @@ -38,12 +37,8 @@ public class FrameHeaderFlyweight { private FrameHeaderFlyweight() {} - public static final ByteBuffer NULL_BYTEBUFFER = ByteBuffer.allocate(0); - public static final int FRAME_HEADER_LENGTH; - private static final boolean INCLUDE_FRAME_LENGTH = true; - private static final int FRAME_TYPE_BITS = 6; private static final int FRAME_TYPE_SHIFT = 16 - FRAME_TYPE_BITS; private static final int FRAME_FLAGS_MASK = 0b0000_0011_1111_1111; @@ -64,16 +59,10 @@ private FrameHeaderFlyweight() {} public static final int FLAGS_N = 0b00_0010_0000; static { - if (INCLUDE_FRAME_LENGTH) { - FRAME_LENGTH_FIELD_OFFSET = 0; - } else { - FRAME_LENGTH_FIELD_OFFSET = -FRAME_LENGTH_SIZE; - } - + FRAME_LENGTH_FIELD_OFFSET = 0; STREAM_ID_FIELD_OFFSET = FRAME_LENGTH_FIELD_OFFSET + FRAME_LENGTH_SIZE; FRAME_TYPE_AND_FLAGS_FIELD_OFFSET = STREAM_ID_FIELD_OFFSET + BitUtil.SIZE_OF_INT; PAYLOAD_OFFSET = FRAME_TYPE_AND_FLAGS_FIELD_OFFSET + BitUtil.SIZE_OF_SHORT; - FRAME_HEADER_LENGTH = PAYLOAD_OFFSET; } @@ -82,8 +71,7 @@ public static int computeFrameHeaderLength(final FrameType frameType, int metada } public static int encodeFrameHeader( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, final int frameLength, final int flags, final FrameType frameType, @@ -93,38 +81,35 @@ public static int encodeFrameHeader( throw new IllegalArgumentException("Frame length is larger than 24 bits"); } - if (INCLUDE_FRAME_LENGTH) { - // frame length field needs to be excluded from the length - encodeLength(mutableDirectBuffer, offset + FRAME_LENGTH_FIELD_OFFSET, frameLength - FRAME_LENGTH_SIZE); - } + // frame length field needs to be excluded from the length + encodeLength(byteBuf, FRAME_LENGTH_FIELD_OFFSET, frameLength - FRAME_LENGTH_SIZE); - mutableDirectBuffer.putInt(offset + STREAM_ID_FIELD_OFFSET, streamId, ByteOrder.BIG_ENDIAN); + byteBuf.setInt(STREAM_ID_FIELD_OFFSET, streamId); short typeAndFlags = (short) (frameType.getEncodedType() << FRAME_TYPE_SHIFT | (short) flags); - mutableDirectBuffer.putShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, typeAndFlags, ByteOrder.BIG_ENDIAN); + byteBuf.setShort(FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, typeAndFlags); return FRAME_HEADER_LENGTH; } public static int encodeMetadata( - final MutableDirectBuffer mutableDirectBuffer, + final ByteBuf byteBuf, final FrameType frameType, - final int frameHeaderStartOffset, final int metadataOffset, - final ByteBuffer metadata + final ByteBuf metadata ) { int length = 0; - final int metadataLength = metadata.remaining(); + final int metadataLength = metadata.readableBytes(); if (0 < metadataLength) { - int typeAndFlags = mutableDirectBuffer.getShort(frameHeaderStartOffset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + int typeAndFlags = byteBuf.getShort(FRAME_TYPE_AND_FLAGS_FIELD_OFFSET); typeAndFlags |= FLAGS_M; - mutableDirectBuffer.putShort(frameHeaderStartOffset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, (short) typeAndFlags, ByteOrder.BIG_ENDIAN); + byteBuf.setShort(FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, (short) typeAndFlags); if (hasMetadataLengthField(frameType)) { - encodeLength(mutableDirectBuffer, metadataOffset, metadataLength); + encodeLength(byteBuf, metadataOffset, metadataLength); length += FRAME_LENGTH_SIZE; } - mutableDirectBuffer.putBytes(metadataOffset + length, metadata, metadataLength); + byteBuf.setBytes(metadataOffset + length, metadata); length += metadataLength; } @@ -132,15 +117,15 @@ public static int encodeMetadata( } public static int encodeData( - final MutableDirectBuffer mutableDirectBuffer, + final ByteBuf byteBuf, final int dataOffset, - final ByteBuffer data + final ByteBuf data ) { int length = 0; - final int dataLength = data.remaining(); + final int dataLength = data.readableBytes(); if (0 < dataLength) { - mutableDirectBuffer.putBytes(dataOffset, data, dataLength); + byteBuf.setBytes(dataOffset, data); length += dataLength; } @@ -149,15 +134,14 @@ public static int encodeData( // only used for types simple enough that they don't have their own FrameFlyweights public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, final int streamId, int flags, final FrameType frameType, - final ByteBuffer metadata, - final ByteBuffer data + final ByteBuf metadata, + final ByteBuf data ) { - final int frameLength = computeFrameHeaderLength(frameType, metadata.remaining(), data.remaining()); + final int frameLength = computeFrameHeaderLength(frameType, metadata.readableBytes(), data.readableBytes()); final FrameType outFrameType; switch (frameType) { @@ -180,21 +164,21 @@ public static int encode( break; } - int length = encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, outFrameType, streamId); + int length = encodeFrameHeader(byteBuf, frameLength, flags, outFrameType, streamId); - length += encodeMetadata(mutableDirectBuffer, frameType, offset, offset + length, metadata); - length += encodeData(mutableDirectBuffer, offset + length, data); + length += encodeMetadata(byteBuf, frameType, length, metadata); + length += encodeData(byteBuf, length, data); return length; } - public static int flags(final DirectBuffer directBuffer, final int offset) { - short typeAndFlags = directBuffer.getShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int flags(final ByteBuf byteBuf) { + short typeAndFlags = byteBuf.getShort(FRAME_TYPE_AND_FLAGS_FIELD_OFFSET); return typeAndFlags & FRAME_FLAGS_MASK; } - public static FrameType frameType(final DirectBuffer directBuffer, final int offset) { - int typeAndFlags = directBuffer.getShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static FrameType frameType(final ByteBuf byteBuf) { + int typeAndFlags = byteBuf.getShort(FRAME_TYPE_AND_FLAGS_FIELD_OFFSET); FrameType result = FrameType.from(typeAndFlags >> FRAME_TYPE_SHIFT); if (FrameType.PAYLOAD == result) { @@ -216,70 +200,66 @@ public static FrameType frameType(final DirectBuffer directBuffer, final int off return result; } - public static int streamId(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + STREAM_ID_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int streamId(final ByteBuf byteBuf) { + return byteBuf.getInt(STREAM_ID_FIELD_OFFSET); } - public static ByteBuffer sliceFrameData(final DirectBuffer directBuffer, final int offset, final int externalLength) { - final FrameType frameType = frameType(directBuffer, offset); - final int frameLength = frameLength(directBuffer, offset, externalLength); - final int dataLength = dataLength(directBuffer, frameType, offset, externalLength); - final int dataOffset = dataOffset(directBuffer, frameType, offset, frameLength); - ByteBuffer result = NULL_BYTEBUFFER; + public static ByteBuf sliceFrameData(final ByteBuf byteBuf) { + final FrameType frameType = frameType(byteBuf); + final int frameLength = frameLength(byteBuf); + final int dataLength = dataLength(byteBuf, frameType); + final int dataOffset = dataOffset(byteBuf, frameType, frameLength); + ByteBuf result = Unpooled.EMPTY_BUFFER; if (0 < dataLength) { - result = ByteBufferUtil.preservingSlice(directBuffer.byteBuffer(), dataOffset, dataOffset + dataLength); + result = byteBuf.slice(dataOffset, dataLength); } return result; } - public static ByteBuffer sliceFrameMetadata(final DirectBuffer directBuffer, final int offset, final int length) { - final FrameType frameType = frameType(directBuffer, offset); - final int frameLength = frameLength(directBuffer, offset, length); - final int metadataLength = Math.max(0, metadataLength(directBuffer, frameType, offset, frameLength)); - int metadataOffset = metadataOffset(directBuffer, offset); + public static ByteBuf sliceFrameMetadata(final ByteBuf byteBuf) { + final FrameType frameType = frameType(byteBuf); + final int frameLength = frameLength(byteBuf); + final int metadataLength = Math.max(0, metadataLength(byteBuf, frameType, frameLength)); + int metadataOffset = metadataOffset(byteBuf); if (hasMetadataLengthField(frameType)) { metadataOffset += FRAME_LENGTH_SIZE; } - ByteBuffer result = NULL_BYTEBUFFER; + ByteBuf result = Unpooled.EMPTY_BUFFER; if (0 < metadataLength) { - result = ByteBufferUtil.preservingSlice(directBuffer.byteBuffer(), metadataOffset, metadataOffset + metadataLength); + result = byteBuf.slice(metadataOffset, metadataLength); } return result; } - static int frameLength(final DirectBuffer directBuffer, final int offset, final int externalFrameLength) { - if (!INCLUDE_FRAME_LENGTH) { - return externalFrameLength; - } - + static int frameLength(final ByteBuf byteBuf) { // frame length field was excluded from the length so we will add it to represent // the entire block - return decodeLength(directBuffer, offset + FRAME_LENGTH_FIELD_OFFSET) + FRAME_LENGTH_SIZE; + return decodeLength(byteBuf, FRAME_LENGTH_FIELD_OFFSET) + FRAME_LENGTH_SIZE; } - private static int metadataFieldLength(DirectBuffer directBuffer, FrameType frameType, int offset, int frameLength) { - return computeMetadataLength(frameType, metadataLength(directBuffer, frameType, offset, frameLength)); + private static int metadataFieldLength(ByteBuf byteBuf, FrameType frameType, int frameLength) { + return computeMetadataLength(frameType, metadataLength(byteBuf, frameType, frameLength)); } - private static int metadataLength(DirectBuffer directBuffer, FrameType frameType, int offset, int frameLength) { - int metadataOffset = metadataOffset(directBuffer, offset); + private static int metadataLength(ByteBuf byteBuf, FrameType frameType, int frameLength) { + int metadataOffset = metadataOffset(byteBuf); if (!hasMetadataLengthField(frameType)) { - return frameLength - (metadataOffset - offset); + return frameLength - metadataOffset; } else { - return decodeMetadataLength(directBuffer, offset, metadataOffset); + return decodeMetadataLength(byteBuf, metadataOffset); } } - static int decodeMetadataLength(final DirectBuffer directBuffer, final int offset, final int metadataOffset) { + static int decodeMetadataLength(final ByteBuf byteBuf, final int metadataOffset) { int metadataLength = 0; - int flags = flags(directBuffer, offset); + int flags = flags(byteBuf); if (FLAGS_M == (FLAGS_M & flags)) { - metadataLength = decodeLength(directBuffer, metadataOffset); + metadataLength = decodeLength(byteBuf, metadataOffset); } return metadataLength; @@ -299,7 +279,7 @@ static boolean hasMetadataLengthField(FrameType frameType) { } private static void encodeLength( - final MutableDirectBuffer mutableDirectBuffer, + final ByteBuf byteBuf, final int offset, final int length ) { @@ -307,72 +287,70 @@ private static void encodeLength( throw new IllegalArgumentException("Length is larger than 24 bits"); } // Write each byte separately in reverse order, this mean we can write 1 << 23 without overflowing. - mutableDirectBuffer.putByte(offset, (byte) (length >> 16)); - mutableDirectBuffer.putByte(offset + 1, (byte) (length >> 8)); - mutableDirectBuffer.putByte(offset + 2, (byte) length); + byteBuf.setByte(offset, length >> 16); + byteBuf.setByte(offset + 1, length >> 8); + byteBuf.setByte(offset + 2, length); } - private static int decodeLength(final DirectBuffer directBuffer, final int offset) { - int length = (directBuffer.getByte(offset) & 0xFF) << 16; - length |= (directBuffer.getByte(offset + 1) & 0xFF) << 8; - length |= directBuffer.getByte(offset + 2) & 0xFF; + private static int decodeLength(final ByteBuf byteBuf, final int offset) { + int length = (byteBuf.getByte(offset) & 0xFF) << 16; + length |= (byteBuf.getByte(offset + 1) & 0xFF) << 8; + length |= byteBuf.getByte(offset + 2) & 0xFF; return length; } - private static int dataLength(final DirectBuffer directBuffer, final FrameType frameType, final int offset, final int externalLength) { - return dataLength(directBuffer, frameType, offset, externalLength, payloadOffset(directBuffer, offset)); + private static int dataLength(final ByteBuf byteBuf, final FrameType frameType) { + return dataLength(byteBuf, frameType, payloadOffset(byteBuf)); } static int dataLength( - final DirectBuffer directBuffer, + final ByteBuf byteBuf, final FrameType frameType, - final int offset, - final int externalLength, final int payloadOffset ) { - final int frameLength = frameLength(directBuffer, offset, externalLength); - final int metadataLength = metadataFieldLength(directBuffer, frameType, offset, frameLength); + final int frameLength = frameLength(byteBuf); + final int metadataLength = metadataFieldLength(byteBuf, frameType, frameLength); - return offset + frameLength - metadataLength - payloadOffset; + return frameLength - metadataLength - payloadOffset; } - private static int payloadOffset(final DirectBuffer directBuffer, final int offset) { - int typeAndFlags = directBuffer.getShort(offset + FRAME_TYPE_AND_FLAGS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + private static int payloadOffset(final ByteBuf byteBuf) { + int typeAndFlags = byteBuf.getShort(FRAME_TYPE_AND_FLAGS_FIELD_OFFSET); FrameType frameType = FrameType.from(typeAndFlags >> FRAME_TYPE_SHIFT); - int result = offset + PAYLOAD_OFFSET; + int result = PAYLOAD_OFFSET; switch (frameType) { case SETUP: - result = SetupFrameFlyweight.payloadOffset(directBuffer, offset); + result = SetupFrameFlyweight.payloadOffset(byteBuf); break; case ERROR: - result = ErrorFrameFlyweight.payloadOffset(directBuffer, offset); + result = ErrorFrameFlyweight.payloadOffset(byteBuf); break; case LEASE: - result = LeaseFrameFlyweight.payloadOffset(directBuffer, offset); + result = LeaseFrameFlyweight.payloadOffset(byteBuf); break; case KEEPALIVE: - result = KeepaliveFrameFlyweight.payloadOffset(directBuffer, offset); + result = KeepaliveFrameFlyweight.payloadOffset(byteBuf); break; case REQUEST_RESPONSE: case FIRE_AND_FORGET: case REQUEST_STREAM: case REQUEST_CHANNEL: - result = RequestFrameFlyweight.payloadOffset(frameType, directBuffer, offset); + result = RequestFrameFlyweight.payloadOffset(frameType, byteBuf); break; case REQUEST_N: - result = RequestNFrameFlyweight.payloadOffset(directBuffer, offset); + result = RequestNFrameFlyweight.payloadOffset(byteBuf); break; } return result; } - private static int metadataOffset(final DirectBuffer directBuffer, final int offset) { - return payloadOffset(directBuffer, offset); + private static int metadataOffset(final ByteBuf byteBuf) { + return payloadOffset(byteBuf); } - private static int dataOffset(DirectBuffer directBuffer, FrameType frameType, int offset, int frameLength) { - return payloadOffset(directBuffer, offset) + metadataFieldLength(directBuffer, frameType, offset, frameLength); + private static int dataOffset(ByteBuf byteBuf, FrameType frameType, int frameLength) { + return payloadOffset(byteBuf) + metadataFieldLength(byteBuf, frameType, frameLength); } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FramePool.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/FramePool.java deleted file mode 100644 index 6899978e6..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/FramePool.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.frame; - -import io.reactivesocket.Frame; -import org.agrona.MutableDirectBuffer; - -import java.nio.ByteBuffer; - -public interface FramePool -{ - Frame acquireFrame(final int size); - - Frame acquireFrame(final ByteBuffer byteBuffer); - - Frame acquireFrame(final MutableDirectBuffer mutableDirectBuffer); - - MutableDirectBuffer acquireMutableDirectBuffer(final int size); - - MutableDirectBuffer acquireMutableDirectBuffer(final ByteBuffer byteBuffer); - - void release(final Frame frame); - - void release(final MutableDirectBuffer mutableDirectBuffer); -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java index 31969cdaf..c68b6219d 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/KeepaliveFrameFlyweight.java @@ -15,12 +15,9 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; import io.reactivesocket.FrameType; -import org.agrona.BitUtil; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; - -import java.nio.ByteBuffer; +import io.reactivesocket.util.BitUtil; public class KeepaliveFrameFlyweight { public static final int FLAGS_KEEPALIVE_R = 0b00_1000_0000; @@ -35,25 +32,24 @@ public static int computeFrameLength(final int dataLength) { } public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, int flags, - final ByteBuffer data + final ByteBuf data ) { - final int frameLength = computeFrameLength(data.remaining()); + final int frameLength = computeFrameLength(data.readableBytes()); - int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, FrameType.KEEPALIVE, 0); + int length = FrameHeaderFlyweight.encodeFrameHeader(byteBuf, frameLength, flags, FrameType.KEEPALIVE, 0); // We don't support resumability, last position is always zero - mutableDirectBuffer.putLong(length, 0); + byteBuf.setLong(length, 0); length += BitUtil.SIZE_OF_LONG; - length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); + length += FrameHeaderFlyweight.encodeData(byteBuf, length, data); return length; } - public static int payloadOffset(final DirectBuffer directBuffer, final int offset) { - return offset + PAYLOAD_OFFSET; + public static int payloadOffset(final ByteBuf byteBuf) { + return PAYLOAD_OFFSET; } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/LeaseFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/LeaseFrameFlyweight.java index 931dec474..31a75054f 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/LeaseFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/LeaseFrameFlyweight.java @@ -15,13 +15,9 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; import io.reactivesocket.FrameType; -import org.agrona.BitUtil; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import io.reactivesocket.util.BitUtil; public class LeaseFrameFlyweight { private LeaseFrameFlyweight() {} @@ -37,34 +33,33 @@ public static int computeFrameLength(final int metadataLength) { } public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, final int ttl, final int numRequests, - final ByteBuffer metadata + final ByteBuf metadata ) { - final int frameLength = computeFrameLength(metadata.remaining()); + final int frameLength = computeFrameLength(metadata.readableBytes()); - int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, 0, FrameType.LEASE, 0); + int length = FrameHeaderFlyweight.encodeFrameHeader(byteBuf, frameLength, 0, FrameType.LEASE, 0); - mutableDirectBuffer.putInt(offset + TTL_FIELD_OFFSET, ttl, ByteOrder.BIG_ENDIAN); - mutableDirectBuffer.putInt(offset + NUM_REQUESTS_FIELD_OFFSET, numRequests, ByteOrder.BIG_ENDIAN); + byteBuf.setInt(TTL_FIELD_OFFSET, ttl); + byteBuf.setInt(NUM_REQUESTS_FIELD_OFFSET, numRequests); length += BitUtil.SIZE_OF_INT * 2; - length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, FrameType.LEASE, offset, offset + length, metadata); + length += FrameHeaderFlyweight.encodeMetadata(byteBuf, FrameType.LEASE, length, metadata); return length; } - public static int ttl(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + TTL_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int ttl(final ByteBuf byteBuf) { + return byteBuf.getInt(TTL_FIELD_OFFSET); } - public static int numRequests(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + NUM_REQUESTS_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int numRequests(final ByteBuf byteBuf) { + return byteBuf.getInt(NUM_REQUESTS_FIELD_OFFSET); } - public static int payloadOffset(final DirectBuffer directBuffer, final int offset) { - return offset + PAYLOAD_OFFSET; + public static int payloadOffset(final ByteBuf byteBuf) { + return PAYLOAD_OFFSET; } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadBuilder.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadBuilder.java index 62e733f31..c2482afa0 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadBuilder.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadBuilder.java @@ -15,14 +15,13 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.reactivesocket.Frame; import io.reactivesocket.Payload; -import org.agrona.BitUtil; -import org.agrona.MutableDirectBuffer; -import org.agrona.concurrent.UnsafeBuffer; +import io.reactivesocket.util.PayloadImpl; import java.nio.ByteBuffer; -import java.util.Arrays; /** * Builder for appending buffers that grows dataCapacity as necessary. Similar to Aeron's PayloadBuilder. @@ -30,94 +29,42 @@ public class PayloadBuilder { public static final int INITIAL_CAPACITY = Math.max(Frame.DATA_MTU, Frame.METADATA_MTU); - private final MutableDirectBuffer dataMutableDirectBuffer; - private final MutableDirectBuffer metadataMutableDirectBuffer; - - private byte[] dataBuffer; - private byte[] metadataBuffer; - private int dataLimit = 0; - private int metadataLimit = 0; - private int dataCapacity; - private int metadataCapacity; + private final ByteBuf dataBuffer; + private final ByteBuf metadataBuffer; public PayloadBuilder() { - dataCapacity = BitUtil.findNextPositivePowerOfTwo(INITIAL_CAPACITY); - metadataCapacity = BitUtil.findNextPositivePowerOfTwo(INITIAL_CAPACITY); - dataBuffer = new byte[dataCapacity]; - metadataBuffer = new byte[metadataCapacity]; - dataMutableDirectBuffer = new UnsafeBuffer(dataBuffer); - metadataMutableDirectBuffer = new UnsafeBuffer(metadataBuffer); + dataBuffer = Unpooled.directBuffer(INITIAL_CAPACITY); + metadataBuffer = Unpooled.directBuffer(INITIAL_CAPACITY); } public Payload payload() { - return new Payload() { - public ByteBuffer getData() - { - return ByteBuffer.wrap(dataBuffer, 0, dataLimit); - } - - public ByteBuffer getMetadata() { - return ByteBuffer.wrap(metadataBuffer, 0, metadataLimit); - } - }; - } - - public void append(final Payload payload) { - final ByteBuffer payloadMetadata = payload.getMetadata(); - final ByteBuffer payloadData = payload.getData(); - final int metadataLength = payloadMetadata.remaining(); - final int dataLength = payloadData.remaining(); - - ensureMetadataCapacity(metadataLength); - ensureDataCapacity(dataLength); - - metadataMutableDirectBuffer.putBytes(metadataLimit, payloadMetadata, metadataLength); - metadataLimit += metadataLength; - dataMutableDirectBuffer.putBytes(dataLimit, payloadData, dataLength); - dataLimit += dataLength; - } - - private void ensureDataCapacity(final int additionalCapacity) { - final int requiredCapacity = dataLimit + additionalCapacity; - - if (requiredCapacity < 0) { - final String s = String.format("Insufficient data capacity: dataLimit=%d additional=%d", dataLimit, additionalCapacity); - throw new IllegalStateException(s); + final ByteBuffer data; + final ByteBuffer metadata; + if (dataBuffer.readableBytes() > 0) { + data = ByteBuffer.allocateDirect(dataBuffer.readableBytes()); + dataBuffer.readBytes(data); + data.flip(); + } else { + data = Frame.NULL_BYTEBUFFER; } - - if (requiredCapacity > dataCapacity) { - final int newCapacity = findSuitableCapacity(dataCapacity, requiredCapacity); - final byte[] newBuffer = Arrays.copyOf(dataBuffer, newCapacity); - - dataCapacity = newCapacity; - dataBuffer = newBuffer; - dataMutableDirectBuffer.wrap(newBuffer); + if (metadataBuffer.readableBytes() > 0) { + metadata = ByteBuffer.allocateDirect(metadataBuffer.readableBytes()); + metadataBuffer.readBytes(metadata); + metadata.flip(); + } else { + metadata = Frame.NULL_BYTEBUFFER; } + return new PayloadImpl(data, metadata); } - private void ensureMetadataCapacity(final int additionalCapacity) { - final int requiredCapacity = metadataLimit + additionalCapacity; - - if (requiredCapacity < 0) { - final String s = String.format("Insufficient metadata capacity: metadataLimit=%d additional=%d", metadataLimit, additionalCapacity); - throw new IllegalStateException(s); - } - - if (requiredCapacity > metadataCapacity) { - final int newCapacity = findSuitableCapacity(metadataCapacity, requiredCapacity); - final byte[] newBuffer = Arrays.copyOf(metadataBuffer, newCapacity); - - metadataCapacity = newCapacity; - metadataBuffer = newBuffer; - metadataMutableDirectBuffer.wrap(newBuffer); - } - } + public void append(final Frame frame) { + final ByteBuf data = FrameHeaderFlyweight.sliceFrameData(frame.content()); + final ByteBuf metadata = FrameHeaderFlyweight.sliceFrameMetadata(frame.content()); - private static int findSuitableCapacity(int capacity, final int requiredCapacity) { - do { - capacity <<= 1; - } while (capacity < requiredCapacity); + dataBuffer.ensureWritable(data.readableBytes(), true); + metadataBuffer.ensureWritable(metadata.readableBytes(), true); - return capacity; + dataBuffer.writeBytes(data); + metadataBuffer.writeBytes(metadata); } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java index 9cd0cdebf..591f848fb 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadFragmenter.java @@ -15,6 +15,8 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.reactivesocket.Frame; import io.reactivesocket.FrameType; import io.reactivesocket.Payload; @@ -34,8 +36,8 @@ private enum Type { private final int metadataMtu; private final int dataMtu; - private ByteBuffer metadata; - private ByteBuffer data; + private ByteBuf metadata; + private ByteBuf data; private Type type; private int metadataOffset; private int dataOffset; @@ -75,25 +77,25 @@ public Iterator iterator() { } public boolean hasNext() { - return dataOffset < data.remaining() || metadataOffset < metadata.remaining(); + return dataOffset < data.readableBytes() || metadataOffset < metadata.readableBytes(); } public Frame next() { - final int metadataLength = Math.min(metadataMtu, metadata.remaining() - metadataOffset); - final int dataLength = Math.min(dataMtu, data.remaining() - dataOffset); + final int metadataLength = Math.min(metadataMtu, metadata.readableBytes() - metadataOffset); + final int dataLength = Math.min(dataMtu, data.readableBytes() - dataOffset); Frame result = null; - final ByteBuffer metadataBuffer = metadataLength > 0 ? - ByteBufferUtil.preservingSlice(metadata, metadataOffset, metadataOffset + metadataLength) : Frame.NULL_BYTEBUFFER; + final ByteBuf metadataBuffer = metadataLength > 0 ? + metadata.slice(metadataOffset, metadataLength) : Unpooled.EMPTY_BUFFER; - final ByteBuffer dataBuffer = dataLength > 0 ? - ByteBufferUtil.preservingSlice(data, dataOffset, dataOffset + dataLength) : Frame.NULL_BYTEBUFFER; + final ByteBuf dataBuffer = dataLength > 0 ? + data.slice(dataOffset, dataLength) : Unpooled.EMPTY_BUFFER; metadataOffset += metadataLength; dataOffset += dataLength; - final boolean isMoreFollowing = metadataOffset < metadata.remaining() || dataOffset < data.remaining(); + final boolean isMoreFollowing = metadataOffset < metadata.readableBytes() || dataOffset < data.readableBytes(); int flags = 0; if (Type.RESPONSE == type) { @@ -122,8 +124,8 @@ public Frame next() { } private void reset(final int streamId, final Payload payload) { - data = payload.getData(); - metadata = payload.getMetadata(); + data = Unpooled.wrappedBuffer(payload.getData()); + metadata = Unpooled.wrappedBuffer(payload.getMetadata()); metadataOffset = 0; dataOffset = 0; this.streamId = streamId; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java index e1849913f..e87953c29 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java @@ -17,7 +17,8 @@ import io.reactivesocket.Frame; import io.reactivesocket.Payload; -import org.agrona.collections.Int2ObjectHashMap; +import io.reactivesocket.internal.Int2ObjectHashMap; +import io.reactivesocket.util.PayloadImpl; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -48,16 +49,18 @@ public void onNext(Frame frame) { PayloadBuilder payloadBuilder = payloadByStreamId.get(streamId); if (FrameHeaderFlyweight.FLAGS_F != (frame.flags() & FrameHeaderFlyweight.FLAGS_F)) { - Payload deliveryPayload = frame; + final Payload payload; // terminal frame if (null != payloadBuilder) { payloadBuilder.append(frame); - deliveryPayload = payloadBuilder.payload(); + payload = payloadBuilder.payload(); payloadByStreamId.remove(streamId); + } else { + payload = new PayloadImpl(frame); } - child.onNext(deliveryPayload); + child.onNext(payload); } else { if (null == payloadBuilder) { diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java index 64e8ecdcd..5defa27ff 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestFrameFlyweight.java @@ -15,13 +15,9 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; import io.reactivesocket.FrameType; -import org.agrona.BitUtil; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import io.reactivesocket.util.BitUtil; public class RequestFrameFlyweight { @@ -41,56 +37,54 @@ public static int computeFrameLength(final FrameType type, final int metadataLen } public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, final int streamId, int flags, final FrameType type, final int initialRequestN, - final ByteBuffer metadata, - final ByteBuffer data + final ByteBuf metadata, + final ByteBuf data ) { - final int frameLength = computeFrameLength(type, metadata.remaining(), data.remaining()); + final int frameLength = computeFrameLength(type, metadata.readableBytes(), data.readableBytes()); - int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, type, streamId); + int length = FrameHeaderFlyweight.encodeFrameHeader(byteBuf, frameLength, flags, type, streamId); - mutableDirectBuffer.putInt(offset + INITIAL_REQUEST_N_FIELD_OFFSET, initialRequestN, ByteOrder.BIG_ENDIAN); + byteBuf.setInt(INITIAL_REQUEST_N_FIELD_OFFSET, initialRequestN); length += BitUtil.SIZE_OF_INT; - length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, type, offset, offset + length, metadata); - length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); + length += FrameHeaderFlyweight.encodeMetadata(byteBuf, type, length, metadata); + length += FrameHeaderFlyweight.encodeData(byteBuf, length, data); return length; } public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, final int streamId, final int flags, final FrameType type, - final ByteBuffer metadata, - final ByteBuffer data + final ByteBuf metadata, + final ByteBuf data ) { if (type.hasInitialRequestN()) { throw new AssertionError(type + " must not be encoded without initial request N"); } - final int frameLength = computeFrameLength(type, metadata.remaining(), data.remaining()); + final int frameLength = computeFrameLength(type, metadata.readableBytes(), data.readableBytes()); - int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, type, streamId); + int length = FrameHeaderFlyweight.encodeFrameHeader(byteBuf, frameLength, flags, type, streamId); - length += FrameHeaderFlyweight.encodeMetadata(mutableDirectBuffer, type, offset, offset + length, metadata); - length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); + length += FrameHeaderFlyweight.encodeMetadata(byteBuf, type, length, metadata); + length += FrameHeaderFlyweight.encodeData(byteBuf, length, data); return length; } - public static int initialRequestN(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + INITIAL_REQUEST_N_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int initialRequestN(final ByteBuf byteBuf) { + return byteBuf.getInt(INITIAL_REQUEST_N_FIELD_OFFSET); } - public static int payloadOffset(final FrameType type, final DirectBuffer directBuffer, final int offset) { - int result = offset + FrameHeaderFlyweight.FRAME_HEADER_LENGTH; + public static int payloadOffset(final FrameType type, final ByteBuf byteBuf) { + int result = FrameHeaderFlyweight.FRAME_HEADER_LENGTH; if (type.hasInitialRequestN()) { result += BitUtil.SIZE_OF_INT; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestNFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestNFrameFlyweight.java index e1c26216f..9cb383b81 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestNFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/RequestNFrameFlyweight.java @@ -15,12 +15,9 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; import io.reactivesocket.FrameType; -import org.agrona.BitUtil; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; - -import java.nio.ByteOrder; +import io.reactivesocket.util.BitUtil; public class RequestNFrameFlyweight { private RequestNFrameFlyweight() {} @@ -35,25 +32,24 @@ public static int computeFrameLength() { } public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, final int streamId, final int requestN ) { final int frameLength = computeFrameLength(); - int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, 0, FrameType.REQUEST_N, streamId); + int length = FrameHeaderFlyweight.encodeFrameHeader(byteBuf, frameLength, 0, FrameType.REQUEST_N, streamId); - mutableDirectBuffer.putInt(offset + REQUEST_N_FIELD_OFFSET, requestN, ByteOrder.BIG_ENDIAN); + byteBuf.setInt(REQUEST_N_FIELD_OFFSET, requestN); return length + BitUtil.SIZE_OF_INT; } - public static int requestN(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + REQUEST_N_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int requestN(final ByteBuf byteBuf) { + return byteBuf.getInt(REQUEST_N_FIELD_OFFSET); } - public static int payloadOffset(final DirectBuffer directBuffer, final int offset) { - return offset + FrameHeaderFlyweight.FRAME_HEADER_LENGTH + BitUtil.SIZE_OF_INT; + public static int payloadOffset(final ByteBuf byteBuf) { + return FrameHeaderFlyweight.FRAME_HEADER_LENGTH + BitUtil.SIZE_OF_INT; } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java index 54aece7e6..a99c7a925 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/SetupFrameFlyweight.java @@ -15,13 +15,11 @@ */ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.reactivesocket.FrameType; -import org.agrona.BitUtil; -import org.agrona.DirectBuffer; -import org.agrona.MutableDirectBuffer; +import io.reactivesocket.util.BitUtil; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; public class SetupFrameFlyweight { @@ -74,27 +72,25 @@ private static int computeFrameLength( } public static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, int flags, final int keepaliveInterval, final int maxLifetime, final String metadataMimeType, final String dataMimeType, - final ByteBuffer metadata, - final ByteBuffer data + final ByteBuf metadata, + final ByteBuf data ) { if ((flags & FLAGS_RESUME_ENABLE) != 0) { throw new IllegalArgumentException("RESUME_ENABLE not supported"); } return encode( - mutableDirectBuffer, - offset, + byteBuf, flags, keepaliveInterval, maxLifetime, - FrameHeaderFlyweight.NULL_BYTEBUFFER, + Unpooled.EMPTY_BUFFER, metadataMimeType, dataMimeType, metadata, @@ -103,110 +99,109 @@ public static int encode( // Only exposed for testing, other code shouldn't create frames with resumption tokens for now static int encode( - final MutableDirectBuffer mutableDirectBuffer, - final int offset, + final ByteBuf byteBuf, int flags, final int keepaliveInterval, final int maxLifetime, - final ByteBuffer resumeToken, + final ByteBuf resumeToken, final String metadataMimeType, final String dataMimeType, - final ByteBuffer metadata, - final ByteBuffer data + final ByteBuf metadata, + final ByteBuf data ) { - final int frameLength = computeFrameLength(flags, resumeToken.remaining(), metadataMimeType, dataMimeType, metadata.remaining(), data.remaining()); + final int frameLength = computeFrameLength(flags, resumeToken.readableBytes(), metadataMimeType, dataMimeType, metadata.readableBytes(), data.readableBytes()); - int length = FrameHeaderFlyweight.encodeFrameHeader(mutableDirectBuffer, offset, frameLength, flags, FrameType.SETUP, 0); + int length = FrameHeaderFlyweight.encodeFrameHeader(byteBuf, frameLength, flags, FrameType.SETUP, 0); - mutableDirectBuffer.putInt(offset + VERSION_FIELD_OFFSET, CURRENT_VERSION, ByteOrder.BIG_ENDIAN); - mutableDirectBuffer.putInt(offset + KEEPALIVE_INTERVAL_FIELD_OFFSET, keepaliveInterval, ByteOrder.BIG_ENDIAN); - mutableDirectBuffer.putInt(offset + MAX_LIFETIME_FIELD_OFFSET, maxLifetime, ByteOrder.BIG_ENDIAN); + byteBuf.setInt(VERSION_FIELD_OFFSET, CURRENT_VERSION); + byteBuf.setInt(KEEPALIVE_INTERVAL_FIELD_OFFSET, keepaliveInterval); + byteBuf.setInt(MAX_LIFETIME_FIELD_OFFSET, maxLifetime); length += BitUtil.SIZE_OF_INT * 3; if ((flags & FLAGS_RESUME_ENABLE) != 0) { - mutableDirectBuffer.putShort(offset + length, (short) resumeToken.remaining(), ByteOrder.BIG_ENDIAN); + byteBuf.setShort(length, resumeToken.readableBytes()); length += BitUtil.SIZE_OF_SHORT; - int resumeTokenLength = resumeToken.remaining(); - mutableDirectBuffer.putBytes(offset + length, resumeToken, resumeTokenLength); + int resumeTokenLength = resumeToken.readableBytes(); + byteBuf.setBytes(length, resumeToken, resumeTokenLength); length += resumeTokenLength; } - length += putMimeType(mutableDirectBuffer, offset + length, metadataMimeType); - length += putMimeType(mutableDirectBuffer, offset + length, dataMimeType); + length += putMimeType(byteBuf, length, metadataMimeType); + length += putMimeType(byteBuf, length, dataMimeType); length += FrameHeaderFlyweight.encodeMetadata( - mutableDirectBuffer, FrameType.SETUP, offset, offset + length, metadata); - length += FrameHeaderFlyweight.encodeData(mutableDirectBuffer, offset + length, data); + byteBuf, FrameType.SETUP, length, metadata); + length += FrameHeaderFlyweight.encodeData(byteBuf, length, data); return length; } - public static int version(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + VERSION_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int version(final ByteBuf byteBuf) { + return byteBuf.getInt(VERSION_FIELD_OFFSET); } - public static int keepaliveInterval(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + KEEPALIVE_INTERVAL_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int keepaliveInterval(final ByteBuf byteBuf) { + return byteBuf.getInt(KEEPALIVE_INTERVAL_FIELD_OFFSET); } - public static int maxLifetime(final DirectBuffer directBuffer, final int offset) { - return directBuffer.getInt(offset + MAX_LIFETIME_FIELD_OFFSET, ByteOrder.BIG_ENDIAN); + public static int maxLifetime(final ByteBuf byteBuf) { + return byteBuf.getInt(MAX_LIFETIME_FIELD_OFFSET); } - public static String metadataMimeType(final DirectBuffer directBuffer, final int offset) { - final byte[] bytes = getMimeType(directBuffer, offset + metadataMimetypeOffset(directBuffer, offset)); + public static String metadataMimeType(final ByteBuf byteBuf) { + final byte[] bytes = getMimeType(byteBuf, metadataMimetypeOffset(byteBuf)); return new String(bytes, StandardCharsets.UTF_8); } - public static String dataMimeType(final DirectBuffer directBuffer, final int offset) { - int fieldOffset = offset + metadataMimetypeOffset(directBuffer, offset); + public static String dataMimeType(final ByteBuf byteBuf) { + int fieldOffset = metadataMimetypeOffset(byteBuf); - fieldOffset += 1 + directBuffer.getByte(fieldOffset); + fieldOffset += 1 + byteBuf.getByte(fieldOffset); - final byte[] bytes = getMimeType(directBuffer, fieldOffset); + final byte[] bytes = getMimeType(byteBuf, fieldOffset); return new String(bytes, StandardCharsets.UTF_8); } - public static int payloadOffset(final DirectBuffer directBuffer, final int offset) { - int fieldOffset = offset + metadataMimetypeOffset(directBuffer, offset); + public static int payloadOffset(final ByteBuf byteBuf) { + int fieldOffset = metadataMimetypeOffset(byteBuf); - final int metadataMimeTypeLength = directBuffer.getByte(fieldOffset); + final int metadataMimeTypeLength = byteBuf.getByte(fieldOffset); fieldOffset += 1 + metadataMimeTypeLength; - final int dataMimeTypeLength = directBuffer.getByte(fieldOffset); + final int dataMimeTypeLength = byteBuf.getByte(fieldOffset); fieldOffset += 1 + dataMimeTypeLength; return fieldOffset; } - private static int metadataMimetypeOffset(final DirectBuffer directBuffer, final int offset) { - return VARIABLE_DATA_OFFSET + resumeTokenTotalLength(directBuffer, offset); + private static int metadataMimetypeOffset(final ByteBuf byteBuf) { + return VARIABLE_DATA_OFFSET + resumeTokenTotalLength(byteBuf); } - private static int resumeTokenTotalLength(final DirectBuffer directBuffer, final int offset) { - if ((FrameHeaderFlyweight.flags(directBuffer, offset) & FLAGS_RESUME_ENABLE) == 0) { + private static int resumeTokenTotalLength(final ByteBuf byteBuf) { + if ((FrameHeaderFlyweight.flags(byteBuf) & FLAGS_RESUME_ENABLE) == 0) { return 0; } else { - return BitUtil.SIZE_OF_SHORT + directBuffer.getShort(offset + VARIABLE_DATA_OFFSET, ByteOrder.BIG_ENDIAN); + return BitUtil.SIZE_OF_SHORT + byteBuf.getShort(VARIABLE_DATA_OFFSET); } } private static int putMimeType( - final MutableDirectBuffer mutableDirectBuffer, final int fieldOffset, final String mimeType) { + final ByteBuf byteBuf, final int fieldOffset, final String mimeType) { byte[] bytes = mimeType.getBytes(StandardCharsets.UTF_8); - mutableDirectBuffer.putByte(fieldOffset, (byte) bytes.length); - mutableDirectBuffer.putBytes(fieldOffset + 1, bytes); + byteBuf.setByte(fieldOffset, (byte) bytes.length); + byteBuf.setBytes(fieldOffset + 1, bytes); return 1 + bytes.length; } - private static byte[] getMimeType(final DirectBuffer directBuffer, final int fieldOffset) { - final int length = directBuffer.getByte(fieldOffset); + private static byte[] getMimeType(final ByteBuf byteBuf, final int fieldOffset) { + final int length = byteBuf.getByte(fieldOffset); final byte[] bytes = new byte[length]; - directBuffer.getBytes(fieldOffset + 1, bytes); + byteBuf.getBytes(fieldOffset + 1, bytes); return bytes; } } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ThreadLocalFramePool.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ThreadLocalFramePool.java deleted file mode 100644 index 4b0b64523..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ThreadLocalFramePool.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.frame; - -import io.reactivesocket.Frame; -import org.agrona.MutableDirectBuffer; -import org.agrona.concurrent.OneToOneConcurrentArrayQueue; -import org.agrona.concurrent.UnsafeBuffer; - -import java.nio.ByteBuffer; - -public class ThreadLocalFramePool implements FramePool { - private static final int MAX_CAHED_FRAMES_PER_THREAD = 16; - - private static final ThreadLocal> PER_THREAD_FRAME_QUEUE = - ThreadLocal.withInitial(() -> new OneToOneConcurrentArrayQueue<>(MAX_CAHED_FRAMES_PER_THREAD)); - - private static final ThreadLocal> PER_THREAD_DIRECTBUFFER_QUEUE = - ThreadLocal.withInitial(() -> new OneToOneConcurrentArrayQueue<>(MAX_CAHED_FRAMES_PER_THREAD)); - - public Frame acquireFrame(int size) { - final MutableDirectBuffer directBuffer = acquireMutableDirectBuffer(size); - - Frame frame = pollFrame(); - if (null == frame) { - frame = Frame.allocate(directBuffer); - } - - return frame; - } - - public Frame acquireFrame(ByteBuffer byteBuffer) { - return Frame.allocate(new UnsafeBuffer(byteBuffer)); - } - - public void release(Frame frame) - { - PER_THREAD_FRAME_QUEUE.get().offer(frame); - } - - public Frame acquireFrame(MutableDirectBuffer mutableDirectBuffer) { - Frame frame = pollFrame(); - if (null == frame) { - frame = Frame.allocate(mutableDirectBuffer); - } - - return frame; - } - - public MutableDirectBuffer acquireMutableDirectBuffer(ByteBuffer byteBuffer) { - MutableDirectBuffer directBuffer = pollMutableDirectBuffer(); - if (null == directBuffer) { - directBuffer = new UnsafeBuffer(byteBuffer); - } - - return directBuffer; - } - - public MutableDirectBuffer acquireMutableDirectBuffer(int size) { - UnsafeBuffer directBuffer = (UnsafeBuffer)pollMutableDirectBuffer(); - if (null == directBuffer || directBuffer.byteBuffer().capacity() < size) { - directBuffer = new UnsafeBuffer(ByteBuffer.allocate(size)); - } else { - directBuffer.byteBuffer().limit(size).position(0); - } - - return directBuffer; - } - - public void release(MutableDirectBuffer mutableDirectBuffer) { - PER_THREAD_DIRECTBUFFER_QUEUE.get().offer(mutableDirectBuffer); - } - - private Frame pollFrame() - { - return PER_THREAD_FRAME_QUEUE.get().poll(); - } - - private MutableDirectBuffer pollMutableDirectBuffer() { - return PER_THREAD_DIRECTBUFFER_QUEUE.get().poll(); - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ThreadSafeFramePool.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/ThreadSafeFramePool.java deleted file mode 100644 index 35f436555..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/ThreadSafeFramePool.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.frame; - -import io.reactivesocket.Frame; -import org.agrona.MutableDirectBuffer; -import org.agrona.concurrent.OneToOneConcurrentArrayQueue; -import org.agrona.concurrent.UnsafeBuffer; - -import java.nio.ByteBuffer; - -public class ThreadSafeFramePool implements FramePool { - private static final int MAX_CACHED_FRAMES = 16; - - private final OneToOneConcurrentArrayQueue frameQueue; - private final OneToOneConcurrentArrayQueue directBufferQueue; - - public ThreadSafeFramePool() { - this(MAX_CACHED_FRAMES, MAX_CACHED_FRAMES); - } - - public ThreadSafeFramePool(final int frameQueueLength, final int directBufferQueueLength) { - frameQueue = new OneToOneConcurrentArrayQueue<>(frameQueueLength); - directBufferQueue = new OneToOneConcurrentArrayQueue<>(directBufferQueueLength); - } - - public Frame acquireFrame(int size) { - final MutableDirectBuffer directBuffer = acquireMutableDirectBuffer(size); - - Frame frame = pollFrame(); - if (null == frame) { - frame = Frame.allocate(directBuffer); - } - - return frame; - } - - public Frame acquireFrame(ByteBuffer byteBuffer) { - return Frame.allocate(new UnsafeBuffer(byteBuffer)); - } - - public Frame acquireFrame(MutableDirectBuffer mutableDirectBuffer) { - Frame frame = pollFrame(); - if (null == frame) { - frame = Frame.allocate(mutableDirectBuffer); - } - - return frame; - } - - public MutableDirectBuffer acquireMutableDirectBuffer(ByteBuffer byteBuffer) { - MutableDirectBuffer directBuffer = pollMutableDirectBuffer(); - if (null == directBuffer) { - directBuffer = new UnsafeBuffer(byteBuffer); - } - - return directBuffer; - } - - public MutableDirectBuffer acquireMutableDirectBuffer(int size) { - UnsafeBuffer directBuffer = (UnsafeBuffer)pollMutableDirectBuffer(); - if (null == directBuffer || directBuffer.capacity() < size) { - directBuffer = new UnsafeBuffer(ByteBuffer.allocate(size)); - } else { - directBuffer.byteBuffer().limit(size).position(0); - } - - return directBuffer; - } - - public void release(Frame frame) { - synchronized (frameQueue) { - frameQueue.offer(frame); - } - } - - public void release(MutableDirectBuffer mutableDirectBuffer) { - synchronized (directBufferQueue) { - directBufferQueue.offer(mutableDirectBuffer); - } - } - - private Frame pollFrame() { - synchronized (frameQueue) { - return frameQueue.poll(); - } - } - - private MutableDirectBuffer pollMutableDirectBuffer() { - synchronized (directBufferQueue) { - return directBufferQueue.poll(); - } - } -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/UnpooledFrame.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/UnpooledFrame.java deleted file mode 100644 index 4ebb6f1e5..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/UnpooledFrame.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.frame; - -import io.reactivesocket.Frame; -import org.agrona.MutableDirectBuffer; -import org.agrona.concurrent.UnsafeBuffer; - -import java.nio.ByteBuffer; - -/** - * On demand creation for Frames, MutableDirectBuffer backed by ByteBuffers of required capacity - */ -public class UnpooledFrame implements FramePool { - /* - * TODO: have all gneration of UnsafeBuffer and ByteBuffer hidden behind acquire() calls (private for ByteBuffer) - */ - - public Frame acquireFrame(int size) { - return Frame.allocate(new UnsafeBuffer(ByteBuffer.allocate(size))); - } - - public Frame acquireFrame(ByteBuffer byteBuffer) { - return Frame.allocate(new UnsafeBuffer(byteBuffer)); - } - - public void release(Frame frame) {} - - public Frame acquireFrame(MutableDirectBuffer mutableDirectBuffer) { - return Frame.allocate(mutableDirectBuffer); - } - - public MutableDirectBuffer acquireMutableDirectBuffer(ByteBuffer byteBuffer) { - return new UnsafeBuffer(byteBuffer); - } - - public MutableDirectBuffer acquireMutableDirectBuffer(int size) { - return new UnsafeBuffer(ByteBuffer.allocate(size)); - } - - public void release(MutableDirectBuffer mutableDirectBuffer) {} -} diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java index a9d67d470..ce2679607 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java @@ -20,7 +20,7 @@ import io.reactivesocket.Frame; import io.reactivesocket.Plugins; import io.reactivesocket.Plugins.DuplexConnectionInterceptor.Type; -import org.agrona.BitUtil; +import io.reactivesocket.util.BitUtil; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/CollectionUtil.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/CollectionUtil.java new file mode 100644 index 000000000..51e5fac87 --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/CollectionUtil.java @@ -0,0 +1,133 @@ +/* + * Copyright 2014-2017 Real Logic Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.internal; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.ToIntFunction; + +/** + * Utility functions for collection objects. + */ +public class CollectionUtil +{ + /** + * A getOrDefault that doesn't create garbage if its suppler is non-capturing. + * + * @param map to perform the lookup on. + * @param key on which the lookup is done. + * @param supplier of the default value if one is not found. + * @param type of the key + * @param type of the value + * @return the value if found or a new default which as been added to the map. + */ + public static V getOrDefault(final Map map, final K key, final Function supplier) + { + V value = map.get(key); + if (value == null) + { + value = supplier.apply(key); + map.put(key, value); + } + + return value; + } + + /** + * Garbage free sum function. + * + * Note: the list must implement {@link java.util.RandomAccess} to be efficient. + * + * @param values the list of input values + * @param function function that map each value to an int + * @param the value to add up + * @return the sum of all the int values returned for each member of the list. + */ + public static int sum(final List values, final ToIntFunction function) + { + int total = 0; + + final int size = values.size(); + for (int i = 0; i < size; i++) + { + final V value = values.get(i); + total += function.applyAsInt(value); + } + + return total; + } + + /** + * Validate that a load factor is greater than 0 and less than 1.0. + * + * @param loadFactor to be validated. + */ + public static void validateLoadFactor(final float loadFactor) + { + if (loadFactor <= 0 || loadFactor >= 1.0) + { + throw new IllegalArgumentException("Load factors must be > 0.0 and < 1.0"); + } + } + + /** + * Validate that a number is a power of two. + * + * @param value to be validated. + */ + public static void validatePositivePowerOfTwo(final int value) + { + if (value > 0 && 1 == (value & (value - 1))) + { + throw new IllegalStateException("Value must be a positive power of two"); + } + } + + /** + * Remove element from a list if it matches a predicate. + * + * Note: the list must implement {@link java.util.RandomAccess} to be efficient. + * + * @param values to be iterated over. + * @param predicate to test the value against + * @param type of the value. + * @return the number of items remove. + */ + public static int removeIf(final List values, final Predicate predicate) + { + int size = values.size(); + int total = 0; + + for (int i = 0; i < size; ) + { + final T value = values.get(i); + if (predicate.test(value)) + { + values.remove(i); + total++; + size--; + } + else + { + i++; + } + } + + return total; + } +} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/Hashing.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/Hashing.java new file mode 100644 index 000000000..e1a4d3606 --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/Hashing.java @@ -0,0 +1,118 @@ +/* + * Copyright 2014-2017 Real Logic Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.internal; + +/** + * Hashing functions for applying to integers. + */ +public class Hashing +{ + /** + * Generate a hash for an int value. This is a no op. + * + * @param value to be hashed. + * @return the hashed value. + */ + public static int hash(final int value) + { + return value; + } + + /** + * Generate a hash for an long value. This is a no op. + * + * @param value to be hashed. + * @return the hashed value. + */ + public static int hash(final long value) + { + return (int)value ^ (int)(value >>> 32); + } + + /** + * Generate a hash for a int value. + * + * @param value to be hashed. + * @param mask mask to be applied that must be a power of 2 - 1. + * @return the hash of the value. + */ + public static int hash(final int value, final int mask) + { + final int hash = value ^ (value >>> 16); + + return hash & mask; + } + + /** + * Generate a hash for a long value. + * + * @param value to be hashed. + * @param mask mask to be applied that must be a power of 2 - 1. + * @return the hash of the value. + */ + public static int hash(final long value, final int mask) + { + int hash = (int)value ^ (int)(value >>> 32); + hash = hash ^ (hash >>> 16); + + return hash & mask; + } + + /** + * Generate a hash for two ints. + * + * @param valueOne to be hashed. + * @param valueTwo to be hashed. + * @param mask mask to be applied that must be a power of 2 - 1. + * @return a hash of the values. + */ + public static int hash(final int valueOne, final int valueTwo, final int mask) + { + int hash = valueOne ^ valueTwo; + hash = hash ^ (hash >>> 16); + + return hash & mask; + } + + /** + * Generate an even hash for a int value. + * + * @param value to be hashed. + * @param mask mask to be applied that must be a power of 2 - 1. + * @return the hash of the value which is always even. + */ + public static int evenHash(final int value, final int mask) + { + final int hash = (value << 1) - (value << 8); + + return hash & mask; + } + + /** + * Generate an even hash for a long value. + * + * @param value to be hashed. + * @param mask mask to be applied that must be a power of 2 - 1. + * @return the hash of the value which is always even. + */ + public static int evenHash(final long value, final int mask) + { + int hash = (int)value ^ (int)(value >>> 32); + hash = (hash << 1) - (hash << 8); + + return hash & mask; + } +} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/Int2ObjectHashMap.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/Int2ObjectHashMap.java new file mode 100644 index 000000000..dc3396ac0 --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/Int2ObjectHashMap.java @@ -0,0 +1,768 @@ +/* + * Copyright 2014-2017 Real Logic Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.internal; + +import io.reactivesocket.util.BitUtil; + +import java.util.*; +import java.util.function.IntFunction; + +import static java.util.Objects.requireNonNull; + +/** + * {@link java.util.Map} implementation specialised for int keys using open addressing and + * linear probing for cache efficient access. + * + * @param type of values stored in the {@link java.util.Map} + */ +public class Int2ObjectHashMap + implements Map +{ + private final float loadFactor; + private int resizeThreshold; + private int size; + + private int[] keys; + private Object[] values; + + private final ValueCollection valueCollection; + private final KeySet keySet; + private final EntrySet entrySet; + + public Int2ObjectHashMap() + { + this(8, 0.67f); + } + + /** + * Construct a new map allowing a configuration for initial capacity and load factor. + * + * @param initialCapacity for the backing array + * @param loadFactor limit for resizing on puts + */ + public Int2ObjectHashMap( + final int initialCapacity, + final float loadFactor) + { + CollectionUtil.validateLoadFactor(loadFactor); + + this.loadFactor = loadFactor; + final int capacity = BitUtil.findNextPositivePowerOfTwo(initialCapacity); + resizeThreshold = (int)(capacity * loadFactor); + + keys = new int[capacity]; + values = new Object[capacity]; + + // Cached to avoid allocation. + valueCollection = new ValueCollection<>(); + keySet = new KeySet(); + entrySet = new EntrySet<>(); + } + + /** + * Get the load factor beyond which the map will increase size. + * + * @return load factor for when the map should increase size. + */ + public float loadFactor() + { + return loadFactor; + } + + /** + * Get the total capacity for the map to which the load factor with be a fraction of. + * + * @return the total capacity for the map. + */ + public int capacity() + { + return values.length; + } + + /** + * Get the actual threshold which when reached the map resize. + * This is a function of the current capacity and load factor. + * + * @return the threshold when the map will resize. + */ + public int resizeThreshold() + { + return resizeThreshold; + } + + /** + * {@inheritDoc} + */ + public int size() + { + return size; + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() + { + return 0 == size; + } + + /** + * {@inheritDoc} + */ + public boolean containsKey(final Object key) + { + return containsKey(((Integer)key).intValue()); + } + + /** + * Overloaded version of {@link Map#containsKey(Object)} that takes a primitive int key. + * + * @param key for indexing the {@link Map} + * @return true if the key is found otherwise false. + */ + public boolean containsKey(final int key) + { + final int mask = values.length - 1; + int index = Hashing.hash(key, mask); + + boolean found = false; + while (null != values[index]) + { + if (key == keys[index]) + { + found = true; + break; + } + + index = ++index & mask; + } + + return found; + } + + /** + * {@inheritDoc} + */ + public boolean containsValue(final Object value) + { + boolean found = false; + if (null != value) + { + for (final Object v : values) + { + if (value.equals(v)) + { + found = true; + break; + } + } + } + + return found; + } + + /** + * {@inheritDoc} + */ + public V get(final Object key) + { + return get(((Integer)key).intValue()); + } + + /** + * Overloaded version of {@link Map#get(Object)} that takes a primitive int key. + * + * @param key for indexing the {@link Map} + * @return the value if found otherwise null + */ + @SuppressWarnings("unchecked") + public V get(final int key) + { + final int mask = values.length - 1; + int index = Hashing.hash(key, mask); + + Object value; + while (null != (value = values[index])) + { + if (key == keys[index]) + { + break; + } + + index = ++index & mask; + } + + return (V)value; + } + + /** + * Get a value for a given key, or if it does not exist then default the value via a + * {@link java.util.function.IntFunction} and put it in the map. + * + * Primitive specialized version of {@link java.util.Map#computeIfAbsent}. + * + * @param key to search on. + * @param mappingFunction to provide a value if the get returns null. + * @return the value if found otherwise the default. + */ + public V computeIfAbsent(final int key, final IntFunction mappingFunction) + { + V value = get(key); + if (value == null) + { + value = mappingFunction.apply(key); + if (value != null) + { + put(key, value); + } + } + + return value; + } + + /** + * {@inheritDoc} + */ + public V put(final Integer key, final V value) + { + return put(key.intValue(), value); + } + + /** + * Overloaded version of {@link Map#put(Object, Object)} that takes a primitive int key. + * + * @param key for indexing the {@link Map} + * @param value to be inserted in the {@link Map} + * @return the previous value if found otherwise null + */ + @SuppressWarnings("unchecked") + public V put(final int key, final V value) + { + requireNonNull(value, "Value cannot be null"); + + V oldValue = null; + final int mask = values.length - 1; + int index = Hashing.hash(key, mask); + + while (null != values[index]) + { + if (key == keys[index]) + { + oldValue = (V)values[index]; + break; + } + + index = ++index & mask; + } + + if (null == oldValue) + { + ++size; + keys[index] = key; + } + + values[index] = value; + + if (size > resizeThreshold) + { + increaseCapacity(); + } + + return oldValue; + } + + /** + * {@inheritDoc} + */ + public V remove(final Object key) + { + return remove(((Integer)key).intValue()); + } + + /** + * Overloaded version of {@link Map#remove(Object)} that takes a primitive int key. + * + * @param key for indexing the {@link Map} + * @return the value if found otherwise null + */ + @SuppressWarnings("unchecked") + public V remove(final int key) + { + final int mask = values.length - 1; + int index = Hashing.hash(key, mask); + + Object value; + while (null != (value = values[index])) + { + if (key == keys[index]) + { + values[index] = null; + --size; + + compactChain(index); + break; + } + + index = ++index & mask; + } + + return (V)value; + } + + /** + * {@inheritDoc} + */ + public void clear() + { + size = 0; + Arrays.fill(values, null); + } + + /** + * Compact the {@link Map} backing arrays by rehashing with a capacity just larger than current size + * and giving consideration to the load factor. + */ + public void compact() + { + final int idealCapacity = (int)Math.round(size() * (1.0d / loadFactor)); + rehash(BitUtil.findNextPositivePowerOfTwo(idealCapacity)); + } + + /** + * {@inheritDoc} + */ + public void putAll(final Map map) + { + for (final Entry entry : map.entrySet()) + { + put(entry.getKey(), entry.getValue()); + } + } + + /** + * {@inheritDoc} + */ + public KeySet keySet() + { + return keySet; + } + + /** + * {@inheritDoc} + */ + public Collection values() + { + return valueCollection; + } + + /** + * {@inheritDoc} + */ + public Set> entrySet() + { + return entrySet; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + final StringBuilder sb = new StringBuilder(); + sb.append('{'); + + for (final Entry entry : entrySet()) + { + sb.append(entry.getKey().intValue()); + sb.append('='); + sb.append(entry.getValue()); + sb.append(", "); + } + + if (sb.length() > 1) + { + sb.setLength(sb.length() - 2); + } + + sb.append('}'); + + return sb.toString(); + } + + /** + * Primitive specialised version of {@link #replace(Object, Object)} + * + * @param key key with which the specified value is associated + * @param value value to be associated with the specified key + * @return the previous value associated with the specified key, or + * {@code null} if there was no mapping for the key. + */ + public V replace(final int key, final V value) + { + V curValue = get(key); + if (curValue != null) + { + curValue = put(key, value); + } + + return curValue; + } + + /** + * Primitive specialised version of {@link #replace(Object, Object, Object)} + * + * @param key key with which the specified value is associated + * @param oldValue value expected to be associated with the specified key + * @param newValue value to be associated with the specified key + * @return {@code true} if the value was replaced + */ + public boolean replace(final int key, final V oldValue, final V newValue) + { + final Object curValue = get(key); + if (curValue == null || !Objects.equals(curValue, oldValue)) + { + return false; + } + + put(key, newValue); + + return true; + } + + private void increaseCapacity() + { + final int newCapacity = values.length << 1; + if (newCapacity < 0) + { + throw new IllegalStateException("Max capacity reached at size=" + size); + } + + rehash(newCapacity); + } + + private void rehash(final int newCapacity) + { + final int mask = newCapacity - 1; + resizeThreshold = (int)(newCapacity * loadFactor); + + final int[] tempKeys = new int[newCapacity]; + final Object[] tempValues = new Object[newCapacity]; + + for (int i = 0, size = values.length; i < size; i++) + { + final Object value = values[i]; + if (null != value) + { + final int key = keys[i]; + int newHash = Hashing.hash(key, mask); + while (null != tempValues[newHash]) + { + newHash = ++newHash & mask; + } + + tempKeys[newHash] = key; + tempValues[newHash] = value; + } + } + + keys = tempKeys; + values = tempValues; + } + + @SuppressWarnings("FinalParameters") + private void compactChain(int deleteIndex) + { + final int mask = values.length - 1; + int index = deleteIndex; + while (true) + { + index = ++index & mask; + if (null == values[index]) + { + break; + } + + final int hash = Hashing.hash(keys[index], mask); + + if ((index < hash && (hash <= deleteIndex || deleteIndex <= index)) || + (hash <= deleteIndex && deleteIndex <= index)) + { + keys[deleteIndex] = keys[index]; + values[deleteIndex] = values[index]; + + values[index] = null; + deleteIndex = index; + } + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // Internal Sets and Collections + /////////////////////////////////////////////////////////////////////////////////////////////// + + public class KeySet extends AbstractSet + { + private final KeyIterator iterator = new KeyIterator(); + + public int size() + { + return Int2ObjectHashMap.this.size(); + } + + public boolean contains(final Object o) + { + return Int2ObjectHashMap.this.containsKey(o); + } + + public boolean contains(final int key) + { + return Int2ObjectHashMap.this.containsKey(key); + } + + public KeyIterator iterator() + { + iterator.reset(); + + return iterator; + } + + public boolean remove(final Object o) + { + return null != Int2ObjectHashMap.this.remove(o); + } + + public boolean remove(final int key) + { + return null != Int2ObjectHashMap.this.remove(key); + } + + public void clear() + { + Int2ObjectHashMap.this.clear(); + } + } + + private class ValueCollection extends AbstractCollection + { + private final ValueIterator iterator = new ValueIterator(); + + public int size() + { + return Int2ObjectHashMap.this.size(); + } + + public boolean contains(final Object o) + { + return Int2ObjectHashMap.this.containsValue(o); + } + + public ValueIterator iterator() + { + iterator.reset(); + + return iterator; + } + + public void clear() + { + Int2ObjectHashMap.this.clear(); + } + } + + private class EntrySet extends AbstractSet> + { + private final EntryIterator iterator = new EntryIterator(); + + public int size() + { + return Int2ObjectHashMap.this.size(); + } + + public Iterator> iterator() + { + iterator.reset(); + + return iterator; + } + + public void clear() + { + Int2ObjectHashMap.this.clear(); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + // Iterators + /////////////////////////////////////////////////////////////////////////////////////////////// + + abstract class AbstractIterator implements Iterator + { + private int posCounter; + private int stopCounter; + private boolean isPositionValid = false; + protected int[] keys; + protected Object[] values; + + protected AbstractIterator() + { + reset(); + } + + protected int position() + { + return posCounter & values.length - 1; + } + + public boolean hasNext() + { + final int mask = values.length - 1; + boolean hasNext = false; + for (int i = posCounter - 1; i >= stopCounter; i--) + { + final int index = i & mask; + if (null != values[index]) + { + hasNext = true; + break; + } + } + + return hasNext; + } + + protected void findNext() + { + final int mask = values.length - 1; + isPositionValid = false; + + for (int i = posCounter - 1; i >= stopCounter; i--) + { + final int index = i & mask; + if (null != values[index]) + { + posCounter = i; + isPositionValid = true; + return; + } + } + + throw new NoSuchElementException(); + } + + public abstract T next(); + + public void remove() + { + if (isPositionValid) + { + final int position = position(); + values[position] = null; + --size; + + compactChain(position); + + isPositionValid = false; + } + else + { + throw new IllegalStateException(); + } + } + + void reset() + { + keys = Int2ObjectHashMap.this.keys; + values = Int2ObjectHashMap.this.values; + final int capacity = values.length; + + int i = capacity; + if (null != values[capacity - 1]) + { + i = 0; + for (int size = capacity; i < size; i++) + { + if (null == values[i]) + { + break; + } + } + } + + stopCounter = i; + posCounter = i + capacity; + } + } + + public class ValueIterator extends AbstractIterator + { + @SuppressWarnings("unchecked") + public T next() + { + findNext(); + + return (T)values[position()]; + } + } + + public class KeyIterator extends AbstractIterator + { + public Integer next() + { + return nextInt(); + } + + public int nextInt() + { + findNext(); + + return keys[position()]; + } + } + + @SuppressWarnings("unchecked") + public class EntryIterator + extends AbstractIterator> + implements Entry + { + public Entry next() + { + findNext(); + + return this; + } + + public Integer getKey() + { + return keys[position()]; + } + + public V getValue() + { + return (V)values[position()]; + } + + public V setValue(final V value) + { + requireNonNull(value); + + final int pos = position(); + final Object oldValue = values[pos]; + values[pos] = value; + + return (V)oldValue; + } + } +} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java index 8a6f05baa..b4e4c0e1e 100755 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/LimitableRequestPublisher.java @@ -1,6 +1,5 @@ package io.reactivesocket.internal; -import io.reactivesocket.Payload; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -13,7 +12,7 @@ /** * */ -public class LimitableRequestPublisher extends Flux implements Subscription { +public class LimitableRequestPublisher extends Flux implements Subscription { private final Publisher source; private final AtomicBoolean canceled; @@ -31,7 +30,7 @@ private LimitableRequestPublisher(Publisher source) { this.canceled = new AtomicBoolean(); } - public static LimitableRequestPublisher wrap(Publisher source) { + public static LimitableRequestPublisher wrap(Publisher source) { return new LimitableRequestPublisher<>(source); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/FairLeaseDistributor.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/FairLeaseDistributor.java index bb62e30cb..99d0bd5ff 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/FairLeaseDistributor.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/FairLeaseDistributor.java @@ -16,7 +16,7 @@ package io.reactivesocket.lease; -import io.reactivesocket.Frame; +import io.netty.buffer.Unpooled; import io.reactivesocket.ReactiveSocket; import org.reactivestreams.Subscription; import reactor.core.Disposable; @@ -109,14 +109,14 @@ private void distribute(int permits) { // it would be more fair to randomized the distribution of extra int extra = permits - budget * recipients; - Lease budgetLease = new LeaseImpl(budget, leaseTTLMillis, Frame.NULL_BYTEBUFFER); + Lease budgetLease = new LeaseImpl(budget, leaseTTLMillis); for (Consumer recipient: activeRecipients) { Lease leaseToSend = budgetLease; int n = budget; if (extra > 0) { n += 1; extra -= 1; - leaseToSend = new LeaseImpl(n, leaseTTLMillis, Frame.NULL_BYTEBUFFER); + leaseToSend = new LeaseImpl(n, leaseTTLMillis); } recipient.accept(leaseToSend); } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/Lease.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/Lease.java index 633dadce6..8ec4e1e49 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/Lease.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/Lease.java @@ -49,7 +49,7 @@ public interface Lease { * * @return Metadata for the lease. */ - ByteBuffer metadata(); + ByteBuffer getMetadata(); /** * Checks if the lease is expired now. diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/lease/LeaseImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/lease/LeaseImpl.java index d7bfdbcc4..cf731ca86 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/lease/LeaseImpl.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/lease/LeaseImpl.java @@ -27,6 +27,10 @@ public final class LeaseImpl implements Lease { private final long expiry; private final ByteBuffer metadata; + public LeaseImpl(int allowedRequests, int ttl) { + this(allowedRequests, ttl, null); + } + public LeaseImpl(int allowedRequests, int ttl, ByteBuffer metadata) { this.allowedRequests = allowedRequests; this.ttl = ttl; @@ -54,7 +58,7 @@ public long expiry() { } @Override - public ByteBuffer metadata() { + public ByteBuffer getMetadata() { return metadata; } diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java b/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java index acd1e977e..d71c2c535 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/server/DefaultReactiveSocketServer.java @@ -74,6 +74,7 @@ public StartedServer start(SocketAcceptor acceptor) { setup.willClientHonorLease(), Throwable::printStackTrace); receiver.start(); + setupFrame.release(); return connection.onClose(); }); diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/util/BitUtil.java b/reactivesocket-core/src/main/java/io/reactivesocket/util/BitUtil.java new file mode 100644 index 000000000..fecfff889 --- /dev/null +++ b/reactivesocket-core/src/main/java/io/reactivesocket/util/BitUtil.java @@ -0,0 +1,335 @@ +/* + * Copyright 2014-2017 Real Logic Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.util; + +import java.nio.charset.Charset; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Miscellaneous useful functions for dealing with low level bits and bytes. + */ +public class BitUtil +{ + /** + * Size of a byte in bytes + */ + public static final int SIZE_OF_BYTE = 1; + + /** + * Size of a boolean in bytes + */ + public static final int SIZE_OF_BOOLEAN = 1; + + /** + * Size of a char in bytes + */ + public static final int SIZE_OF_CHAR = 2; + + /** + * Size of a short in bytes + */ + public static final int SIZE_OF_SHORT = 2; + + /** + * Size of an int in bytes + */ + public static final int SIZE_OF_INT = 4; + + /** + * Size of a a float in bytes + */ + public static final int SIZE_OF_FLOAT = 4; + + /** + * Size of a long in bytes + */ + public static final int SIZE_OF_LONG = 8; + + /** + * Size of a double in bytes + */ + public static final int SIZE_OF_DOUBLE = 8; + + /** + * Length of the data blocks used by the CPU cache sub-system in bytes. + */ + public static final int CACHE_LINE_LENGTH = 64; + + private static final byte[] HEX_DIGIT_TABLE = + { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + private static final byte[] FROM_HEX_DIGIT_TABLE; + + static + { + FROM_HEX_DIGIT_TABLE = new byte[128]; + FROM_HEX_DIGIT_TABLE['0'] = 0x00; + FROM_HEX_DIGIT_TABLE['1'] = 0x01; + FROM_HEX_DIGIT_TABLE['2'] = 0x02; + FROM_HEX_DIGIT_TABLE['3'] = 0x03; + FROM_HEX_DIGIT_TABLE['4'] = 0x04; + FROM_HEX_DIGIT_TABLE['5'] = 0x05; + FROM_HEX_DIGIT_TABLE['6'] = 0x06; + FROM_HEX_DIGIT_TABLE['7'] = 0x07; + FROM_HEX_DIGIT_TABLE['8'] = 0x08; + FROM_HEX_DIGIT_TABLE['9'] = 0x09; + FROM_HEX_DIGIT_TABLE['a'] = 0x0a; + FROM_HEX_DIGIT_TABLE['A'] = 0x0a; + FROM_HEX_DIGIT_TABLE['b'] = 0x0b; + FROM_HEX_DIGIT_TABLE['B'] = 0x0b; + FROM_HEX_DIGIT_TABLE['c'] = 0x0c; + FROM_HEX_DIGIT_TABLE['C'] = 0x0c; + FROM_HEX_DIGIT_TABLE['d'] = 0x0d; + FROM_HEX_DIGIT_TABLE['D'] = 0x0d; + FROM_HEX_DIGIT_TABLE['e'] = 0x0e; + FROM_HEX_DIGIT_TABLE['E'] = 0x0e; + FROM_HEX_DIGIT_TABLE['f'] = 0x0f; + FROM_HEX_DIGIT_TABLE['F'] = 0x0f; + } + + private static final int LAST_DIGIT_MASK = 0b1; + + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + + /** + * Fast method of finding the next power of 2 greater than or equal to the supplied value. + * + * If the value is <= 0 then 1 will be returned. + * + * This method is not suitable for {@link Integer#MIN_VALUE} or numbers greater than 2^30. + * + * @param value from which to search for next power of 2 + * @return The next power of 2 or the value itself if it is a power of 2 + */ + public static int findNextPositivePowerOfTwo(final int value) + { + return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); + } + + /** + * Align a value to the next multiple up of alignment. + * If the value equals an alignment multiple then it is returned unchanged. + * + * This method executes without branching. This code is designed to be use in the fast path and should not + * be used with negative numbers. Negative numbers will result in undefined behaviour. + * + * @param value to be aligned up. + * @param alignment to be used. + * @return the value aligned to the next boundary. + */ + public static int align(final int value, final int alignment) + { + return (value + (alignment - 1)) & ~(alignment - 1); + } + + /** + * Generate a byte array from the hex representation of the given byte array. + * + * @param buffer to convert from a hex representation (in Big Endian) + * @return new byte array that is decimal representation of the passed array + */ + public static byte[] fromHexByteArray(final byte[] buffer) + { + final byte[] outputBuffer = new byte[buffer.length >> 1]; + + for (int i = 0; i < buffer.length; i += 2) + { + outputBuffer[i >> 1] = + (byte)((FROM_HEX_DIGIT_TABLE[buffer[i]] << 4) | FROM_HEX_DIGIT_TABLE[buffer[i + 1]]); + } + + return outputBuffer; + } + + /** + * Generate a byte array that is a hex representation of a given byte array. + * + * @param buffer to convert to a hex representation + * @return new byte array that is hex representation (in Big Endian) of the passed array + */ + public static byte[] toHexByteArray(final byte[] buffer) + { + return toHexByteArray(buffer, 0, buffer.length); + } + + /** + * Generate a byte array that is a hex representation of a given byte array. + * + * @param buffer to convert to a hex representation + * @param offset the offset into the buffer + * @param length the number of bytes to convert + * @return new byte array that is hex representation (in Big Endian) of the passed array + */ + public static byte[] toHexByteArray(final byte[] buffer, final int offset, final int length) + { + final byte[] outputBuffer = new byte[length << 1]; + + for (int i = 0; i < (length << 1); i += 2) + { + final byte b = buffer[offset + (i >> 1)]; + + outputBuffer[i] = HEX_DIGIT_TABLE[(b >> 4) & 0x0F]; + outputBuffer[i + 1] = HEX_DIGIT_TABLE[b & 0x0F]; + } + + return outputBuffer; + } + + /** + * Generate a byte array from a string that is the hex representation of the given byte array. + * + * @param string to convert from a hex representation (in Big Endian) + * @return new byte array holding the decimal representation of the passed array + */ + public static byte[] fromHex(final String string) + { + return fromHexByteArray(string.getBytes(UTF8_CHARSET)); + } + + /** + * Generate a string that is the hex representation of a given byte array. + * + * @param buffer to convert to a hex representation + * @param offset the offset into the buffer + * @param length the number of bytes to convert + * @return new String holding the hex representation (in Big Endian) of the passed array + */ + public static String toHex(final byte[] buffer, final int offset, final int length) + { + return new String(toHexByteArray(buffer, offset, length), UTF8_CHARSET); + } + + /** + * Generate a string that is the hex representation of a given byte array. + * + * @param buffer to convert to a hex representation + * @return new String holding the hex representation (in Big Endian) of the passed array + */ + public static String toHex(final byte[] buffer) + { + return new String(toHexByteArray(buffer), UTF8_CHARSET); + } + + /** + * Is a number even. + * + * @param value to check. + * @return true if the number is even otherwise false. + */ + public static boolean isEven(final int value) + { + return (value & LAST_DIGIT_MASK) == 0; + } + + /** + * Is a value a positive power of two. + * + * @param value to be checked. + * @return true if the number is a positive power of two otherwise false. + */ + public static boolean isPowerOfTwo(final int value) + { + return value > 0 && ((value & (~value + 1)) == value); + } + + /** + * Cycles indices of an array one at a time in a forward fashion + * + * @param current value to be incremented. + * @param max value for the cycle. + * @return the next value, or zero if max is reached. + */ + public static int next(final int current, final int max) + { + int next = current + 1; + if (next == max) + { + next = 0; + } + + return next; + } + + /** + * Cycles indices of an array one at a time in a backwards fashion + * + * @param current value to be decremented. + * @param max value of the cycle. + * @return the next value, or max - 1 if current is zero + */ + public static int previous(final int current, final int max) + { + if (0 == current) + { + return max - 1; + } + + return current - 1; + } + + /** + * Calculate the shift value to scale a number based on how refs are compressed or not. + * + * @param scale of the number reported by Unsafe. + * @return how many times the number needs to be shifted to the left. + */ + public static int calculateShiftForScale(final int scale) + { + if (4 == scale) + { + return 2; + } + else if (8 == scale) + { + return 3; + } + else + { + throw new IllegalArgumentException("Unknown pointer size"); + } + } + + /** + * Generate a randomized integer over [{@link Integer#MIN_VALUE}, {@link Integer#MAX_VALUE}] suitable for + * use as an Aeron Id. + * + * @return randomized integer suitable as an Id. + */ + public static int generateRandomisedId() + { + return ThreadLocalRandom.current().nextInt(); + } + + /** + * Is an address aligned on a boundary. + * + * @param address to be tested. + * @param alignment boundary the address is tested against. + * @return true if the address is on the aligned boundary otherwise false. + * @throws IllegalArgumentException if the alignment is not a power of 2` + */ + public static boolean isAligned(final long address, final int alignment) + { + if (!BitUtil.isPowerOfTwo(alignment)) + { + throw new IllegalArgumentException("Alignment must be a power of 2: alignment=" + alignment); + } + + return (address & (alignment - 1)) == 0; + } +} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/util/PayloadImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/util/PayloadImpl.java index 43a54f2c9..6fcaaf95d 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/util/PayloadImpl.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/util/PayloadImpl.java @@ -25,37 +25,35 @@ /** * An implementation of {@link Payload}. This implementation is not thread-safe, and hence any method can not be * invoked concurrently. - * - *

Reusability

- * - * By default, an instance is reusable, i.e. everytime {@link #getData()} or {@link #getMetadata()} is invoked, the - * position of the returned buffer is reset to the start of data in the buffer. For creating an instance for single-use, - * {@link #PayloadImpl(ByteBuffer, ByteBuffer, boolean)} must be used. */ public class PayloadImpl implements Payload { - public static final Payload EMPTY = new PayloadImpl(Frame.NULL_BYTEBUFFER, Frame.NULL_BYTEBUFFER); + public static final PayloadImpl EMPTY = new PayloadImpl(Frame.NULL_BYTEBUFFER, Frame.NULL_BYTEBUFFER, false); private final ByteBuffer data; + private final ByteBuffer metadata; private final int dataStartPosition; private final int metadataStartPosition; private final boolean reusable; - private final ByteBuffer metadata; + + public PayloadImpl(Frame frame) { + this(frame.getData(), frame.getMetadata()); + } public PayloadImpl(String data) { - this(data, (String) null); + this(data, Charset.defaultCharset()); } public PayloadImpl(String data, String metadata) { - this(fromString(data), fromString(metadata)); + this(data, Charset.defaultCharset(), metadata, Charset.defaultCharset()); } - public PayloadImpl(String data, Charset charset) { - this(fromString(data, charset), fromString(null)); + public PayloadImpl(String data, Charset dataCharset) { + this(dataCharset.encode(data), Frame.NULL_BYTEBUFFER); } public PayloadImpl(String data, Charset dataCharset, String metadata, Charset metaDataCharset) { - this(fromString(data, dataCharset), fromString(metadata, metaDataCharset)); + this(dataCharset.encode(data), metaDataCharset.encode(metadata)); } public PayloadImpl(byte[] data) { @@ -74,21 +72,12 @@ public PayloadImpl(ByteBuffer data, ByteBuffer metadata) { this(data, metadata, true); } - /** - * New instance where every invocation of {@link #getMetadata()} and {@link #getData()} will reset the position of - * the buffer to the position when it is created, if and only if, {@code reusable} is set to {@code true}. - * - * @param data Buffer for data. - * @param metadata Buffer for metadata. - * @param reusable {@code true} if the buffer position is to be reset on every invocation of {@link #getData()} and - * {@link #getMetadata()}. - */ public PayloadImpl(ByteBuffer data, ByteBuffer metadata, boolean reusable) { this.data = data; + this.metadata = metadata; this.reusable = reusable; - this.metadata = null == metadata ? Frame.NULL_BYTEBUFFER : metadata; - dataStartPosition = reusable ? this.data.position() : 0; - metadataStartPosition = reusable ? this.metadata.position() : 0; + this.dataStartPosition = reusable ? this.data.position() : 0; + this.metadataStartPosition = reusable ? this.metadata.position() : 0; } @Override @@ -106,12 +95,4 @@ public ByteBuffer getMetadata() { } return metadata; } - - private static ByteBuffer fromString(String data) { - return fromString(data, Charset.defaultCharset()); - } - - private static ByteBuffer fromString(String data, Charset charset) { - return data == null ? Frame.NULL_BYTEBUFFER : ByteBuffer.wrap(data.getBytes(charset)); - } } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java deleted file mode 100644 index b90fbae5d..000000000 --- a/reactivesocket-core/src/test/java/io/reactivesocket/FrameTest.java +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket; - -import io.reactivesocket.exceptions.Exceptions; -import io.reactivesocket.exceptions.RejectedException; -import io.reactivesocket.frame.SetupFrameFlyweight; -import org.agrona.concurrent.UnsafeBuffer; -import org.junit.Test; -import org.junit.experimental.theories.DataPoint; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; - -import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; - -import static io.reactivesocket.frame.ErrorFrameFlyweight.REJECTED; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; - -@RunWith(Theories.class) -public class FrameTest -{ - private static Payload createPayload(final ByteBuffer metadata, final ByteBuffer data) - { - return new Payload() - { - public ByteBuffer getData() - { - return data; - } - - public ByteBuffer getMetadata() - { - return metadata; - } - }; - } - - @DataPoint - public static final int ZERO_OFFSET = 0; - - @DataPoint - public static final int NON_ZERO_OFFSET = 127; - - private static final UnsafeBuffer reusableMutableDirectBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); - private static final Frame reusableFrame = Frame.allocate(reusableMutableDirectBuffer); - - @Test - public void testWriteThenRead() { - final ByteBuffer helloBuffer = TestUtil.byteBufferFromUtf8String("hello"); - final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, helloBuffer); - - Frame f = Frame.Request.from(1, FrameType.REQUEST_RESPONSE, payload, 1); - - assertEquals("hello", TestUtil.byteToString(f.getData())); - assertEquals(FrameType.REQUEST_RESPONSE, f.getType()); - assertEquals(1, f.getStreamId()); - - ByteBuffer b = f.getByteBuffer(); - - Frame f2 = Frame.from(b); - assertEquals("hello", TestUtil.byteToString(f2.getData())); - assertEquals(FrameType.REQUEST_RESPONSE, f2.getType()); - assertEquals(1, f2.getStreamId()); - } - - @Test - public void testWrapMessage() { - final ByteBuffer helloBuffer = TestUtil.byteBufferFromUtf8String("hello"); - final ByteBuffer doneBuffer = TestUtil.byteBufferFromUtf8String("done"); - final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, helloBuffer); - - Frame f = Frame.Request.from(1, FrameType.REQUEST_RESPONSE, payload, 1); - - f.wrap(2, FrameType.NEXT_COMPLETE, doneBuffer); - assertEquals("done", TestUtil.byteToString(f.getData())); - assertEquals(FrameType.NEXT_COMPLETE, f.getType()); - assertEquals(2, f.getStreamId()); - } - - @Test - public void testWrapBytes() { - final ByteBuffer helloBuffer = TestUtil.byteBufferFromUtf8String("hello"); - final ByteBuffer anotherBuffer = TestUtil.byteBufferFromUtf8String("another"); - final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, helloBuffer); - final Payload anotherPayload = createPayload(Frame.NULL_BYTEBUFFER, anotherBuffer); - - Frame f = Frame.Request.from(1, FrameType.REQUEST_RESPONSE, payload, 1); - Frame f2 = Frame.PayloadFrame.from(20, FrameType.NEXT_COMPLETE, anotherPayload); - - ByteBuffer b = f2.getByteBuffer(); - f.wrap(b, 0); - - assertEquals("another", TestUtil.byteToString(f.getData())); - assertEquals(FrameType.NEXT_COMPLETE, f.getType()); - assertEquals(20, f.getStreamId()); - } - - @Test - @Theory - public void shouldReturnCorrectDataPlusMetadataForRequestResponse(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("request data"); - final ByteBuffer requestMetadata = TestUtil.byteBufferFromUtf8String("request metadata"); - final Payload payload = createPayload(requestMetadata, requestData); - - Frame encodedFrame = Frame.Request.from(1, FrameType.REQUEST_RESPONSE, payload, 1); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(FrameType.REQUEST_RESPONSE, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - assertEquals("request data", TestUtil.byteToString(reusableFrame.getData())); - assertEquals("request metadata", TestUtil.byteToString(reusableFrame.getMetadata())); - } - - @Test - @Theory - public void shouldReturnCorrectDataPlusMetadataForFireAndForget(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("request data"); - final ByteBuffer requestMetadata = TestUtil.byteBufferFromUtf8String("request metadata"); - final Payload payload = createPayload(requestMetadata, requestData); - - Frame encodedFrame = Frame.Request.from(1, FrameType.FIRE_AND_FORGET, payload, 1); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("request data", TestUtil.byteToString(reusableFrame.getData())); - assertEquals("request metadata", TestUtil.byteToString(reusableFrame.getMetadata())); - assertEquals(FrameType.FIRE_AND_FORGET, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - } - - @Test - @Theory - public void shouldReturnCorrectDataPlusMetadataForRequestStream(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("request data"); - final ByteBuffer requestMetadata = TestUtil.byteBufferFromUtf8String("request metadata"); - final Payload payload = createPayload(requestMetadata, requestData); - - Frame encodedFrame = Frame.Request.from(1, FrameType.REQUEST_STREAM, payload, 128); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("request data", TestUtil.byteToString(reusableFrame.getData())); - assertEquals("request metadata", TestUtil.byteToString(reusableFrame.getMetadata())); - assertEquals(FrameType.REQUEST_STREAM, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - assertEquals(128, Frame.Request.initialRequestN(reusableFrame)); - } - - @Test - @Theory - public void shouldReturnCorrectDataPlusMetadataForResponse(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("response data"); - final ByteBuffer requestMetadata = TestUtil.byteBufferFromUtf8String("response metadata"); - final Payload payload = createPayload(requestMetadata, requestData); - - Frame encodedFrame = Frame.PayloadFrame.from(1, FrameType.NEXT, payload); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("response data", TestUtil.byteToString(reusableFrame.getData())); - assertEquals("response metadata", TestUtil.byteToString(reusableFrame.getMetadata())); - assertEquals(FrameType.NEXT, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - } - - @Test - @Theory - public void shouldReturnCorrectDataWithoutMetadataForRequestResponse(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("request data"); - final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, requestData); - - Frame encodedFrame = Frame.Request.from(1, FrameType.REQUEST_RESPONSE, payload, 1); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("request data", TestUtil.byteToString(reusableFrame.getData())); - - final ByteBuffer metadataBuffer = reusableFrame.getMetadata(); - assertEquals(0, metadataBuffer.remaining()); - assertEquals(FrameType.REQUEST_RESPONSE, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - } - - @Test - @Theory - public void shouldReturnCorrectDataWithoutMetadataForFireAndForget(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("request data"); - final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, requestData); - - Frame encodedFrame = Frame.Request.from(1, FrameType.FIRE_AND_FORGET, payload, 1); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("request data", TestUtil.byteToString(reusableFrame.getData())); - - final ByteBuffer metadataBuffer = reusableFrame.getMetadata(); - assertEquals(0, metadataBuffer.remaining()); - assertEquals(FrameType.FIRE_AND_FORGET, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - } - - @Test - @Theory - public void shouldReturnCorrectDataWithoutMetadataForRequestStream(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("request data"); - final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, requestData); - - Frame encodedFrame = Frame.Request.from(1, FrameType.REQUEST_STREAM, payload, 128); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("request data", TestUtil.byteToString(reusableFrame.getData())); - - final ByteBuffer metadataBuffer = reusableFrame.getMetadata(); - assertEquals(0, metadataBuffer.remaining()); - assertEquals(FrameType.REQUEST_STREAM, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - assertEquals(128, Frame.Request.initialRequestN(reusableFrame)); - } - - @Test - @Theory - public void shouldReturnCorrectDataWithoutMetadataForResponse(final int offset) - { - final ByteBuffer requestData = TestUtil.byteBufferFromUtf8String("response data"); - final Payload payload = createPayload(Frame.NULL_BYTEBUFFER, requestData); - - Frame encodedFrame = Frame.PayloadFrame.from(1, FrameType.NEXT, payload); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals("response data", TestUtil.byteToString(reusableFrame.getData())); - - final ByteBuffer metadataBuffer = reusableFrame.getMetadata(); - assertEquals(0, metadataBuffer.remaining()); - assertEquals(FrameType.NEXT, reusableFrame.getType()); - assertEquals(1, reusableFrame.getStreamId()); - } - - @Test - @Theory - public void shouldReturnCorrectDataPlusMetadataForSetup(final int offset) - { - final int flags = SetupFrameFlyweight.FLAGS_WILL_HONOR_LEASE | SetupFrameFlyweight.FLAGS_STRICT_INTERPRETATION; - final int version = SetupFrameFlyweight.CURRENT_VERSION; - final int keepaliveInterval = 1001; - final int maxLifetime = keepaliveInterval * 5; - final String metadataMimeType = "application/json"; - final String dataMimeType = "application/cbor"; - final ByteBuffer setupData = TestUtil.byteBufferFromUtf8String("setup data"); - final ByteBuffer setupMetadata = TestUtil.byteBufferFromUtf8String("setup metadata"); - - Frame encodedFrame = Frame.Setup.from(flags, keepaliveInterval, maxLifetime, metadataMimeType, dataMimeType, new Payload() - { - public ByteBuffer getData() - { - return setupData; - } - - public ByteBuffer getMetadata() - { - return setupMetadata; - } - }); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(FrameType.SETUP, reusableFrame.getType()); - assertEquals(flags, Frame.Setup.getFlags(reusableFrame)); - assertEquals(version, Frame.Setup.version(reusableFrame)); - assertEquals(keepaliveInterval, Frame.Setup.keepaliveInterval(reusableFrame)); - assertEquals(maxLifetime, Frame.Setup.maxLifetime(reusableFrame)); - assertEquals(metadataMimeType, Frame.Setup.metadataMimeType(reusableFrame)); - assertEquals(dataMimeType, Frame.Setup.dataMimeType(reusableFrame)); - assertEquals("setup data", TestUtil.byteToString(reusableFrame.getData())); - assertEquals("setup metadata", TestUtil.byteToString(reusableFrame.getMetadata())); - } - - @Test - @Theory - public void shouldReturnCorrectDataWithoutMetadataForSetup(final int offset) - { - final int flags = SetupFrameFlyweight.FLAGS_WILL_HONOR_LEASE | SetupFrameFlyweight.FLAGS_STRICT_INTERPRETATION; - final int version = SetupFrameFlyweight.CURRENT_VERSION; - final int keepaliveInterval = 1001; - final int maxLifetime = keepaliveInterval * 5; - final String metadataMimeType = "application/json"; - final String dataMimeType = "application/cbor"; - final ByteBuffer setupData = TestUtil.byteBufferFromUtf8String("setup data"); - - Frame encodedFrame = Frame.Setup.from(flags, keepaliveInterval, maxLifetime, metadataMimeType, dataMimeType, new Payload() - { - public ByteBuffer getData() - { - return setupData; - } - - public ByteBuffer getMetadata() - { - return Frame.NULL_BYTEBUFFER; - } - }); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(FrameType.SETUP, reusableFrame.getType()); - assertEquals(flags, Frame.Setup.getFlags(reusableFrame)); - assertEquals(version, Frame.Setup.version(reusableFrame)); - assertEquals(keepaliveInterval, Frame.Setup.keepaliveInterval(reusableFrame)); - assertEquals(maxLifetime, Frame.Setup.maxLifetime(reusableFrame)); - assertEquals(metadataMimeType, Frame.Setup.metadataMimeType(reusableFrame)); - assertEquals(dataMimeType, Frame.Setup.dataMimeType(reusableFrame)); - assertEquals("setup data", TestUtil.byteToString(reusableFrame.getData())); - assertEquals(Frame.NULL_BYTEBUFFER, reusableFrame.getMetadata()); - } - - @Test - @Theory - public void shouldFormCorrectlyWithoutDataNorMetadataForSetup(final int offset) - { - final int flags = SetupFrameFlyweight.FLAGS_WILL_HONOR_LEASE | SetupFrameFlyweight.FLAGS_STRICT_INTERPRETATION; - final int version = SetupFrameFlyweight.CURRENT_VERSION; - final int keepaliveInterval = 1001; - final int maxLifetime = keepaliveInterval * 5; - final String metadataMimeType = "application/json"; - final String dataMimeType = "application/cbor"; - - Frame encodedFrame = Frame.Setup.from(flags, keepaliveInterval, maxLifetime, metadataMimeType, dataMimeType, new Payload() - { - public ByteBuffer getData() - { - return Frame.NULL_BYTEBUFFER; - } - - public ByteBuffer getMetadata() - { - return Frame.NULL_BYTEBUFFER; - } - }); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(FrameType.SETUP, reusableFrame.getType()); - assertEquals(flags, Frame.Setup.getFlags(reusableFrame)); - assertEquals(version, Frame.Setup.version(reusableFrame)); - assertEquals(keepaliveInterval, Frame.Setup.keepaliveInterval(reusableFrame)); - assertEquals(maxLifetime, Frame.Setup.maxLifetime(reusableFrame)); - assertEquals(metadataMimeType, Frame.Setup.metadataMimeType(reusableFrame)); - assertEquals(dataMimeType, Frame.Setup.dataMimeType(reusableFrame)); - assertEquals(Frame.NULL_BYTEBUFFER, reusableFrame.getData()); - assertEquals(Frame.NULL_BYTEBUFFER, reusableFrame.getMetadata()); - } - - @Test - @Theory - public void shouldReturnCorrectDataPlusMetadataForError(final int offset) - { - final int streamId = 24; - final Throwable exception = new RejectedException("test"); - final String data = "error data"; - final String metadata = "error metadata"; - final ByteBuffer dataByteBuffer = ByteBuffer.wrap(data.getBytes(UTF_8)); - final ByteBuffer metadataByteBuffer = ByteBuffer.wrap(metadata.getBytes(UTF_8)); - - Frame encodedFrame = Frame.Error.from(streamId, exception, metadataByteBuffer, dataByteBuffer); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(FrameType.ERROR, reusableFrame.getType()); - assertEquals(REJECTED, Frame.Error.errorCode(reusableFrame)); - assertEquals(data, TestUtil.byteToString(reusableFrame.getData())); - assertEquals(metadata, TestUtil.byteToString(reusableFrame.getMetadata())); - } - - @Test - @Theory - public void shouldReturnCorrectDataWithThrowableForError(final int offset) - { - final int errorCode = 42; - final String metadata = "my metadata"; - final String exMessage = "exception message"; - - Frame encodedFrame = Frame.Error.from( - errorCode, - new Exception(exMessage), - TestUtil.byteBufferFromUtf8String(metadata) - ); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(FrameType.ERROR, reusableFrame.getType()); - assertEquals(exMessage, TestUtil.byteToString(reusableFrame.getData())); - assertEquals(TestUtil.byteBufferFromUtf8String(metadata), reusableFrame.getMetadata()); - - final Throwable throwable = Exceptions.from(encodedFrame); - assertEquals(exMessage, throwable.getMessage()); - } - - @Test - @Theory - public void shouldReturnCorrectDataWithoutMetadataForError(final int offset) - { - final int errorCode = 42; - final String metadata = "metadata"; - final String data = "error data"; - - Frame encodedFrame = Frame.Error.from( - errorCode, - new Exception("my exception"), - TestUtil.byteBufferFromUtf8String(metadata), - TestUtil.byteBufferFromUtf8String(data) - ); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(FrameType.ERROR, reusableFrame.getType()); - assertEquals(data, TestUtil.byteToString(reusableFrame.getData())); - assertEquals(metadata, TestUtil.byteToString(reusableFrame.getMetadata())); - } - - @Test - @Theory - public void shouldFormCorrectlyForRequestN(final int offset) - { - final int n = 128; - final Frame encodedFrame = Frame.RequestN.from(1, n); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(FrameType.REQUEST_N, reusableFrame.getType()); - assertEquals(n, Frame.RequestN.requestN(reusableFrame)); - assertEquals(Frame.NULL_BYTEBUFFER, reusableFrame.getData()); - assertEquals(Frame.NULL_BYTEBUFFER, reusableFrame.getMetadata()); - } - - @Test - @Theory - public void shouldFormCorrectlyWithoutMetadataForLease(final int offset) - { - final int ttl = (int)TimeUnit.SECONDS.toMillis(8); - final int numberOfRequests = 16; - final Frame encodedFrame = Frame.Lease.from(ttl, numberOfRequests, Frame.NULL_BYTEBUFFER); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(0, reusableFrame.getStreamId()); - assertEquals(FrameType.LEASE, reusableFrame.getType()); - assertEquals(ttl, Frame.Lease.ttl(reusableFrame)); - assertEquals(numberOfRequests, Frame.Lease.numberOfRequests(reusableFrame)); - assertEquals(Frame.NULL_BYTEBUFFER, reusableFrame.getData()); - assertEquals(Frame.NULL_BYTEBUFFER, reusableFrame.getMetadata()); - } - - @Test - @Theory - public void shouldFormCorrectlyWithMetadataForLease(final int offset) - { - final int ttl = (int)TimeUnit.SECONDS.toMillis(8); - final int numberOfRequests = 16; - final ByteBuffer leaseMetadata = TestUtil.byteBufferFromUtf8String("lease metadata"); - - final Frame encodedFrame = Frame.Lease.from(ttl, numberOfRequests, leaseMetadata); - TestUtil.copyFrame(reusableMutableDirectBuffer, offset, encodedFrame); - reusableFrame.wrap(reusableMutableDirectBuffer, offset); - - assertEquals(0, reusableFrame.getStreamId()); - assertEquals(FrameType.LEASE, reusableFrame.getType()); - assertEquals(ttl, Frame.Lease.ttl(reusableFrame)); - assertEquals(numberOfRequests, Frame.Lease.numberOfRequests(reusableFrame)); - assertEquals(Frame.NULL_BYTEBUFFER, reusableFrame.getData()); - assertEquals("lease metadata", TestUtil.byteToString(reusableFrame.getMetadata())); - } -} diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java index 02da818ef..820128681 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/ServerReactiveSocketTest.java @@ -16,6 +16,7 @@ package io.reactivesocket; +import io.netty.buffer.Unpooled; import io.reactivesocket.test.util.TestDuplexConnection; import io.reactivesocket.util.PayloadImpl; import io.reactivex.subscribers.TestSubscriber; @@ -40,7 +41,7 @@ public class ServerReactiveSocketTest { @Test(timeout = 2000) public void testHandleKeepAlive() throws Exception { - rule.connection.addToReceivedBuffer(Frame.Keepalive.from(Frame.NULL_BYTEBUFFER, true)); + rule.connection.addToReceivedBuffer(Frame.Keepalive.from(Unpooled.EMPTY_BUFFER, true)); Frame sent = rule.connection.awaitSend(); assertThat("Unexpected frame sent.", sent.getType(), is(FrameType.KEEPALIVE)); /*Keep alive ack must not have respond flag else, it will result in infinite ping-pong of keep alive frames.*/ diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java b/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java index 2bf81253d..f388de38c 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/TestUtil.java @@ -15,7 +15,7 @@ */ package io.reactivesocket; -import org.agrona.MutableDirectBuffer; +import io.reactivesocket.util.PayloadImpl; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -28,23 +28,12 @@ private TestUtil() {} public static Frame utf8EncodedRequestFrame(final int streamId, final FrameType type, final String data, final int initialRequestN) { - return Frame.Request.from(streamId, type, new Payload() - { - public ByteBuffer getData() - { - return byteBufferFromUtf8String(data); - } - - public ByteBuffer getMetadata() - { - return Frame.NULL_BYTEBUFFER; - } - }, initialRequestN); + return Frame.Request.from(streamId, type, new PayloadImpl(data), initialRequestN); } public static Frame utf8EncodedResponseFrame(final int streamId, final FrameType type, final String data) { - return Frame.PayloadFrame.from(streamId, type, utf8EncodedPayload(data, null)); + return Frame.PayloadFrame.from(streamId, type, new PayloadImpl(data)); } public static Frame utf8EncodedErrorFrame(final int streamId, final String data) @@ -59,77 +48,11 @@ public static Payload utf8EncodedPayload(final String data, final String metadat public static String byteToString(final ByteBuffer byteBuffer) { - final byte[] bytes = new byte[byteBuffer.remaining()]; - byteBuffer.get(bytes); - return new String(bytes, StandardCharsets.UTF_8); + return StandardCharsets.UTF_8.decode(byteBuffer).toString(); } public static ByteBuffer byteBufferFromUtf8String(final String data) { - final byte[] bytes = data.getBytes(StandardCharsets.UTF_8); - return ByteBuffer.wrap(bytes); + return StandardCharsets.UTF_8.encode(data); } - - public static void copyFrame(final MutableDirectBuffer dst, final int offset, final Frame frame) - { - dst.putBytes(offset, frame.getByteBuffer(), frame.offset(), frame.length()); - } - - public static String bytesToHex(ByteBuffer buffer) - { - char[] hexChars = new char[buffer.limit() * 2]; - for ( int j = 0; j < buffer.limit(); j++ ) { - int v = buffer.get(j) & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); - } - - private static class PayloadImpl implements Payload // some JDK shoutout - { - private ByteBuffer data; - private ByteBuffer metadata; - - public PayloadImpl(final String data, final String metadata) - { - if (null == data) - { - this.data = ByteBuffer.allocate(0); - } - else - { - this.data = byteBufferFromUtf8String(data); - } - - if (null == metadata) - { - this.metadata = ByteBuffer.allocate(0); - } - else - { - this.metadata = byteBufferFromUtf8String(metadata); - } - } - - public boolean equals(Object obj) - { - System.out.println("equals: " + obj); - final Payload rhs = (Payload)obj; - - return (TestUtil.byteToString(data).equals(TestUtil.byteToString(rhs.getData()))) && - (TestUtil.byteToString(metadata).equals(TestUtil.byteToString(rhs.getMetadata()))); - } - - public ByteBuffer getData() - { - return data; - } - - public ByteBuffer getMetadata() - { - return metadata; - } - } - } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/client/SetupProviderImplTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/client/SetupProviderImplTest.java index b332976ec..a0b316f37 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/client/SetupProviderImplTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/client/SetupProviderImplTest.java @@ -20,6 +20,7 @@ import io.reactivesocket.Frame.Setup; import io.reactivesocket.FrameType; import io.reactivesocket.ReactiveSocket; +import io.reactivesocket.TestUtil; import io.reactivesocket.lease.DefaultLeaseEnforcingSocket; import io.reactivesocket.lease.DefaultLeaseHonoringSocket; import io.reactivesocket.lease.FairLeaseDistributor; @@ -30,7 +31,6 @@ import reactor.core.publisher.Mono; import java.nio.ByteBuffer; -import java.nio.charset.Charset; import static io.reactivesocket.client.SetupProvider.*; import static org.hamcrest.MatcherAssert.*; @@ -44,8 +44,8 @@ public void testSetup() throws Exception { SetupProvider setupProvider = new SetupProviderImpl(setup, reactiveSocket -> new DefaultLeaseHonoringSocket(reactiveSocket), KeepAliveProvider.never(), Throwable::printStackTrace); - ByteBuffer dataBuffer = ByteBuffer.wrap("hello".getBytes(Charset.defaultCharset())); - ByteBuffer metaDataBuffer = ByteBuffer.wrap("helloMeta".getBytes(Charset.defaultCharset())); + ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String("hello"); + ByteBuffer metaDataBuffer = TestUtil.byteBufferFromUtf8String("helloMeta"); PayloadImpl setupPayload = new PayloadImpl(dataBuffer, metaDataBuffer); setupProvider = setupProvider.setupPayload(setupPayload); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java index 580d64474..ad2211504 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/FrameHeaderFlyweightTest.java @@ -1,107 +1,104 @@ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; import io.reactivesocket.FrameType; -import org.agrona.BitUtil; -import org.agrona.concurrent.UnsafeBuffer; +import io.reactivesocket.util.BitUtil; import org.junit.Test; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import static io.reactivesocket.TestUtil.bytesToHex; import static io.reactivesocket.frame.FrameHeaderFlyweight.FRAME_HEADER_LENGTH; -import static io.reactivesocket.frame.FrameHeaderFlyweight.NULL_BYTEBUFFER; import static org.junit.Assert.*; public class FrameHeaderFlyweightTest { // Taken from spec private static final int FRAME_MAX_SIZE = 16_777_215; - private final UnsafeBuffer directBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); + private final ByteBuf byteBuf = Unpooled.buffer(1024); @Test public void headerSize() { int frameLength = 123456; - FrameHeaderFlyweight.encodeFrameHeader(directBuffer, 0, frameLength, 0, FrameType.SETUP, 0); - assertEquals(frameLength, FrameHeaderFlyweight.frameLength(directBuffer, 0, frameLength)); + FrameHeaderFlyweight.encodeFrameHeader(byteBuf, frameLength, 0, FrameType.SETUP, 0); + assertEquals(frameLength, FrameHeaderFlyweight.frameLength(byteBuf)); } @Test public void headerSizeMax() { int frameLength = FRAME_MAX_SIZE; - FrameHeaderFlyweight.encodeFrameHeader(directBuffer, 0, frameLength, 0, FrameType.SETUP, 0); - assertEquals(frameLength, FrameHeaderFlyweight.frameLength(directBuffer, 0, frameLength)); + FrameHeaderFlyweight.encodeFrameHeader(byteBuf, frameLength, 0, FrameType.SETUP, 0); + assertEquals(frameLength, FrameHeaderFlyweight.frameLength(byteBuf)); } @Test(expected = IllegalArgumentException.class) public void headerSizeTooLarge() { - FrameHeaderFlyweight.encodeFrameHeader(directBuffer, 0, FRAME_MAX_SIZE + 1, 0, FrameType.SETUP, 0); + FrameHeaderFlyweight.encodeFrameHeader(byteBuf, FRAME_MAX_SIZE + 1, 0, FrameType.SETUP, 0); } @Test public void frameLength() { - int length = FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.SETUP, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + int length = FrameHeaderFlyweight.encode(byteBuf, 0, 0, FrameType.SETUP, Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER); assertEquals(length, 9); // 72 bits } @Test public void metadataLength() { - ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); - FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.SETUP, metadata, NULL_BYTEBUFFER); - assertEquals(4, FrameHeaderFlyweight.decodeMetadataLength(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + ByteBuf metadata = Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4}); + FrameHeaderFlyweight.encode(byteBuf, 0, 0, FrameType.SETUP, metadata, Unpooled.EMPTY_BUFFER); + assertEquals(4, FrameHeaderFlyweight.decodeMetadataLength(byteBuf, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); } @Test public void dataLength() { - ByteBuffer data = ByteBuffer.wrap(new byte[]{1, 2, 3, 4, 5}); - int length = FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.SETUP, NULL_BYTEBUFFER, data); - assertEquals(5, FrameHeaderFlyweight.dataLength(directBuffer, FrameType.SETUP, 0, length, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + ByteBuf data = Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4, 5}); + int length = FrameHeaderFlyweight.encode(byteBuf, 0, 0, FrameType.SETUP, Unpooled.EMPTY_BUFFER, data); + assertEquals(5, FrameHeaderFlyweight.dataLength(byteBuf, FrameType.SETUP, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); } @Test public void metadataSlice() { - ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); - FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.REQUEST_RESPONSE, metadata, NULL_BYTEBUFFER); - metadata.rewind(); + ByteBuf metadata = Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4}); + FrameHeaderFlyweight.encode(byteBuf, 0, 0, FrameType.REQUEST_RESPONSE, metadata, Unpooled.EMPTY_BUFFER); + metadata.resetReaderIndex(); - assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(byteBuf)); } @Test public void dataSlice() { - ByteBuffer data = ByteBuffer.wrap(new byte[]{1, 2, 3, 4, 5}); - FrameHeaderFlyweight.encode(directBuffer, 0, 0, 0, FrameType.REQUEST_RESPONSE, NULL_BYTEBUFFER, data); - data.rewind(); + ByteBuf data = Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4, 5}); + FrameHeaderFlyweight.encode(byteBuf, 0, 0, FrameType.REQUEST_RESPONSE, Unpooled.EMPTY_BUFFER, data); + data.resetReaderIndex(); - assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + assertEquals(data, FrameHeaderFlyweight.sliceFrameData(byteBuf)); } @Test public void streamId() { int streamId = 1234; - FrameHeaderFlyweight.encode(directBuffer, 0, streamId, 0, FrameType.SETUP, NULL_BYTEBUFFER, NULL_BYTEBUFFER); - assertEquals(streamId, FrameHeaderFlyweight.streamId(directBuffer, 0)); + FrameHeaderFlyweight.encode(byteBuf, streamId, 0, FrameType.SETUP, Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER); + assertEquals(streamId, FrameHeaderFlyweight.streamId(byteBuf)); } @Test public void typeAndFlag() { FrameType frameType = FrameType.FIRE_AND_FORGET; int flags = 0b1110110111; - FrameHeaderFlyweight.encode(directBuffer, 0, 0, flags, frameType, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + FrameHeaderFlyweight.encode(byteBuf, 0, flags, frameType, Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER); - assertEquals(flags, FrameHeaderFlyweight.flags(directBuffer, 0)); - assertEquals(frameType, FrameHeaderFlyweight.frameType(directBuffer, 0)); + assertEquals(flags, FrameHeaderFlyweight.flags(byteBuf)); + assertEquals(frameType, FrameHeaderFlyweight.frameType(byteBuf)); } @Test public void typeAndFlagTruncated() { FrameType frameType = FrameType.SETUP; int flags = 0b11110110111; // 1 bit too many - FrameHeaderFlyweight.encode(directBuffer, 0, 0, flags, FrameType.SETUP, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + FrameHeaderFlyweight.encode(byteBuf, 0, flags, FrameType.SETUP, Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER); - assertNotEquals(flags, FrameHeaderFlyweight.flags(directBuffer, 0)); - assertEquals(flags & 0b0000_0011_1111_1111, FrameHeaderFlyweight.flags(directBuffer, 0)); - assertEquals(frameType, FrameHeaderFlyweight.frameType(directBuffer, 0)); + assertNotEquals(flags, FrameHeaderFlyweight.flags(byteBuf)); + assertEquals(flags & 0b0000_0011_1111_1111, FrameHeaderFlyweight.flags(byteBuf)); + assertEquals(frameType, FrameHeaderFlyweight.frameType(byteBuf)); } @Test @@ -129,25 +126,25 @@ public void missingMetadataLength() { @Test public void wireFormat() { - UnsafeBuffer expectedMutable = new UnsafeBuffer(ByteBuffer.allocate(1024)); + ByteBuf expectedBuffer = Unpooled.buffer(1024); int currentIndex = 0; // frame length int frameLength = FrameHeaderFlyweight.FRAME_HEADER_LENGTH - FrameHeaderFlyweight.FRAME_LENGTH_SIZE; - expectedMutable.putInt(currentIndex, frameLength << 8, ByteOrder.BIG_ENDIAN); + expectedBuffer.setInt(currentIndex, frameLength << 8); currentIndex += 3; // stream id - expectedMutable.putInt(currentIndex, 5, ByteOrder.BIG_ENDIAN); + expectedBuffer.setInt(currentIndex, 5); currentIndex += BitUtil.SIZE_OF_INT; // flags and frame type - expectedMutable.putShort(currentIndex, (short) 0b001010_0001100000, ByteOrder.BIG_ENDIAN); + expectedBuffer.setShort(currentIndex, (short) 0b001010_0001100000); currentIndex += BitUtil.SIZE_OF_SHORT; FrameType frameType = FrameType.NEXT_COMPLETE; - FrameHeaderFlyweight.encode(directBuffer, 0, 5, 0, frameType, NULL_BYTEBUFFER, NULL_BYTEBUFFER); + FrameHeaderFlyweight.encode(byteBuf, 5, 0, frameType, Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER); - ByteBuffer expected = ByteBufferUtil.preservingSlice(expectedMutable.byteBuffer(), 0, currentIndex); - ByteBuffer actual = ByteBufferUtil.preservingSlice(directBuffer.byteBuffer(), 0, FRAME_HEADER_LENGTH); + ByteBuf expected = expectedBuffer.slice(0, currentIndex); + ByteBuf actual = byteBuf.slice(0, FRAME_HEADER_LENGTH); - assertEquals(bytesToHex(expected), bytesToHex(actual)); + assertEquals(ByteBufUtil.hexDump(expected), ByteBufUtil.hexDump(actual)); } } \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java index 338738de4..0fe1d76c7 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/KeepaliveFrameFlyweightTest.java @@ -1,22 +1,21 @@ package io.reactivesocket.frame; -import org.agrona.concurrent.UnsafeBuffer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import org.junit.Test; -import java.nio.ByteBuffer; - import static org.junit.Assert.*; public class KeepaliveFrameFlyweightTest { - private final UnsafeBuffer directBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); + private final ByteBuf byteBuf = Unpooled.buffer(1024); @Test public void canReadData() { - ByteBuffer data = ByteBuffer.wrap(new byte[]{5, 4, 3}); - int length = KeepaliveFrameFlyweight.encode(directBuffer, 0, KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R, data); - data.rewind(); + ByteBuf data = Unpooled.wrappedBuffer(new byte[]{5, 4, 3}); + int length = KeepaliveFrameFlyweight.encode(byteBuf, KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R, data); + data.resetReaderIndex(); - assertEquals(KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R, FrameHeaderFlyweight.flags(directBuffer, 0) & KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R); - assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, length)); + assertEquals(KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R, FrameHeaderFlyweight.flags(byteBuf) & KeepaliveFrameFlyweight.FLAGS_KEEPALIVE_R); + assertEquals(data, FrameHeaderFlyweight.sliceFrameData(byteBuf)); } } \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/LeaseFrameFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/LeaseFrameFlyweightTest.java index 91fcde0c7..f55fc4dbb 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/LeaseFrameFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/LeaseFrameFlyweightTest.java @@ -1,19 +1,18 @@ package io.reactivesocket.frame; -import org.agrona.concurrent.UnsafeBuffer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import org.junit.Test; -import java.nio.ByteBuffer; - import static org.junit.Assert.*; public class LeaseFrameFlyweightTest { - private final UnsafeBuffer directBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); + private final ByteBuf byteBuf = Unpooled.buffer(1024); @Test public void size() { - ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); - int length = LeaseFrameFlyweight.encode(directBuffer, 0, 0, 0, metadata); + ByteBuf metadata = Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4}); + int length = LeaseFrameFlyweight.encode(byteBuf, 0, 0, metadata); assertEquals(length, 9 + 4 * 2 + 4); // Frame header + ttl + #requests + 4 byte metadata } } \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java index 9ce5c1732..f809ac253 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/frame/SetupFrameFlyweightTest.java @@ -1,53 +1,52 @@ package io.reactivesocket.frame; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.reactivesocket.FrameType; -import org.agrona.concurrent.UnsafeBuffer; import org.junit.Test; -import java.nio.ByteBuffer; - import static org.junit.Assert.*; public class SetupFrameFlyweightTest { - private final UnsafeBuffer directBuffer = new UnsafeBuffer(ByteBuffer.allocate(1024)); + private final ByteBuf byteBuf = Unpooled.buffer(1024); @Test public void validFrame() { - ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); - ByteBuffer data = ByteBuffer.wrap(new byte[]{5, 4, 3}); - SetupFrameFlyweight.encode(directBuffer, 0, 0, 5, 500, "metadata_type", "data_type", metadata, data); - - metadata.rewind(); - data.rewind(); - - assertEquals(FrameType.SETUP, FrameHeaderFlyweight.frameType(directBuffer, 0)); - assertEquals("metadata_type", SetupFrameFlyweight.metadataMimeType(directBuffer, 0)); - assertEquals("data_type", SetupFrameFlyweight.dataMimeType(directBuffer, 0)); - assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); - assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); + ByteBuf metadata = Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4}); + ByteBuf data = Unpooled.wrappedBuffer(new byte[]{5, 4, 3}); + SetupFrameFlyweight.encode(byteBuf, 0, 5, 500, "metadata_type", "data_type", metadata, data); + + metadata.resetReaderIndex(); + data.resetReaderIndex(); + + assertEquals(FrameType.SETUP, FrameHeaderFlyweight.frameType(byteBuf)); + assertEquals("metadata_type", SetupFrameFlyweight.metadataMimeType(byteBuf)); + assertEquals("data_type", SetupFrameFlyweight.dataMimeType(byteBuf)); + assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(byteBuf)); + assertEquals(data, FrameHeaderFlyweight.sliceFrameData(byteBuf)); } @Test(expected = IllegalArgumentException.class) public void resumeNotSupported() { - SetupFrameFlyweight.encode(directBuffer, 0, SetupFrameFlyweight.FLAGS_RESUME_ENABLE, 5, 500, "", "", FrameHeaderFlyweight.NULL_BYTEBUFFER, FrameHeaderFlyweight.NULL_BYTEBUFFER); + SetupFrameFlyweight.encode(byteBuf, SetupFrameFlyweight.FLAGS_RESUME_ENABLE, 5, 500, "", "", Unpooled.EMPTY_BUFFER, Unpooled.EMPTY_BUFFER); } @Test public void validResumeFrame() { - ByteBuffer token = ByteBuffer.wrap(new byte[]{2, 3}); - ByteBuffer metadata = ByteBuffer.wrap(new byte[]{1, 2, 3, 4}); - ByteBuffer data = ByteBuffer.wrap(new byte[]{5, 4, 3}); - SetupFrameFlyweight.encode(directBuffer, 0, SetupFrameFlyweight.FLAGS_RESUME_ENABLE, 5, 500, token, "metadata_type", "data_type", metadata, data); - - token.rewind(); - metadata.rewind(); - data.rewind(); - - assertEquals(FrameType.SETUP, FrameHeaderFlyweight.frameType(directBuffer, 0)); - assertEquals("metadata_type", SetupFrameFlyweight.metadataMimeType(directBuffer, 0)); - assertEquals("data_type", SetupFrameFlyweight.dataMimeType(directBuffer, 0)); - assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); - assertEquals(data, FrameHeaderFlyweight.sliceFrameData(directBuffer, 0, FrameHeaderFlyweight.FRAME_HEADER_LENGTH)); - assertEquals(SetupFrameFlyweight.FLAGS_RESUME_ENABLE, FrameHeaderFlyweight.flags(directBuffer, 0) & SetupFrameFlyweight.FLAGS_RESUME_ENABLE); + ByteBuf token = Unpooled.wrappedBuffer(new byte[]{2, 3}); + ByteBuf metadata = Unpooled.wrappedBuffer(new byte[]{1, 2, 3, 4}); + ByteBuf data = Unpooled.wrappedBuffer(new byte[]{5, 4, 3}); + SetupFrameFlyweight.encode(byteBuf, SetupFrameFlyweight.FLAGS_RESUME_ENABLE, 5, 500, token, "metadata_type", "data_type", metadata, data); + + token.resetReaderIndex(); + metadata.resetReaderIndex(); + data.resetReaderIndex(); + + assertEquals(FrameType.SETUP, FrameHeaderFlyweight.frameType(byteBuf)); + assertEquals("metadata_type", SetupFrameFlyweight.metadataMimeType(byteBuf)); + assertEquals("data_type", SetupFrameFlyweight.dataMimeType(byteBuf)); + assertEquals(metadata, FrameHeaderFlyweight.sliceFrameMetadata(byteBuf)); + assertEquals(data, FrameHeaderFlyweight.sliceFrameData(byteBuf)); + assertEquals(SetupFrameFlyweight.FLAGS_RESUME_ENABLE, FrameHeaderFlyweight.flags(byteBuf) & SetupFrameFlyweight.FLAGS_RESUME_ENABLE); } } \ No newline at end of file diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java index aec5bcdfe..db26a5e89 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/internal/ReassemblerTest.java @@ -15,16 +15,17 @@ */ package io.reactivesocket.internal; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.reactivesocket.Frame; import io.reactivesocket.FrameType; import io.reactivesocket.Payload; -import io.reactivesocket.TestUtil; import io.reactivesocket.frame.FrameHeaderFlyweight; import io.reactivesocket.frame.PayloadReassembler; import org.junit.Test; import reactor.core.publisher.ReplayProcessor; -import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; public class ReassemblerTest { @@ -37,8 +38,8 @@ public void shouldPassThroughUnfragmentedFrame() final PayloadReassembler reassembler = PayloadReassembler.with(replaySubject); final String metadata = "metadata"; final String data = "data"; - final ByteBuffer metadataBuffer = TestUtil.byteBufferFromUtf8String(metadata); - final ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String(data); + final ByteBuf metadataBuffer = Unpooled.copiedBuffer(metadata, StandardCharsets.UTF_8); + final ByteBuf dataBuffer = Unpooled.copiedBuffer(data, StandardCharsets.UTF_8); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, dataBuffer, 0)); @@ -54,8 +55,8 @@ public void shouldNotPassThroughFragmentedFrameIfStillMoreFollowing() final PayloadReassembler reassembler = PayloadReassembler.with(replaySubject); final String metadata = "metadata"; final String data = "data"; - final ByteBuffer metadataBuffer = TestUtil.byteBufferFromUtf8String(metadata); - final ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String(data); + final ByteBuf metadataBuffer = Unpooled.copiedBuffer(metadata, StandardCharsets.UTF_8); + final ByteBuf dataBuffer = Unpooled.copiedBuffer(data, StandardCharsets.UTF_8); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, dataBuffer, FrameHeaderFlyweight.FLAGS_F)); @@ -73,10 +74,10 @@ public void shouldReassembleTwoFramesWithFragmentedDataAndMetadata() final String data0 = "data0"; final String data1 = "d1"; final String data = data0 + data1; - final ByteBuffer metadata0Buffer = TestUtil.byteBufferFromUtf8String(metadata0); - final ByteBuffer data0Buffer = TestUtil.byteBufferFromUtf8String(data0); - final ByteBuffer metadata1Buffer = TestUtil.byteBufferFromUtf8String(metadata1); - final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); + final ByteBuf metadata0Buffer = Unpooled.copiedBuffer(metadata0, StandardCharsets.UTF_8); + final ByteBuf data0Buffer = Unpooled.copiedBuffer(data0, StandardCharsets.UTF_8); + final ByteBuf metadata1Buffer = Unpooled.copiedBuffer(metadata1, StandardCharsets.UTF_8); + final ByteBuf data1Buffer = Unpooled.copiedBuffer(data1, StandardCharsets.UTF_8); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_F)); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, 0)); @@ -95,12 +96,12 @@ public void shouldReassembleTwoFramesWithFragmentedData() final String data0 = "data0"; final String data1 = "d1"; final String data = data0 + data1; - final ByteBuffer metadataBuffer = TestUtil.byteBufferFromUtf8String(metadata); - final ByteBuffer data0Buffer = TestUtil.byteBufferFromUtf8String(data0); - final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); + final ByteBuf metadataBuffer = Unpooled.copiedBuffer(metadata, StandardCharsets.UTF_8); + final ByteBuf data0Buffer = Unpooled.copiedBuffer(data0, StandardCharsets.UTF_8); + final ByteBuf data1Buffer = Unpooled.copiedBuffer(data1, StandardCharsets.UTF_8); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadataBuffer, data0Buffer, FrameHeaderFlyweight.FLAGS_F)); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, Frame.NULL_BYTEBUFFER, data1Buffer, 0)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, Unpooled.EMPTY_BUFFER, data1Buffer, 0)); //assertEquals(1, replaySubject.getValues().length); //assertEquals(data, TestUtil.byteToString(replaySubject.getValue().getData())); @@ -116,12 +117,12 @@ public void shouldReassembleTwoFramesWithFragmentedMetadata() final String metadata1 = "md1"; final String metadata = metadata0 + metadata1; final String data = "data"; - final ByteBuffer metadata0Buffer = TestUtil.byteBufferFromUtf8String(metadata0); - final ByteBuffer dataBuffer = TestUtil.byteBufferFromUtf8String(data); - final ByteBuffer metadata1Buffer = TestUtil.byteBufferFromUtf8String(metadata1); + final ByteBuf metadata0Buffer = Unpooled.copiedBuffer(metadata0, StandardCharsets.UTF_8); + final ByteBuf dataBuffer = Unpooled.copiedBuffer(data, StandardCharsets.UTF_8); + final ByteBuf metadata1Buffer = Unpooled.copiedBuffer(metadata1, StandardCharsets.UTF_8); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, dataBuffer, FrameHeaderFlyweight.FLAGS_F)); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, Frame.NULL_BYTEBUFFER, 0)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, Unpooled.EMPTY_BUFFER, 0)); //assertEquals(1, replaySubject.getValues().length); //assertEquals(data, TestUtil.byteToString(replaySubject.getValue().getData())); @@ -140,15 +141,15 @@ public void shouldReassembleTwoFramesWithFragmentedDataAndMetadataWithMoreThanTw final String data1 = "d1"; final String data2 = "d2"; final String data = data0 + data1 + data2; - final ByteBuffer metadata0Buffer = TestUtil.byteBufferFromUtf8String(metadata0); - final ByteBuffer data0Buffer = TestUtil.byteBufferFromUtf8String(data0); - final ByteBuffer metadata1Buffer = TestUtil.byteBufferFromUtf8String(metadata1); - final ByteBuffer data1Buffer = TestUtil.byteBufferFromUtf8String(data1); - final ByteBuffer data2Buffer = TestUtil.byteBufferFromUtf8String(data2); + final ByteBuf metadata0Buffer = Unpooled.copiedBuffer(metadata0, StandardCharsets.UTF_8); + final ByteBuf data0Buffer = Unpooled.copiedBuffer(data0, StandardCharsets.UTF_8); + final ByteBuf metadata1Buffer = Unpooled.copiedBuffer(metadata1, StandardCharsets.UTF_8); + final ByteBuf data1Buffer = Unpooled.copiedBuffer(data1, StandardCharsets.UTF_8); + final ByteBuf data2Buffer = Unpooled.copiedBuffer(data2, StandardCharsets.UTF_8); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata0Buffer, data0Buffer, FrameHeaderFlyweight.FLAGS_F)); reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, metadata1Buffer, data1Buffer, FrameHeaderFlyweight.FLAGS_F)); - reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, Frame.NULL_BYTEBUFFER, data2Buffer, 0)); + reassembler.onNext(Frame.PayloadFrame.from(STREAM_ID, FrameType.NEXT, Unpooled.EMPTY_BUFFER, data2Buffer, 0)); //assertEquals(1, replaySubject.getValues().length); //assertEquals(data, TestUtil.byteToString(replaySubject.getValue().getData())); diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseHonoringSocketTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseHonoringSocketTest.java index 663e91aa0..8935e4de6 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseHonoringSocketTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/lease/DefaultLeaseHonoringSocketTest.java @@ -16,7 +16,6 @@ package io.reactivesocket.lease; -import io.reactivesocket.Frame; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.exceptions.RejectedException; import io.reactivesocket.lease.DefaultLeaseHonoringSocketTest.SocketHolder; @@ -92,7 +91,7 @@ protected DefaultLeaseHonoringSocket newSocket(ReactiveSocket delegate) { } public SocketHolder sendLease(int permits, int ttl) { - reactiveSocket.accept(new LeaseImpl(permits, ttl, Frame.NULL_BYTEBUFFER)); + reactiveSocket.accept(new LeaseImpl(permits, ttl)); return this; } } diff --git a/reactivesocket-core/src/test/java/io/reactivesocket/util/PayloadImplTest.java b/reactivesocket-core/src/test/java/io/reactivesocket/util/PayloadImplTest.java index 7b04f0fce..a30b6f851 100644 --- a/reactivesocket-core/src/test/java/io/reactivesocket/util/PayloadImplTest.java +++ b/reactivesocket-core/src/test/java/io/reactivesocket/util/PayloadImplTest.java @@ -13,18 +13,15 @@ package io.reactivesocket.util; +import io.reactivesocket.TestUtil; import org.junit.Test; -import java.nio.ByteBuffer; - -import static java.nio.ByteBuffer.wrap; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; public class PayloadImplTest { - public static final String DATA_VAL = "data"; - public static final String METADATA_VAL = "Metadata"; + public static final String METADATA_VAL = "metadata"; @Test public void testReuse() throws Exception { @@ -32,13 +29,6 @@ public void testReuse() throws Exception { assertDataAndMetadata(p); assertDataAndMetadata(p); } - @Test - public void testSingleUse() throws Exception { - PayloadImpl p = new PayloadImpl(wrap(DATA_VAL.getBytes()), wrap(METADATA_VAL.getBytes()), false); - assertDataAndMetadata(p); - assertThat("Unexpected data length", p.getData().remaining(), is(0)); - assertThat("Unexpected metadata length", p.getMetadata().remaining(), is(0)); - } @Test public void testReuseWithExternalMark() throws Exception { @@ -49,13 +39,7 @@ public void testReuseWithExternalMark() throws Exception { } public void assertDataAndMetadata(PayloadImpl p) { - assertThat("Unexpected data.", readValue(p.getData()), equalTo(DATA_VAL)); - assertThat("Unexpected metadata.", readValue(p.getMetadata()), equalTo(METADATA_VAL)); - } - - public String readValue(ByteBuffer buffer) { - byte[] bytes = new byte[buffer.remaining()]; - buffer.get(bytes); - return new String(bytes); + assertThat("Unexpected data.", TestUtil.byteToString(p.getData()), equalTo(DATA_VAL)); + assertThat("Unexpected metadata.", TestUtil.byteToString(p.getMetadata()), equalTo(METADATA_VAL)); } } \ No newline at end of file diff --git a/reactivesocket-examples/build.gradle b/reactivesocket-examples/build.gradle index a584ac54c..257ccf82d 100644 --- a/reactivesocket-examples/build.gradle +++ b/reactivesocket-examples/build.gradle @@ -13,23 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -buildscript { - repositories { - maven { url "https://plugins.gradle.org/m2/" } - } - dependencies { - classpath 'gradle.plugin.me.champeau.gradle:jmh-gradle-plugin:0.3.0' - } +plugins { + id "me.champeau.gradle.jmh" version "0.3.1" } -apply plugin: 'me.champeau.gradle.jmh' - jmh { jmhVersion = '1.15' - jvmArgs = '-XX:+UnlockCommercialFeatures -XX:+FlightRecorder' + jvmArgs = '-XX:+UseG1GC -Xmx16g -Xms16g -XX:+UnlockCommercialFeatures -XX:+FlightRecorder' profilers = ['gc'] zip64 = true + warmupBatchSize = 10 + iterations = 500 + duplicateClassesStrategy = 'warn' } dependencies { diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/channel/ChannelEchoClient.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/channel/ChannelEchoClient.java index 9ebbfa3bc..46a50ffe4 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/channel/ChannelEchoClient.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/channel/ChannelEchoClient.java @@ -18,7 +18,6 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.ReactiveSocketClient; -import io.reactivesocket.frame.ByteBufferUtil; import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.server.ReactiveSocketServer; @@ -34,6 +33,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; import java.time.Duration; import static io.reactivesocket.client.KeepAliveProvider.*; @@ -55,8 +55,7 @@ public static void main(String[] args) { .map(i -> "Hello - " + i) .map(PayloadImpl::new) .repeat()) - .map(payload -> payload.getData()) - .map(ByteBufferUtil::toUtf8String) + .map(payload -> StandardCharsets.UTF_8.decode(payload.getData()).toString()) .doOnNext(System.out::println) .take(10) .concatWith(socket.close().cast(String.class)) @@ -70,8 +69,7 @@ public LeaseEnforcingSocket accept(ConnectionSetupPayload setupPayload, Reactive @Override public Flux requestChannel(Publisher payloads) { return Flux.from(payloads) - .map(Payload::getData) - .map(ByteBufferUtil::toUtf8String) + .map(payload -> StandardCharsets.UTF_8.decode(payload.getData()).toString()) .map(s -> "Echo: " + s) .map(PayloadImpl::new); } diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/duplex/DuplexClient.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/duplex/DuplexClient.java index ade9be226..5691a2076 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/duplex/DuplexClient.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/duplex/DuplexClient.java @@ -18,7 +18,6 @@ import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.ReactiveSocketClient; import io.reactivesocket.client.ReactiveSocketClient.SocketAcceptor; -import io.reactivesocket.frame.ByteBufferUtil; import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.server.ReactiveSocketServer; @@ -32,6 +31,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; import java.time.Duration; import static io.reactivesocket.client.KeepAliveProvider.*; @@ -43,8 +43,7 @@ public static void main(String[] args) { StartedServer server = ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create())) .start((setupPayload, reactiveSocket) -> { reactiveSocket.requestStream(new PayloadImpl("Hello-Bidi")) - .map(Payload::getData) - .map(ByteBufferUtil::toUtf8String) + .map(payload -> StandardCharsets.UTF_8.decode(payload.getData()).toString()) .log() .subscribe(); return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { }); diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/requestresponse/HelloWorldClient.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/requestresponse/HelloWorldClient.java index 72584dd4e..fe8f8df1f 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/requestresponse/HelloWorldClient.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/requestresponse/HelloWorldClient.java @@ -20,7 +20,6 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.ReactiveSocketClient; -import io.reactivesocket.frame.ByteBufferUtil; import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.server.ReactiveSocketServer; import io.reactivesocket.transport.TransportServer.StartedServer; @@ -33,6 +32,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; import static io.reactivesocket.client.KeepAliveProvider.*; import static io.reactivesocket.client.SetupProvider.*; @@ -58,8 +58,7 @@ public Mono requestResponse(Payload p) { ReactiveSocket socket = client.connect().block(); socket.requestResponse(new PayloadImpl("Hello")) - .map(payload -> payload.getData()) - .map(ByteBufferUtil::toUtf8String) + .map(payload -> StandardCharsets.UTF_8.decode(payload.getData()).toString()) .doOnNext(System.out::println) .concatWith(socket.close().cast(String.class)) .ignoreElements() diff --git a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stream/StreamingClient.java b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stream/StreamingClient.java index 95095e03e..b7580ec53 100644 --- a/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stream/StreamingClient.java +++ b/reactivesocket-examples/src/main/java/io/reactivesocket/examples/transport/tcp/stream/StreamingClient.java @@ -18,7 +18,6 @@ import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; import io.reactivesocket.client.ReactiveSocketClient; -import io.reactivesocket.frame.ByteBufferUtil; import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.server.ReactiveSocketServer; @@ -33,6 +32,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; import java.time.Duration; import static io.reactivesocket.client.KeepAliveProvider.*; @@ -52,8 +52,7 @@ public static void main(String[] args) { .block(); socket.requestStream(new PayloadImpl("Hello")) - .map(payload -> payload.getData()) - .map(ByteBufferUtil::toUtf8String) + .map(payload -> StandardCharsets.UTF_8.decode(payload.getData()).toString()) .doOnNext(System.out::println) .take(10) .thenEmpty(socket.close()) diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/MySubscriber.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/MySubscriber.java index 44364683a..ac6836086 100644 --- a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/MySubscriber.java +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/common/MySubscriber.java @@ -1,6 +1,7 @@ package io.reactivesocket.tckdrivers.common; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; @@ -10,7 +11,6 @@ import io.reactivesocket.Frame; import io.reactivesocket.Payload; -import io.reactivesocket.frame.ByteBufferUtil; import io.reactivex.subscribers.TestSubscriber; @@ -55,8 +55,8 @@ public void onSubscribe(Subscription s) { @Override public void onNext(T t) { Payload p = (Payload) t; - Tuple tup = new Tuple<>(ByteBufferUtil.toUtf8String(p.getData()), - ByteBufferUtil.toUtf8String(p.getMetadata())); + Tuple tup = new Tuple<>(StandardCharsets.UTF_8.decode(p.getData()).toString(), + StandardCharsets.UTF_8.decode(p.getMetadata()).toString()); consoleUtils.info("On NEXT got : " + tup.getK() + " " + tup.getV()); if (isEcho) { echosub.add(tup); @@ -121,8 +121,8 @@ public final boolean assertValues(List> values) { } for (int i = 0; i < values.size(); i++) { Frame p = (Frame) this.values().get(i); - Tuple v = new Tuple<>(ByteBufferUtil.toUtf8String(p.getData()), - ByteBufferUtil.toUtf8String(p.getMetadata())); + Tuple v = new Tuple<>(StandardCharsets.UTF_8.decode(p.getData()).toString(), + StandardCharsets.UTF_8.decode(p.getMetadata()).toString()); Tuple u = values.get(i); if (!Objects.equals(u, v)) { myFail(prefix + "Values at position " + i + " differ; Expected: " @@ -146,8 +146,8 @@ public final void assertValue(Tuple value) { myFail("value does not match"); } Payload p = (Payload) values().get(0); - Tuple v = new Tuple<>(ByteBufferUtil.toUtf8String(p.getData()), - ByteBufferUtil.toUtf8String(p.getMetadata())); + Tuple v = new Tuple<>(StandardCharsets.UTF_8.decode(p.getData()).toString(), + StandardCharsets.UTF_8.decode(p.getMetadata()).toString()); if (!Objects.equals(value, v)) { myFail(prefix + "Expected: " + valueAndClass(value) + ", Actual: " + valueAndClass(v)); myFail("value does not match"); @@ -209,8 +209,8 @@ public final void take(long n) { public Tuple getElement(int n) { assert(n < values.size()); Payload p = (Payload) values().get(n); - Tuple tup = new Tuple<>(ByteBufferUtil.toUtf8String(p.getData()), - ByteBufferUtil.toUtf8String(p.getMetadata())); + Tuple tup = new Tuple<>(StandardCharsets.UTF_8.decode(p.getData()).toString(), + StandardCharsets.UTF_8.decode(p.getMetadata()).toString()); return tup; } diff --git a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaServerDriver.java b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaServerDriver.java index 55c6d4cfa..3d496bfed 100644 --- a/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaServerDriver.java +++ b/reactivesocket-tck-drivers/src/main/java/io/reactivesocket/tckdrivers/server/JavaServerDriver.java @@ -17,7 +17,6 @@ import io.reactivesocket.ConnectionSetupPayload; import io.reactivesocket.Payload; import io.reactivesocket.ReactiveSocket; -import io.reactivesocket.frame.ByteBufferUtil; import io.reactivesocket.lease.DisabledLeaseAcceptingSocket; import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.tckdrivers.common.*; @@ -35,6 +34,7 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.CountDownLatch; @@ -137,8 +137,8 @@ public Flux requestChannel(Publisher payloads) { @Override public final Mono fireAndForget(Payload payload) { return Mono.from(s -> { - Tuple initialPayload = new Tuple<>(ByteBufferUtil.toUtf8String(payload.getData()), - ByteBufferUtil.toUtf8String(payload.getMetadata())); + Tuple initialPayload = new Tuple<>(StandardCharsets.UTF_8.decode(payload.getData()).toString(), + StandardCharsets.UTF_8.decode(payload.getMetadata()).toString()); consoleUtils.initialPayload("Received firenforget " + initialPayload.getK() + " " + initialPayload.getV()); if (initialPayload.getK().equals("shutdown") && initialPayload.getV().equals("shutdown")) { try { @@ -154,8 +154,8 @@ public final Mono fireAndForget(Payload payload) { @Override public Mono requestResponse(Payload payload) { return Mono.from(s -> { - Tuple initialPayload = new Tuple<>(ByteBufferUtil.toUtf8String(payload.getData()), - ByteBufferUtil.toUtf8String(payload.getMetadata())); + Tuple initialPayload = new Tuple<>(StandardCharsets.UTF_8.decode(payload.getData()).toString(), + StandardCharsets.UTF_8.decode(payload.getMetadata()).toString()); String marble = requestResponseMarbles.get(initialPayload); consoleUtils.initialPayload("Received requestresponse " + initialPayload.getK() + " " + initialPayload.getV()); @@ -173,8 +173,8 @@ public Mono requestResponse(Payload payload) { @Override public Flux requestStream(Payload payload) { return Flux.from(s -> { - Tuple initialPayload = new Tuple<>(ByteBufferUtil.toUtf8String(payload.getData()), - ByteBufferUtil.toUtf8String(payload.getMetadata())); + Tuple initialPayload = new Tuple<>(StandardCharsets.UTF_8.decode(payload.getData()).toString(), + StandardCharsets.UTF_8.decode(payload.getMetadata()).toString()); String marble = requestStreamMarbles.get(initialPayload); consoleUtils.initialPayload("Received Stream " + initialPayload.getK() + " " + initialPayload.getV()); if (marble != null) { diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java index 3cd3fad86..7e46b6747 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/ClientSetupRule.java @@ -22,6 +22,7 @@ import io.reactivesocket.client.ReactiveSocketClient; import io.reactivesocket.client.SetupProvider; import io.reactivesocket.transport.TransportClient; +import io.reactivesocket.util.PayloadImpl; import io.reactivex.subscribers.TestSubscriber; import org.junit.rules.ExternalResource; import org.junit.runner.Description; @@ -29,6 +30,7 @@ import reactor.core.publisher.Flux; import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.function.Function; @@ -83,7 +85,7 @@ public void testFireAndForget(int count) { Flux.range(1, count) .flatMap(i -> getReactiveSocket() - .fireAndForget(TestUtil.utf8EncodedPayload("hello", "metadata")) + .fireAndForget(new PayloadImpl("hello", "metadata")) ) .doOnError(Throwable::printStackTrace) .subscribe(ts); @@ -99,7 +101,7 @@ public void testMetadata(int count) { Flux.range(1, count) .flatMap(i -> getReactiveSocket() - .metadataPush(TestUtil.utf8EncodedPayload("", "metadata")) + .metadataPush(new PayloadImpl("", "metadata")) ) .doOnError(Throwable::printStackTrace) .subscribe(ts); @@ -113,10 +115,10 @@ public void testMetadata(int count) { public void testRequestResponseN(int count) { TestSubscriber ts = TestSubscriber.create(); Flux.range(1, count) - .flatMap(i -> + .flatMap(i -> getReactiveSocket() - .requestResponse(TestUtil.utf8EncodedPayload("hello", "metadata"))) - .map(payload -> TestUtil.byteToString(payload.getData()) + .requestResponse(new PayloadImpl("hello", "metadata")) + .map(payload -> StandardCharsets.UTF_8.decode(payload.getData()).toString()) ) .doOnError(Throwable::printStackTrace) .subscribe(ts); @@ -129,11 +131,11 @@ public void testRequestResponseN(int count) { } public void testRequestStream() { - testStream(socket -> socket.requestStream(TestUtil.utf8EncodedPayload("hello", "metadata"))); + testStream(socket -> socket.requestStream(new PayloadImpl("hello", "metadata"))); } public void testRequestStreamWithRequestN() { - testStreamRequestN(socket -> socket.requestStream(TestUtil.utf8EncodedPayload("hello", "metadata"))); + testStreamRequestN(socket -> socket.requestStream(new PayloadImpl("hello", "metadata"))); } diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java index 4d89b1ae2..75a17bfe9 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingClient.java @@ -27,13 +27,13 @@ public class PingClient { - private final String request; + private final Payload payload; private final ReactiveSocketClient client; private ReactiveSocket reactiveSocket; public PingClient(ReactiveSocketClient client) { this.client = client; - request = "hello"; + this.payload = new PayloadImpl("hello"); } public PingClient connect() { @@ -61,11 +61,11 @@ public Flux startPingPong(int count, final Recorder histogram) { return Flux.range(1, count) .flatMap(i -> { long start = System.nanoTime(); - return reactiveSocket.requestResponse(new PayloadImpl(request)) - .doFinally(signalType -> { - long diff = System.nanoTime() - start; - histogram.recordValue(diff); - }); + return reactiveSocket.requestResponse(payload) + .doFinally(signalType -> { + long diff = System.nanoTime() - start; + histogram.recordValue(diff); + }); }) .doOnError(Throwable::printStackTrace); } diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingHandler.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingHandler.java index ae413481c..e05a7cd0a 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/PingHandler.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/PingHandler.java @@ -30,15 +30,16 @@ public class PingHandler implements SocketAcceptor { - private final byte[] pong; + private final Payload pong; public PingHandler() { - pong = new byte[1024]; - ThreadLocalRandom.current().nextBytes(pong); + byte[] data = new byte[1024]; + ThreadLocalRandom.current().nextBytes(data); + pong = new PayloadImpl(data); } - public PingHandler(byte[] pong) { - this.pong = pong; + public PingHandler(byte[] data) { + pong = new PayloadImpl(data); } @Override @@ -46,7 +47,7 @@ public LeaseEnforcingSocket accept(ConnectionSetupPayload setupPayload, Reactive return new DisabledLeaseAcceptingSocket(new AbstractReactiveSocket() { @Override public Mono requestResponse(Payload payload) { - return Mono.just(new PayloadImpl(pong)); + return Mono.just(pong); } }); } diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java index 20c1754f5..87dd6d84c 100644 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java +++ b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestReactiveSocket.java @@ -18,6 +18,7 @@ import io.reactivesocket.AbstractReactiveSocket; import io.reactivesocket.Payload; +import io.reactivesocket.util.PayloadImpl; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -25,7 +26,7 @@ public class TestReactiveSocket extends AbstractReactiveSocket { @Override public Mono requestResponse(Payload payload) { - return Mono.just(TestUtil.utf8EncodedPayload("hello world", "metadata")); + return Mono.just(new PayloadImpl("hello world", "metadata")); } @Override @@ -35,6 +36,11 @@ public Flux requestStream(Payload payload) { .flatMap(l -> requestResponse(payload)); } + @Override + public Mono metadataPush(Payload payload) { + return Mono.empty(); + } + @Override public Mono fireAndForget(Payload payload) { return Mono.empty(); diff --git a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestUtil.java b/reactivesocket-test/src/main/java/io/reactivesocket/test/TestUtil.java deleted file mode 100644 index 51912fc33..000000000 --- a/reactivesocket-test/src/main/java/io/reactivesocket/test/TestUtil.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.test; - -import io.reactivesocket.Frame; -import io.reactivesocket.FrameType; -import io.reactivesocket.Payload; -import org.agrona.MutableDirectBuffer; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; - -public class TestUtil -{ - public static Frame utf8EncodedRequestFrame(final int streamId, final FrameType type, final String data, final int initialRequestN) - { - return Frame.Request.from(streamId, type, new Payload() - { - public ByteBuffer getData() - { - return byteBufferFromUtf8String(data); - } - - public ByteBuffer getMetadata() - { - return Frame.NULL_BYTEBUFFER; - } - }, initialRequestN); - } - - public static Frame utf8EncodedResponseFrame(final int streamId, final FrameType type, final String data) - { - return Frame.PayloadFrame.from(streamId, type, utf8EncodedPayload(data, null)); - } - - public static Frame utf8EncodedErrorFrame(final int streamId, final String data) - { - return Frame.Error.from(streamId, new Exception(data)); - } - - public static Payload utf8EncodedPayload(final String data, final String metadata) - { - return new PayloadImpl(data, metadata); - } - - public static String dataAsString(Payload payload) { - ByteBuffer data = payload.getData(); - byte[] dst = new byte[data.remaining()]; - data.get(dst); - return new String(dst, StandardCharsets.UTF_8); - } - - public static String byteToString(ByteBuffer byteBuffer) - { - byteBuffer = byteBuffer.duplicate(); - - final byte[] bytes = new byte[byteBuffer.remaining()]; - byteBuffer.get(bytes); - return new String(bytes, StandardCharsets.UTF_8); - } - - public static ByteBuffer byteBufferFromUtf8String(final String data) - { - final byte[] bytes = data.getBytes(StandardCharsets.UTF_8); - return ByteBuffer.wrap(bytes); - } - - public static void copyFrame(final MutableDirectBuffer dst, final int offset, final Frame frame) - { - dst.putBytes(offset, frame.getByteBuffer(), frame.offset(), frame.length()); - } - - private static class PayloadImpl implements Payload // some JDK shoutout - { - private ByteBuffer data; - private ByteBuffer metadata; - - public PayloadImpl(final String data, final String metadata) - { - if (null == data) - { - this.data = ByteBuffer.allocate(0); - } - else - { - this.data = byteBufferFromUtf8String(data); - } - - if (null == metadata) - { - this.metadata = ByteBuffer.allocate(0); - } - else - { - this.metadata = byteBufferFromUtf8String(metadata); - } - } - - public boolean equals(Object obj) - { - System.out.println("equals: " + obj); - final Payload rhs = (Payload)obj; - - return (TestUtil.byteToString(data).equals(TestUtil.byteToString(rhs.getData()))) && - (TestUtil.byteToString(metadata).equals(TestUtil.byteToString(rhs.getMetadata()))); - } - - public ByteBuffer getData() - { - return data; - } - - public ByteBuffer getMetadata() - { - return metadata; - } - } -} diff --git a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/AeronDuplexConnection.java b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/AeronDuplexConnection.java index 4f8e96718..c300e6620 100644 --- a/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/AeronDuplexConnection.java +++ b/reactivesocket-transport-aeron/src/main/java/io/reactivesocket/aeron/AeronDuplexConnection.java @@ -15,6 +15,7 @@ */ package io.reactivesocket.aeron; +import io.netty.buffer.Unpooled; import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; import io.reactivesocket.aeron.internal.reactivestreams.AeronChannel; @@ -42,7 +43,7 @@ public AeronDuplexConnection(String name, AeronChannel channel) { @Override public Mono send(Publisher frame) { Flux buffers = Flux.from(frame) - .map(f -> new UnsafeBuffer(f.getByteBuffer())); + .map(f -> new UnsafeBuffer(f.content().nioBuffer())); return channel.send(buffers); } @@ -51,7 +52,7 @@ public Mono send(Publisher frame) { public Flux receive() { return channel .receive() - .map(b -> Frame.from(b, 0, b.capacity())) + .map(b -> Frame.from(Unpooled.wrappedBuffer(b.byteBuffer()))) .doOnError(throwable -> throwable.printStackTrace()); } diff --git a/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java b/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java index 8f1340fd8..8baf86cad 100644 --- a/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java +++ b/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java @@ -28,8 +28,9 @@ public Mono send(Publisher frames) { @Override public Mono sendOne(Frame frame) { - out.onNext(frame); - return Mono.empty(); + return Mono.fromRunnable(() -> + out.onNext(frame) + ); } @Override diff --git a/reactivesocket-transport-local/src/test/java/io/reactivesocket/transport/local/LocalClientServerTest.java b/reactivesocket-transport-local/src/test/java/io/reactivesocket/transport/local/LocalClientServerTest.java index 237341f1f..57c5e50d9 100644 --- a/reactivesocket-transport-local/src/test/java/io/reactivesocket/transport/local/LocalClientServerTest.java +++ b/reactivesocket-transport-local/src/test/java/io/reactivesocket/transport/local/LocalClientServerTest.java @@ -16,7 +16,6 @@ package io.reactivesocket.transport.local; import io.reactivesocket.test.ClientSetupRule; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -25,6 +24,16 @@ public class LocalClientServerTest { @Rule public final ClientSetupRule setup = new LocalClientSetupRule(); + @Test(timeout = 10000) + public void testFireNForget10() { + setup.testFireAndForget(10); + } + + @Test(timeout = 10000) + public void testPushMetadata10() { + setup.testMetadata(10); + } + @Test(timeout = 10000) public void testRequestResponse1() { setup.testRequestResponseN(1); @@ -35,7 +44,6 @@ public void testRequestResponse10() { setup.testRequestResponseN(10); } - @Test(timeout = 10000) public void testRequestResponse100() { setup.testRequestResponseN(100); @@ -46,9 +54,13 @@ public void testRequestResponse10_000() { setup.testRequestResponseN(10_000); } - @Ignore("Fix request-stream") @Test(timeout = 10000) public void testRequestStream() { setup.testRequestStream(); } + + @Test(timeout = 10000) + public void testRequestStreamWithRequestN() { + setup.testRequestStreamWithRequestN(); + } } \ No newline at end of file diff --git a/reactivesocket-transport-local/src/test/java/io/reactivesocket/transport/local/LocalPingPong.java b/reactivesocket-transport-local/src/test/java/io/reactivesocket/transport/local/LocalPingPong.java new file mode 100644 index 000000000..50eb024bf --- /dev/null +++ b/reactivesocket-transport-local/src/test/java/io/reactivesocket/transport/local/LocalPingPong.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.reactivesocket.transport.local; + +import io.reactivesocket.client.KeepAliveProvider; +import io.reactivesocket.client.ReactiveSocketClient; +import io.reactivesocket.client.SetupProvider; +import io.reactivesocket.server.ReactiveSocketServer; +import io.reactivesocket.test.PingClient; +import io.reactivesocket.test.PingHandler; +import org.HdrHistogram.Recorder; + +import java.time.Duration; + +public final class LocalPingPong { + + public static void main(String... args) throws Exception { + ReactiveSocketServer.create(LocalServer.create("test-local-server")) + .start(new PingHandler()); + + SetupProvider setup = SetupProvider.keepAlive(KeepAliveProvider.never()).disableLease(); + ReactiveSocketClient client = + ReactiveSocketClient.create(LocalClient.create("test-local-server"), setup); + PingClient pingClient = new PingClient(client); + Recorder recorder = pingClient.startTracker(Duration.ofSeconds(1)); + final int count = 1_000_000_000; + pingClient.connect() + .startPingPong(count, recorder) + .doOnTerminate(() -> { + System.out.println("Sent " + count + " messages."); + }) + .blockLast(); + } +} diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java index 4a5438d4f..9d28be54b 100644 --- a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/NettyDuplexConnection.java @@ -15,7 +15,6 @@ */ package io.reactivesocket.transport.netty; -import io.netty.buffer.ByteBuf; import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; import org.reactivestreams.Publisher; @@ -25,8 +24,6 @@ import reactor.ipc.netty.NettyInbound; import reactor.ipc.netty.NettyOutbound; -import java.nio.ByteBuffer; - public class NettyDuplexConnection implements DuplexConnection { private final NettyInbound in; private final NettyOutbound out; @@ -41,26 +38,21 @@ public NettyDuplexConnection(NettyInbound in, NettyOutbound out, NettyContext co @Override public Mono send(Publisher frames) { return Flux.from(frames) - .concatMap(this::sendOne) - .then(); + .concatMap(this::sendOne) + .then(); } @Override public Mono sendOne(Frame frame) { - ByteBuffer src = frame.getByteBuffer(); - ByteBuf msg = out.alloc().buffer(src.remaining()).writeBytes(src); - return out.sendObject(msg).then(); + return out.sendObject(frame.content()) + .then(); } @Override public Flux receive() { return in .receive() - .map(byteBuf -> { - ByteBuffer buffer = ByteBuffer.allocate(byteBuf.capacity()); - byteBuf.getBytes(0, buffer); - return Frame.from(buffer); - }); + .map(buf -> Frame.from(buf.retain())); } @Override diff --git a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/WebsocketDuplexConnection.java b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/WebsocketDuplexConnection.java index c19335a61..793b5b269 100644 --- a/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/WebsocketDuplexConnection.java +++ b/reactivesocket-transport-netty/src/main/java/io/reactivesocket/transport/netty/WebsocketDuplexConnection.java @@ -15,7 +15,6 @@ */ package io.reactivesocket.transport.netty; -import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; @@ -26,8 +25,6 @@ import reactor.ipc.netty.NettyInbound; import reactor.ipc.netty.NettyOutbound; -import java.nio.ByteBuffer; - public class WebsocketDuplexConnection implements DuplexConnection { private final NettyInbound in; private final NettyOutbound out; @@ -42,26 +39,21 @@ public WebsocketDuplexConnection(NettyInbound in, NettyOutbound out, NettyContex @Override public Mono send(Publisher frames) { return Flux.from(frames) - .concatMap(this::sendOne) - .then(); + .concatMap(this::sendOne) + .then(); } @Override public Mono sendOne(Frame frame) { - ByteBuffer src = frame.getByteBuffer(); - ByteBuf msg = out.alloc().buffer(src.remaining()).writeBytes(src); - return out.sendObject(new BinaryWebSocketFrame(msg)).then(); + return out.sendObject(new BinaryWebSocketFrame(frame.content())) + .then(); } @Override public Flux receive() { return in .receive() - .map(byteBuf -> { - ByteBuffer buffer = ByteBuffer.allocate(byteBuf.capacity()); - byteBuf.getBytes(0, buffer); - return Frame.from(buffer); - }); + .map(buf -> Frame.from(buf.retain())); } @Override diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPongServer.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPongServer.java index 9ee026351..4fb69874e 100644 --- a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPongServer.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/TcpPongServer.java @@ -24,7 +24,7 @@ public final class TcpPongServer { public static void main(String... args) throws Exception { ReactiveSocketServer.create(TcpTransportServer.create(TcpServer.create(7878))) - .start(new PingHandler()) - .awaitShutdown(); + .start(new PingHandler()) + .awaitShutdown(); } } diff --git a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientServerTest.java b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientServerTest.java index f3170adfb..249df9005 100644 --- a/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientServerTest.java +++ b/reactivesocket-transport-netty/src/test/java/io/reactivesocket/transport/netty/WebsocketClientServerTest.java @@ -25,6 +25,16 @@ public class WebsocketClientServerTest { @Rule public final ClientSetupRule setup = new WebsocketClientSetupRule(); + @Test(timeout = 10000) + public void testFireNForget10() { + setup.testFireAndForget(10); + } + + @Test(timeout = 10000) + public void testPushMetadata10() { + setup.testMetadata(10); + } + @Test(timeout = 10000) public void testRequestResponse1() { setup.testRequestResponseN(1); @@ -35,7 +45,6 @@ public void testRequestResponse10() { setup.testRequestResponseN(10); } - @Test(timeout = 10000) public void testRequestResponse100() { setup.testRequestResponseN(100); @@ -46,7 +55,6 @@ public void testRequestResponse10_000() { setup.testRequestResponseN(10_000); } - @Ignore("Fix request-stream") @Test(timeout = 10000) public void testRequestStream() { setup.testRequestStream(); From fc5524acc2df5ce5449333ecf5a5cdc4c5cc597c Mon Sep 17 00:00:00 2001 From: Ryland Degnan Date: Wed, 29 Mar 2017 16:16:17 -0700 Subject: [PATCH 2/3] Fix tests --- build.gradle | 1 - reactivesocket-core/build.gradle | 2 +- .../internal/ClientServerInputMultiplexer.java | 9 +++++++-- .../transport/local/LocalDuplexConnection.java | 5 ++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 6b77663c5..b863fb150 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,6 @@ subprojects { repositories { jcenter() maven { url 'https://oss.jfrog.org/libs-snapshot' } - maven { url 'http://repo.spring.io/snapshot' } maven { url 'https://dl.bintray.com/reactivesocket/ReactiveSocket' } } diff --git a/reactivesocket-core/build.gradle b/reactivesocket-core/build.gradle index 1c9425c63..3531fb3b2 100644 --- a/reactivesocket-core/build.gradle +++ b/reactivesocket-core/build.gradle @@ -29,7 +29,7 @@ jmh { } dependencies { - compile 'io.projectreactor:reactor-core:3.0.6.BUILD-20170323.100806-6' + compile 'io.projectreactor:reactor-core:3.0.5.RELEASE' compile 'io.netty:netty-buffer:4.1.8.Final' jmh 'org.openjdk.jmh:jmh-core:1.15' diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java index ce2679607..0f2126f33 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/internal/ClientServerInputMultiplexer.java @@ -18,6 +18,7 @@ import io.reactivesocket.DuplexConnection; import io.reactivesocket.Frame; +import io.reactivesocket.FrameType; import io.reactivesocket.Plugins; import io.reactivesocket.Plugins.DuplexConnectionInterceptor.Type; import io.reactivesocket.util.BitUtil; @@ -57,9 +58,13 @@ public ClientServerInputMultiplexer(DuplexConnection source) { source.receive() .groupBy(frame -> { int streamId = frame.getStreamId(); - Type type; + final Type type; if (streamId == 0) { - type = Type.STREAM_ZERO; + if (frame.getType() == FrameType.SETUP) { + type = Type.STREAM_ZERO; + } else { + type = Type.CLIENT; + } } else if (BitUtil.isEven(streamId)) { type = Type.SERVER; } else { diff --git a/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java b/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java index 8baf86cad..8f1340fd8 100644 --- a/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java +++ b/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java @@ -28,9 +28,8 @@ public Mono send(Publisher frames) { @Override public Mono sendOne(Frame frame) { - return Mono.fromRunnable(() -> - out.onNext(frame) - ); + out.onNext(frame); + return Mono.empty(); } @Override From a6a82e3ba3c713362008f36687346eed9bee239e Mon Sep 17 00:00:00 2001 From: Ryland Degnan Date: Wed, 5 Apr 2017 15:03:32 -0700 Subject: [PATCH 3/3] Clean up ServerReactiveSocket --- reactivesocket-core/build.gradle | 2 +- .../reactivesocket/ClientReactiveSocket.java | 10 +- .../reactivesocket/ServerReactiveSocket.java | 98 +-- .../frame/PayloadReassembler.java | 4 +- .../internal/CollectionUtil.java | 133 --- .../io/reactivesocket/internal/Hashing.java | 118 --- .../internal/Int2ObjectHashMap.java | 768 ------------------ .../io/reactivesocket/util/PayloadImpl.java | 3 +- .../local/LocalDuplexConnection.java | 5 +- 9 files changed, 63 insertions(+), 1078 deletions(-) delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/CollectionUtil.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/Hashing.java delete mode 100644 reactivesocket-core/src/main/java/io/reactivesocket/internal/Int2ObjectHashMap.java diff --git a/reactivesocket-core/build.gradle b/reactivesocket-core/build.gradle index 3531fb3b2..9ea180ead 100644 --- a/reactivesocket-core/build.gradle +++ b/reactivesocket-core/build.gradle @@ -29,7 +29,7 @@ jmh { } dependencies { - compile 'io.projectreactor:reactor-core:3.0.5.RELEASE' + compile 'io.projectreactor:reactor-core:3.0.6.RELEASE' compile 'io.netty:netty-buffer:4.1.8.Final' jmh 'org.openjdk.jmh:jmh-core:1.15' diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java index e2b1a41b6..3e4ac1d47 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ClientReactiveSocket.java @@ -17,8 +17,8 @@ package io.reactivesocket; import io.netty.buffer.Unpooled; +import io.netty.util.collection.IntObjectHashMap; import io.reactivesocket.client.KeepAliveProvider; -import io.reactivesocket.internal.Int2ObjectHashMap; import io.reactivesocket.exceptions.Exceptions; import io.reactivesocket.internal.KnownErrorFilter; import io.reactivesocket.internal.LimitableRequestPublisher; @@ -52,8 +52,8 @@ public class ClientReactiveSocket implements ReactiveSocket { private final StreamIdSupplier streamIdSupplier; private final KeepAliveProvider keepAliveProvider; private final MonoProcessor started; - private final Int2ObjectHashMap senders; - private final Int2ObjectHashMap> receivers; + private final IntObjectHashMap senders; + private final IntObjectHashMap> receivers; private Disposable keepAliveSendSub; private volatile Consumer leaseConsumer; // Provided on start() @@ -65,8 +65,8 @@ public ClientReactiveSocket(DuplexConnection connection, Consumer err this.streamIdSupplier = streamIdSupplier; this.keepAliveProvider = keepAliveProvider; this.started = MonoProcessor.create(); - this.senders = new Int2ObjectHashMap<>(256, 0.9f); - this.receivers = new Int2ObjectHashMap<>(256, 0.9f); + this.senders = new IntObjectHashMap<>(256, 0.9f); + this.receivers = new IntObjectHashMap<>(256, 0.9f); connection .onClose() diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java index a0aff489e..23b038b0b 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/ServerReactiveSocket.java @@ -18,9 +18,9 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.util.collection.IntObjectHashMap; import io.reactivesocket.Frame.Lease; import io.reactivesocket.Frame.Request; -import io.reactivesocket.internal.Int2ObjectHashMap; import io.reactivesocket.exceptions.ApplicationException; import io.reactivesocket.frame.FrameHeaderFlyweight; import io.reactivesocket.internal.KnownErrorFilter; @@ -28,6 +28,7 @@ import io.reactivesocket.lease.LeaseEnforcingSocket; import io.reactivesocket.util.PayloadImpl; import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import reactor.core.Disposable; import reactor.core.publisher.Flux; @@ -46,8 +47,8 @@ public class ServerReactiveSocket implements ReactiveSocket { private final DuplexConnection connection; private final Consumer errorConsumer; - private final Int2ObjectHashMap sendingSubscriptions; - private final Int2ObjectHashMap> receivers; + private final IntObjectHashMap sendingSubscriptions; + private final IntObjectHashMap> channelProcessors; private final ReactiveSocket requestHandler; @@ -58,8 +59,8 @@ public ServerReactiveSocket(DuplexConnection connection, ReactiveSocket requestH this.requestHandler = requestHandler; this.connection = connection; this.errorConsumer = new KnownErrorFilter(errorConsumer); - this.sendingSubscriptions = new Int2ObjectHashMap<>(); - this.receivers = new Int2ObjectHashMap<>(); + this.sendingSubscriptions = new IntObjectHashMap<>(); + this.channelProcessors = new IntObjectHashMap<>(); connection.onClose() .doFinally(signalType -> cleanup()) @@ -149,7 +150,7 @@ public ServerReactiveSocket start() { .flatMap(frame -> { try { int streamId = frame.getStreamId(); - UnicastProcessor receiver; + Subscriber receiver; switch (frame.getType()) { case FIRE_AND_FORGET: return handleFireAndForget(streamId, fireAndForget(new PayloadImpl(frame))); @@ -174,33 +175,25 @@ public ServerReactiveSocket start() { // Lease must not be received here as this is the server end of the socket which sends leases. return Mono.empty(); case NEXT: - synchronized (ServerReactiveSocket.this) { - receiver = receivers.get(streamId); - } + receiver = getChannelProcessor(streamId); if (receiver != null) { receiver.onNext(new PayloadImpl(frame)); } return Mono.empty(); case COMPLETE: - synchronized (ServerReactiveSocket.this) { - receiver = receivers.get(streamId); - } + receiver = getChannelProcessor(streamId); if (receiver != null) { receiver.onComplete(); } return Mono.empty(); case ERROR: - synchronized (ServerReactiveSocket.this) { - receiver = receivers.get(streamId); - } + receiver = getChannelProcessor(streamId); if (receiver != null) { receiver.onError(new ApplicationException(new PayloadImpl(frame))); } return Mono.empty(); case NEXT_COMPLETE: - synchronized (ServerReactiveSocket.this) { - receiver = receivers.get(streamId); - } + receiver = getChannelProcessor(streamId); if (receiver != null) { receiver.onNext(new PayloadImpl(frame)); receiver.onComplete(); @@ -239,33 +232,26 @@ private void cleanup() { subscribe.dispose(); } - sendingSubscriptions.values().forEach(this::cleanUpSendingSubscription); - receivers.values().forEach(this::cleanUpReceivingSubscription); - - synchronized (this) { - sendingSubscriptions.clear(); - receivers.clear(); - } + cleanUpSendingSubscriptions(); + cleanUpChannelProcessors(); requestHandler.close().subscribe(); } - private synchronized void cleanUpSendingSubscription(Subscription subscription) { - subscription.cancel(); + private synchronized void cleanUpSendingSubscriptions() { + sendingSubscriptions.values().forEach(Subscription::cancel); + sendingSubscriptions.clear(); } - private synchronized void cleanUpReceivingSubscription(Subscription subscription) { - subscription.cancel(); + private synchronized void cleanUpChannelProcessors() { + channelProcessors.values().forEach(Subscription::cancel); + channelProcessors.clear(); } - private Mono handleFireAndForget(int streamId, Mono result) { return result .doOnSubscribe(subscription -> addSubscription(streamId, subscription)) - .doOnError(t -> { - removeSubscription(streamId); - errorConsumer.accept(t); - }) + .doOnError(errorConsumer) .doFinally(signalType -> removeSubscription(streamId)) .ignoreElement(); } @@ -278,12 +264,12 @@ private Mono handleRequestResponse(int streamId, Mono response) { Frame.PayloadFrame.from(streamId, FrameType.NEXT_COMPLETE, payload, FrameHeaderFlyweight.FLAGS_C)) .doOnCancel(() -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer); } }) .doOnError(t -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer); } }) .doFinally(signalType -> { @@ -309,17 +295,17 @@ private Mono handleStream(int streamId, Flux response, Frame firs }) .doOnCancel(() -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer); } }) .doOnError(t -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer); } }) .doOnComplete(() -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.PayloadFrame.from(streamId, FrameType.COMPLETE)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.PayloadFrame.from(streamId, FrameType.COMPLETE)).subscribe(null, errorConsumer); } }) .doFinally(signalType -> { @@ -330,24 +316,28 @@ private Mono handleStream(int streamId, Flux response, Frame firs } private Mono handleChannel(int streamId, Frame firstFrame) { - UnicastProcessor frames = UnicastProcessor.create(); + UnicastProcessor frames = UnicastProcessor.create(); + addChannelProcessor(streamId, frames); + Flux payloads = frames .doOnCancel(() -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.Cancel.from(streamId)).subscribe(null, errorConsumer); } }) .doOnError(t -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.Error.from(streamId, t)).subscribe(null, errorConsumer); } }) .doOnRequest(l -> { if (connection.availability() > 0.0) { - connection.sendOne(Frame.RequestN.from(streamId, l)).subscribe(null, errorConsumer::accept); + connection.sendOne(Frame.RequestN.from(streamId, l)).subscribe(null, errorConsumer); } }) - .cast(Payload.class); + .doFinally(signalType -> { + removeChannelProcessor(streamId); + }); return handleStream(streamId, requestChannel(payloads), firstFrame); } @@ -381,10 +371,7 @@ private Mono handleError(int streamId, Throwable t) { } private Mono handleRequestN(int streamId, Frame frame) { - Subscription subscription; - synchronized (this) { - subscription = sendingSubscriptions.get(streamId); - } + final Subscription subscription = getSubscription(streamId); if (subscription != null) { int n = Frame.RequestN.requestN(frame); subscription.request(n >= Integer.MAX_VALUE ? Long.MAX_VALUE : n); @@ -396,8 +383,23 @@ private synchronized void addSubscription(int streamId, Subscription subscriptio sendingSubscriptions.put(streamId, subscription); } + private synchronized Subscription getSubscription(int streamId) { + return sendingSubscriptions.get(streamId); + } + private synchronized void removeSubscription(int streamId) { sendingSubscriptions.remove(streamId); } + private synchronized void addChannelProcessor(int streamId, UnicastProcessor processor) { + channelProcessors.put(streamId, processor); + } + + private synchronized UnicastProcessor getChannelProcessor(int streamId) { + return channelProcessors.get(streamId); + } + + private synchronized void removeChannelProcessor(int streamId) { + channelProcessors.remove(streamId); + } } \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java index e87953c29..b9695cd54 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/frame/PayloadReassembler.java @@ -15,9 +15,9 @@ */ package io.reactivesocket.frame; +import io.netty.util.collection.IntObjectHashMap; import io.reactivesocket.Frame; import io.reactivesocket.Payload; -import io.reactivesocket.internal.Int2ObjectHashMap; import io.reactivesocket.util.PayloadImpl; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -25,7 +25,7 @@ public class PayloadReassembler implements Subscriber { private final Subscriber child; - private final Int2ObjectHashMap payloadByStreamId = new Int2ObjectHashMap<>(); + private final IntObjectHashMap payloadByStreamId = new IntObjectHashMap<>(); private PayloadReassembler(final Subscriber child) { this.child = child; diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/CollectionUtil.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/CollectionUtil.java deleted file mode 100644 index 51e5fac87..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/CollectionUtil.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2014-2017 Real Logic Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.internal; - -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.ToIntFunction; - -/** - * Utility functions for collection objects. - */ -public class CollectionUtil -{ - /** - * A getOrDefault that doesn't create garbage if its suppler is non-capturing. - * - * @param map to perform the lookup on. - * @param key on which the lookup is done. - * @param supplier of the default value if one is not found. - * @param type of the key - * @param type of the value - * @return the value if found or a new default which as been added to the map. - */ - public static V getOrDefault(final Map map, final K key, final Function supplier) - { - V value = map.get(key); - if (value == null) - { - value = supplier.apply(key); - map.put(key, value); - } - - return value; - } - - /** - * Garbage free sum function. - * - * Note: the list must implement {@link java.util.RandomAccess} to be efficient. - * - * @param values the list of input values - * @param function function that map each value to an int - * @param the value to add up - * @return the sum of all the int values returned for each member of the list. - */ - public static int sum(final List values, final ToIntFunction function) - { - int total = 0; - - final int size = values.size(); - for (int i = 0; i < size; i++) - { - final V value = values.get(i); - total += function.applyAsInt(value); - } - - return total; - } - - /** - * Validate that a load factor is greater than 0 and less than 1.0. - * - * @param loadFactor to be validated. - */ - public static void validateLoadFactor(final float loadFactor) - { - if (loadFactor <= 0 || loadFactor >= 1.0) - { - throw new IllegalArgumentException("Load factors must be > 0.0 and < 1.0"); - } - } - - /** - * Validate that a number is a power of two. - * - * @param value to be validated. - */ - public static void validatePositivePowerOfTwo(final int value) - { - if (value > 0 && 1 == (value & (value - 1))) - { - throw new IllegalStateException("Value must be a positive power of two"); - } - } - - /** - * Remove element from a list if it matches a predicate. - * - * Note: the list must implement {@link java.util.RandomAccess} to be efficient. - * - * @param values to be iterated over. - * @param predicate to test the value against - * @param type of the value. - * @return the number of items remove. - */ - public static int removeIf(final List values, final Predicate predicate) - { - int size = values.size(); - int total = 0; - - for (int i = 0; i < size; ) - { - final T value = values.get(i); - if (predicate.test(value)) - { - values.remove(i); - total++; - size--; - } - else - { - i++; - } - } - - return total; - } -} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/Hashing.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/Hashing.java deleted file mode 100644 index e1a4d3606..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/Hashing.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2014-2017 Real Logic Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.internal; - -/** - * Hashing functions for applying to integers. - */ -public class Hashing -{ - /** - * Generate a hash for an int value. This is a no op. - * - * @param value to be hashed. - * @return the hashed value. - */ - public static int hash(final int value) - { - return value; - } - - /** - * Generate a hash for an long value. This is a no op. - * - * @param value to be hashed. - * @return the hashed value. - */ - public static int hash(final long value) - { - return (int)value ^ (int)(value >>> 32); - } - - /** - * Generate a hash for a int value. - * - * @param value to be hashed. - * @param mask mask to be applied that must be a power of 2 - 1. - * @return the hash of the value. - */ - public static int hash(final int value, final int mask) - { - final int hash = value ^ (value >>> 16); - - return hash & mask; - } - - /** - * Generate a hash for a long value. - * - * @param value to be hashed. - * @param mask mask to be applied that must be a power of 2 - 1. - * @return the hash of the value. - */ - public static int hash(final long value, final int mask) - { - int hash = (int)value ^ (int)(value >>> 32); - hash = hash ^ (hash >>> 16); - - return hash & mask; - } - - /** - * Generate a hash for two ints. - * - * @param valueOne to be hashed. - * @param valueTwo to be hashed. - * @param mask mask to be applied that must be a power of 2 - 1. - * @return a hash of the values. - */ - public static int hash(final int valueOne, final int valueTwo, final int mask) - { - int hash = valueOne ^ valueTwo; - hash = hash ^ (hash >>> 16); - - return hash & mask; - } - - /** - * Generate an even hash for a int value. - * - * @param value to be hashed. - * @param mask mask to be applied that must be a power of 2 - 1. - * @return the hash of the value which is always even. - */ - public static int evenHash(final int value, final int mask) - { - final int hash = (value << 1) - (value << 8); - - return hash & mask; - } - - /** - * Generate an even hash for a long value. - * - * @param value to be hashed. - * @param mask mask to be applied that must be a power of 2 - 1. - * @return the hash of the value which is always even. - */ - public static int evenHash(final long value, final int mask) - { - int hash = (int)value ^ (int)(value >>> 32); - hash = (hash << 1) - (hash << 8); - - return hash & mask; - } -} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/internal/Int2ObjectHashMap.java b/reactivesocket-core/src/main/java/io/reactivesocket/internal/Int2ObjectHashMap.java deleted file mode 100644 index dc3396ac0..000000000 --- a/reactivesocket-core/src/main/java/io/reactivesocket/internal/Int2ObjectHashMap.java +++ /dev/null @@ -1,768 +0,0 @@ -/* - * Copyright 2014-2017 Real Logic Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.reactivesocket.internal; - -import io.reactivesocket.util.BitUtil; - -import java.util.*; -import java.util.function.IntFunction; - -import static java.util.Objects.requireNonNull; - -/** - * {@link java.util.Map} implementation specialised for int keys using open addressing and - * linear probing for cache efficient access. - * - * @param type of values stored in the {@link java.util.Map} - */ -public class Int2ObjectHashMap - implements Map -{ - private final float loadFactor; - private int resizeThreshold; - private int size; - - private int[] keys; - private Object[] values; - - private final ValueCollection valueCollection; - private final KeySet keySet; - private final EntrySet entrySet; - - public Int2ObjectHashMap() - { - this(8, 0.67f); - } - - /** - * Construct a new map allowing a configuration for initial capacity and load factor. - * - * @param initialCapacity for the backing array - * @param loadFactor limit for resizing on puts - */ - public Int2ObjectHashMap( - final int initialCapacity, - final float loadFactor) - { - CollectionUtil.validateLoadFactor(loadFactor); - - this.loadFactor = loadFactor; - final int capacity = BitUtil.findNextPositivePowerOfTwo(initialCapacity); - resizeThreshold = (int)(capacity * loadFactor); - - keys = new int[capacity]; - values = new Object[capacity]; - - // Cached to avoid allocation. - valueCollection = new ValueCollection<>(); - keySet = new KeySet(); - entrySet = new EntrySet<>(); - } - - /** - * Get the load factor beyond which the map will increase size. - * - * @return load factor for when the map should increase size. - */ - public float loadFactor() - { - return loadFactor; - } - - /** - * Get the total capacity for the map to which the load factor with be a fraction of. - * - * @return the total capacity for the map. - */ - public int capacity() - { - return values.length; - } - - /** - * Get the actual threshold which when reached the map resize. - * This is a function of the current capacity and load factor. - * - * @return the threshold when the map will resize. - */ - public int resizeThreshold() - { - return resizeThreshold; - } - - /** - * {@inheritDoc} - */ - public int size() - { - return size; - } - - /** - * {@inheritDoc} - */ - public boolean isEmpty() - { - return 0 == size; - } - - /** - * {@inheritDoc} - */ - public boolean containsKey(final Object key) - { - return containsKey(((Integer)key).intValue()); - } - - /** - * Overloaded version of {@link Map#containsKey(Object)} that takes a primitive int key. - * - * @param key for indexing the {@link Map} - * @return true if the key is found otherwise false. - */ - public boolean containsKey(final int key) - { - final int mask = values.length - 1; - int index = Hashing.hash(key, mask); - - boolean found = false; - while (null != values[index]) - { - if (key == keys[index]) - { - found = true; - break; - } - - index = ++index & mask; - } - - return found; - } - - /** - * {@inheritDoc} - */ - public boolean containsValue(final Object value) - { - boolean found = false; - if (null != value) - { - for (final Object v : values) - { - if (value.equals(v)) - { - found = true; - break; - } - } - } - - return found; - } - - /** - * {@inheritDoc} - */ - public V get(final Object key) - { - return get(((Integer)key).intValue()); - } - - /** - * Overloaded version of {@link Map#get(Object)} that takes a primitive int key. - * - * @param key for indexing the {@link Map} - * @return the value if found otherwise null - */ - @SuppressWarnings("unchecked") - public V get(final int key) - { - final int mask = values.length - 1; - int index = Hashing.hash(key, mask); - - Object value; - while (null != (value = values[index])) - { - if (key == keys[index]) - { - break; - } - - index = ++index & mask; - } - - return (V)value; - } - - /** - * Get a value for a given key, or if it does not exist then default the value via a - * {@link java.util.function.IntFunction} and put it in the map. - * - * Primitive specialized version of {@link java.util.Map#computeIfAbsent}. - * - * @param key to search on. - * @param mappingFunction to provide a value if the get returns null. - * @return the value if found otherwise the default. - */ - public V computeIfAbsent(final int key, final IntFunction mappingFunction) - { - V value = get(key); - if (value == null) - { - value = mappingFunction.apply(key); - if (value != null) - { - put(key, value); - } - } - - return value; - } - - /** - * {@inheritDoc} - */ - public V put(final Integer key, final V value) - { - return put(key.intValue(), value); - } - - /** - * Overloaded version of {@link Map#put(Object, Object)} that takes a primitive int key. - * - * @param key for indexing the {@link Map} - * @param value to be inserted in the {@link Map} - * @return the previous value if found otherwise null - */ - @SuppressWarnings("unchecked") - public V put(final int key, final V value) - { - requireNonNull(value, "Value cannot be null"); - - V oldValue = null; - final int mask = values.length - 1; - int index = Hashing.hash(key, mask); - - while (null != values[index]) - { - if (key == keys[index]) - { - oldValue = (V)values[index]; - break; - } - - index = ++index & mask; - } - - if (null == oldValue) - { - ++size; - keys[index] = key; - } - - values[index] = value; - - if (size > resizeThreshold) - { - increaseCapacity(); - } - - return oldValue; - } - - /** - * {@inheritDoc} - */ - public V remove(final Object key) - { - return remove(((Integer)key).intValue()); - } - - /** - * Overloaded version of {@link Map#remove(Object)} that takes a primitive int key. - * - * @param key for indexing the {@link Map} - * @return the value if found otherwise null - */ - @SuppressWarnings("unchecked") - public V remove(final int key) - { - final int mask = values.length - 1; - int index = Hashing.hash(key, mask); - - Object value; - while (null != (value = values[index])) - { - if (key == keys[index]) - { - values[index] = null; - --size; - - compactChain(index); - break; - } - - index = ++index & mask; - } - - return (V)value; - } - - /** - * {@inheritDoc} - */ - public void clear() - { - size = 0; - Arrays.fill(values, null); - } - - /** - * Compact the {@link Map} backing arrays by rehashing with a capacity just larger than current size - * and giving consideration to the load factor. - */ - public void compact() - { - final int idealCapacity = (int)Math.round(size() * (1.0d / loadFactor)); - rehash(BitUtil.findNextPositivePowerOfTwo(idealCapacity)); - } - - /** - * {@inheritDoc} - */ - public void putAll(final Map map) - { - for (final Entry entry : map.entrySet()) - { - put(entry.getKey(), entry.getValue()); - } - } - - /** - * {@inheritDoc} - */ - public KeySet keySet() - { - return keySet; - } - - /** - * {@inheritDoc} - */ - public Collection values() - { - return valueCollection; - } - - /** - * {@inheritDoc} - */ - public Set> entrySet() - { - return entrySet; - } - - /** - * {@inheritDoc} - */ - public String toString() - { - final StringBuilder sb = new StringBuilder(); - sb.append('{'); - - for (final Entry entry : entrySet()) - { - sb.append(entry.getKey().intValue()); - sb.append('='); - sb.append(entry.getValue()); - sb.append(", "); - } - - if (sb.length() > 1) - { - sb.setLength(sb.length() - 2); - } - - sb.append('}'); - - return sb.toString(); - } - - /** - * Primitive specialised version of {@link #replace(Object, Object)} - * - * @param key key with which the specified value is associated - * @param value value to be associated with the specified key - * @return the previous value associated with the specified key, or - * {@code null} if there was no mapping for the key. - */ - public V replace(final int key, final V value) - { - V curValue = get(key); - if (curValue != null) - { - curValue = put(key, value); - } - - return curValue; - } - - /** - * Primitive specialised version of {@link #replace(Object, Object, Object)} - * - * @param key key with which the specified value is associated - * @param oldValue value expected to be associated with the specified key - * @param newValue value to be associated with the specified key - * @return {@code true} if the value was replaced - */ - public boolean replace(final int key, final V oldValue, final V newValue) - { - final Object curValue = get(key); - if (curValue == null || !Objects.equals(curValue, oldValue)) - { - return false; - } - - put(key, newValue); - - return true; - } - - private void increaseCapacity() - { - final int newCapacity = values.length << 1; - if (newCapacity < 0) - { - throw new IllegalStateException("Max capacity reached at size=" + size); - } - - rehash(newCapacity); - } - - private void rehash(final int newCapacity) - { - final int mask = newCapacity - 1; - resizeThreshold = (int)(newCapacity * loadFactor); - - final int[] tempKeys = new int[newCapacity]; - final Object[] tempValues = new Object[newCapacity]; - - for (int i = 0, size = values.length; i < size; i++) - { - final Object value = values[i]; - if (null != value) - { - final int key = keys[i]; - int newHash = Hashing.hash(key, mask); - while (null != tempValues[newHash]) - { - newHash = ++newHash & mask; - } - - tempKeys[newHash] = key; - tempValues[newHash] = value; - } - } - - keys = tempKeys; - values = tempValues; - } - - @SuppressWarnings("FinalParameters") - private void compactChain(int deleteIndex) - { - final int mask = values.length - 1; - int index = deleteIndex; - while (true) - { - index = ++index & mask; - if (null == values[index]) - { - break; - } - - final int hash = Hashing.hash(keys[index], mask); - - if ((index < hash && (hash <= deleteIndex || deleteIndex <= index)) || - (hash <= deleteIndex && deleteIndex <= index)) - { - keys[deleteIndex] = keys[index]; - values[deleteIndex] = values[index]; - - values[index] = null; - deleteIndex = index; - } - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Internal Sets and Collections - /////////////////////////////////////////////////////////////////////////////////////////////// - - public class KeySet extends AbstractSet - { - private final KeyIterator iterator = new KeyIterator(); - - public int size() - { - return Int2ObjectHashMap.this.size(); - } - - public boolean contains(final Object o) - { - return Int2ObjectHashMap.this.containsKey(o); - } - - public boolean contains(final int key) - { - return Int2ObjectHashMap.this.containsKey(key); - } - - public KeyIterator iterator() - { - iterator.reset(); - - return iterator; - } - - public boolean remove(final Object o) - { - return null != Int2ObjectHashMap.this.remove(o); - } - - public boolean remove(final int key) - { - return null != Int2ObjectHashMap.this.remove(key); - } - - public void clear() - { - Int2ObjectHashMap.this.clear(); - } - } - - private class ValueCollection extends AbstractCollection - { - private final ValueIterator iterator = new ValueIterator(); - - public int size() - { - return Int2ObjectHashMap.this.size(); - } - - public boolean contains(final Object o) - { - return Int2ObjectHashMap.this.containsValue(o); - } - - public ValueIterator iterator() - { - iterator.reset(); - - return iterator; - } - - public void clear() - { - Int2ObjectHashMap.this.clear(); - } - } - - private class EntrySet extends AbstractSet> - { - private final EntryIterator iterator = new EntryIterator(); - - public int size() - { - return Int2ObjectHashMap.this.size(); - } - - public Iterator> iterator() - { - iterator.reset(); - - return iterator; - } - - public void clear() - { - Int2ObjectHashMap.this.clear(); - } - } - - /////////////////////////////////////////////////////////////////////////////////////////////// - // Iterators - /////////////////////////////////////////////////////////////////////////////////////////////// - - abstract class AbstractIterator implements Iterator - { - private int posCounter; - private int stopCounter; - private boolean isPositionValid = false; - protected int[] keys; - protected Object[] values; - - protected AbstractIterator() - { - reset(); - } - - protected int position() - { - return posCounter & values.length - 1; - } - - public boolean hasNext() - { - final int mask = values.length - 1; - boolean hasNext = false; - for (int i = posCounter - 1; i >= stopCounter; i--) - { - final int index = i & mask; - if (null != values[index]) - { - hasNext = true; - break; - } - } - - return hasNext; - } - - protected void findNext() - { - final int mask = values.length - 1; - isPositionValid = false; - - for (int i = posCounter - 1; i >= stopCounter; i--) - { - final int index = i & mask; - if (null != values[index]) - { - posCounter = i; - isPositionValid = true; - return; - } - } - - throw new NoSuchElementException(); - } - - public abstract T next(); - - public void remove() - { - if (isPositionValid) - { - final int position = position(); - values[position] = null; - --size; - - compactChain(position); - - isPositionValid = false; - } - else - { - throw new IllegalStateException(); - } - } - - void reset() - { - keys = Int2ObjectHashMap.this.keys; - values = Int2ObjectHashMap.this.values; - final int capacity = values.length; - - int i = capacity; - if (null != values[capacity - 1]) - { - i = 0; - for (int size = capacity; i < size; i++) - { - if (null == values[i]) - { - break; - } - } - } - - stopCounter = i; - posCounter = i + capacity; - } - } - - public class ValueIterator extends AbstractIterator - { - @SuppressWarnings("unchecked") - public T next() - { - findNext(); - - return (T)values[position()]; - } - } - - public class KeyIterator extends AbstractIterator - { - public Integer next() - { - return nextInt(); - } - - public int nextInt() - { - findNext(); - - return keys[position()]; - } - } - - @SuppressWarnings("unchecked") - public class EntryIterator - extends AbstractIterator> - implements Entry - { - public Entry next() - { - findNext(); - - return this; - } - - public Integer getKey() - { - return keys[position()]; - } - - public V getValue() - { - return (V)values[position()]; - } - - public V setValue(final V value) - { - requireNonNull(value); - - final int pos = position(); - final Object oldValue = values[pos]; - values[pos] = value; - - return (V)oldValue; - } - } -} \ No newline at end of file diff --git a/reactivesocket-core/src/main/java/io/reactivesocket/util/PayloadImpl.java b/reactivesocket-core/src/main/java/io/reactivesocket/util/PayloadImpl.java index 6fcaaf95d..f4bf34143 100644 --- a/reactivesocket-core/src/main/java/io/reactivesocket/util/PayloadImpl.java +++ b/reactivesocket-core/src/main/java/io/reactivesocket/util/PayloadImpl.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * An implementation of {@link Payload}. This implementation is not thread-safe, and hence any method can not be @@ -45,7 +46,7 @@ public PayloadImpl(String data) { } public PayloadImpl(String data, String metadata) { - this(data, Charset.defaultCharset(), metadata, Charset.defaultCharset()); + this(data, StandardCharsets.UTF_8, metadata, StandardCharsets.UTF_8); } public PayloadImpl(String data, Charset dataCharset) { diff --git a/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java b/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java index 8f1340fd8..8baf86cad 100644 --- a/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java +++ b/reactivesocket-transport-local/src/main/java/io/reactivesocket/transport/local/LocalDuplexConnection.java @@ -28,8 +28,9 @@ public Mono send(Publisher frames) { @Override public Mono sendOne(Frame frame) { - out.onNext(frame); - return Mono.empty(); + return Mono.fromRunnable(() -> + out.onNext(frame) + ); } @Override