Skip to content

Commit e10436d

Browse files
committed
output api: doc, code cleanup + refactor
- move split and transferTo(ByteBuffer) out of public API - use more performant version of send(String)
1 parent aef9d6a commit e10436d

File tree

32 files changed

+706
-199
lines changed

32 files changed

+706
-199
lines changed

jooby/src/main/java/io/jooby/ServerSentMessage.java

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
import static java.nio.charset.StandardCharsets.UTF_8;
99

10+
import java.nio.ByteBuffer;
11+
import java.util.ArrayList;
12+
import java.util.Iterator;
1013
import java.util.function.IntPredicate;
1114

1215
import edu.umd.cs.findbugs.annotations.NonNull;
@@ -156,7 +159,7 @@ public ServerSentMessage(@NonNull Object data) {
156159

157160
IntPredicate nl = ch -> ch == '\n';
158161
var message = encoder.encode(ctx, data);
159-
var lines = message.split(nl);
162+
var lines = split(message.iterator(), nl);
160163
while (lines.hasNext()) {
161164
buffer.write(lines.next());
162165
if (lines.hasNext()) {
@@ -170,4 +173,46 @@ public ServerSentMessage(@NonNull Object data) {
170173
throw SneakyThrows.propagate(x);
171174
}
172175
}
176+
177+
private Iterator<ByteBuffer> split(Iterator<ByteBuffer> buffers, IntPredicate predicate) {
178+
var chunks = new ArrayList<ByteBuffer>();
179+
ByteBuffer left = null;
180+
while (buffers.hasNext()) {
181+
var buffer = buffers.next();
182+
var offset = 0;
183+
for (int i = 0; i < buffer.remaining(); i++) {
184+
var b = buffer.get(i);
185+
if (predicate.test(b)) {
186+
if (left == null) {
187+
chunks.add(buffer.duplicate().position(offset).limit(i + 1));
188+
} else {
189+
chunks.add(merge(left, buffer, offset, i + 1));
190+
left = null;
191+
}
192+
offset = i + 1;
193+
}
194+
}
195+
if (offset < buffer.remaining()) {
196+
if (left == null) {
197+
left = buffer.duplicate().position(offset);
198+
} else {
199+
left = merge(left, buffer, offset, buffer.remaining());
200+
}
201+
} else {
202+
left = null;
203+
}
204+
}
205+
if (left != null) {
206+
chunks.add(left);
207+
}
208+
return chunks.iterator();
209+
}
210+
211+
private ByteBuffer merge(ByteBuffer tail, ByteBuffer buffer, int offset, int index) {
212+
ByteBuffer chunk = ByteBuffer.allocate(tail.remaining() + index - offset);
213+
chunk.put(tail);
214+
chunk.put(buffer.duplicate().position(offset).limit(index));
215+
chunk.flip();
216+
return chunk;
217+
}
173218
}

jooby/src/main/java/io/jooby/internal/handler/ChunkedSubscriber.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public void onComplete() {
119119
private static Output prepend(Context ctx, Output data, byte c) {
120120
var buffer = ctx.getOutputFactory().newCompositeOutput();
121121
buffer.write(c);
122-
data.accept(buffer::write);
122+
data.transferTo(buffer::write);
123123
return buffer;
124124
}
125125

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.output;
7+
8+
import java.nio.ByteBuffer;
9+
import java.nio.charset.Charset;
10+
11+
import edu.umd.cs.findbugs.annotations.NonNull;
12+
import io.jooby.Context;
13+
import io.jooby.SneakyThrows;
14+
import io.jooby.output.Output;
15+
16+
public class ByteArrayWrappedOutput implements Output {
17+
18+
private final byte[] buffer;
19+
20+
public ByteArrayWrappedOutput(byte[] source) {
21+
this.buffer = source;
22+
}
23+
24+
@Override
25+
public Output write(byte b) {
26+
throw new UnsupportedOperationException();
27+
}
28+
29+
@Override
30+
public Output write(byte[] source) {
31+
throw new UnsupportedOperationException();
32+
}
33+
34+
@Override
35+
public Output write(byte[] source, int offset, int length) {
36+
throw new UnsupportedOperationException();
37+
}
38+
39+
@Override
40+
public Output clear() {
41+
return this;
42+
}
43+
44+
@Override
45+
public int size() {
46+
return buffer.length;
47+
}
48+
49+
@Override
50+
public void transferTo(@NonNull SneakyThrows.Consumer<ByteBuffer> consumer) {
51+
consumer.accept(ByteBuffer.wrap(buffer));
52+
}
53+
54+
@Override
55+
public ByteBuffer asByteBuffer() {
56+
return ByteBuffer.wrap(buffer);
57+
}
58+
59+
@Override
60+
public String asString(@NonNull Charset charset) {
61+
return charset.decode(asByteBuffer()).toString();
62+
}
63+
64+
@Override
65+
public String toString() {
66+
return "size=" + size();
67+
}
68+
69+
@Override
70+
public void send(Context ctx) {
71+
ctx.send(buffer);
72+
}
73+
}

jooby/src/main/java/io/jooby/output/ByteBufferOut.java renamed to jooby/src/main/java/io/jooby/internal/output/ByteBufferOutput.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
44
* Copyright 2014 Edgar Espina
55
*/
6-
package io.jooby.output;
6+
package io.jooby.internal.output;
77

88
import java.nio.ByteBuffer;
99
import java.nio.charset.Charset;
@@ -13,8 +13,9 @@
1313
import edu.umd.cs.findbugs.annotations.NonNull;
1414
import io.jooby.Context;
1515
import io.jooby.SneakyThrows;
16+
import io.jooby.output.Output;
1617

17-
class ByteBufferOut implements Output {
18+
public class ByteBufferOutput implements Output {
1819
private static final int MAX_CAPACITY = Integer.MAX_VALUE;
1920

2021
private static final int CAPACITY_THRESHOLD = 1024 * 1024 * 4;
@@ -27,20 +28,20 @@ class ByteBufferOut implements Output {
2728

2829
private int writePosition;
2930

30-
public ByteBufferOut(boolean direct, int capacity) {
31+
public ByteBufferOutput(boolean direct, int capacity) {
3132
this.buffer = allocate(capacity, direct);
3233
this.capacity = this.buffer.remaining();
3334
}
3435

35-
public ByteBufferOut(boolean direct) {
36+
public ByteBufferOutput(boolean direct) {
3637
this(direct, BUFFER_SIZE);
3738
}
3839

39-
public ByteBufferOut(int bufferSize) {
40+
public ByteBufferOutput(int bufferSize) {
4041
this(false, bufferSize);
4142
}
4243

43-
public ByteBufferOut() {
44+
public ByteBufferOutput() {
4445
this(BUFFER_SIZE);
4546
}
4647

@@ -57,12 +58,12 @@ private void ensureWritable(int length) {
5758
}
5859

5960
@Override
60-
public void accept(SneakyThrows.Consumer<ByteBuffer> consumer) {
61+
public void transferTo(@NonNull SneakyThrows.Consumer<ByteBuffer> consumer) {
6162
consumer.accept(asByteBuffer());
6263
}
6364

6465
@Override
65-
public Iterator<ByteBuffer> iterator() {
66+
public @NonNull Iterator<ByteBuffer> iterator() {
6667
return List.of(asByteBuffer()).iterator();
6768
}
6869

@@ -71,7 +72,7 @@ private int writableByteCount() {
7172
}
7273

7374
@Override
74-
public ByteBuffer asByteBuffer() {
75+
public @NonNull ByteBuffer asByteBuffer() {
7576
return this.buffer.duplicate().position(this.readPosition).limit(this.writePosition);
7677
}
7778

jooby/src/main/java/io/jooby/output/ByteBufferWrappedOutput.java renamed to jooby/src/main/java/io/jooby/internal/output/ByteBufferWrappedOutput.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,20 @@
33
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
44
* Copyright 2014 Edgar Espina
55
*/
6-
package io.jooby.output;
6+
package io.jooby.internal.output;
77

88
import java.nio.ByteBuffer;
99
import java.nio.charset.Charset;
1010

1111
import edu.umd.cs.findbugs.annotations.NonNull;
1212
import io.jooby.Context;
1313
import io.jooby.SneakyThrows;
14+
import io.jooby.output.Output;
1415

15-
class ByteBufferWrappedOutput implements Output {
16+
public class ByteBufferWrappedOutput implements Output {
1617

1718
private final ByteBuffer buffer;
1819

19-
public ByteBufferWrappedOutput(byte[] source, int offset, int length) {
20-
this(ByteBuffer.wrap(source, offset, length));
21-
}
22-
23-
public ByteBufferWrappedOutput(byte[] source) {
24-
this(source, 0, source.length);
25-
}
26-
2720
public ByteBufferWrappedOutput(ByteBuffer buffer) {
2821
this.buffer = buffer;
2922
}
@@ -55,7 +48,7 @@ public int size() {
5548
}
5649

5750
@Override
58-
public void accept(SneakyThrows.Consumer<ByteBuffer> consumer) {
51+
public void transferTo(@NonNull SneakyThrows.Consumer<ByteBuffer> consumer) {
5952
consumer.accept(buffer);
6053
}
6154

jooby/src/main/java/io/jooby/output/CompsiteByteBufferOutput.java renamed to jooby/src/main/java/io/jooby/internal/output/CompsiteByteBufferOutput.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
44
* Copyright 2014 Edgar Espina
55
*/
6-
package io.jooby.output;
6+
package io.jooby.internal.output;
77

88
import java.nio.ByteBuffer;
99
import java.nio.charset.Charset;
@@ -13,8 +13,9 @@
1313
import edu.umd.cs.findbugs.annotations.NonNull;
1414
import io.jooby.Context;
1515
import io.jooby.SneakyThrows;
16+
import io.jooby.output.Output;
1617

17-
class CompsiteByteBufferOutput implements Output {
18+
public class CompsiteByteBufferOutput implements Output {
1819
private final List<ByteBuffer> chunks = new ArrayList<>();
1920
private int size = 0;
2021
private int bufferSizeHint = BUFFER_SIZE;
@@ -70,15 +71,10 @@ public String asString(@NonNull Charset charset) {
7071
}
7172

7273
@Override
73-
public void accept(SneakyThrows.Consumer<ByteBuffer> consumer) {
74+
public void transferTo(@NonNull SneakyThrows.Consumer<ByteBuffer> consumer) {
7475
chunks.forEach(consumer);
7576
}
7677

77-
@Override
78-
public int bufferSizeHint() {
79-
return bufferSizeHint;
80-
}
81-
8278
@Override
8379
public String toString() {
8480
return "chunks=" + chunks.size() + ", size=" + size;

jooby/src/main/java/io/jooby/output/OutputOutputStream.java renamed to jooby/src/main/java/io/jooby/internal/output/OutputOutputStream.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,20 @@
33
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
44
* Copyright 2014 Edgar Espina
55
*/
6-
package io.jooby.output;
6+
package io.jooby.internal.output;
77

88
import java.io.IOException;
99
import java.io.OutputStream;
1010

1111
import edu.umd.cs.findbugs.annotations.NonNull;
12+
import io.jooby.output.Output;
1213

1314
/**
1415
* An {@link OutputStream} that writes to a {@link io.jooby.output.Output}.
1516
*
1617
* @see io.jooby.output.Output#asOutputStream()
1718
*/
18-
final class OutputOutputStream extends OutputStream {
19+
public class OutputOutputStream extends OutputStream {
1920

2021
private final Output output;
2122

jooby/src/main/java/io/jooby/output/OutputWriter.java renamed to jooby/src/main/java/io/jooby/internal/output/OutputWriter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
44
* Copyright 2014 Edgar Espina
55
*/
6-
package io.jooby.output;
6+
package io.jooby.internal.output;
77

88
import java.io.IOException;
99
import java.io.Writer;
1010
import java.nio.CharBuffer;
1111
import java.nio.charset.Charset;
1212

1313
import edu.umd.cs.findbugs.annotations.NonNull;
14+
import io.jooby.output.Output;
1415

15-
class OutputWriter extends Writer {
16+
public class OutputWriter extends Writer {
1617
private final Output output;
1718
private final Charset charset;
1819
private boolean closed;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@ReturnValuesAreNonnullByDefault
2+
package io.jooby.internal.output;
3+
4+
import edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault;

jooby/src/main/java/io/jooby/output/DefaultOutputFactory.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,22 @@
77

88
import java.nio.ByteBuffer;
99

10+
import edu.umd.cs.findbugs.annotations.NonNull;
11+
import io.jooby.internal.output.ByteArrayWrappedOutput;
12+
import io.jooby.internal.output.ByteBufferOutput;
13+
import io.jooby.internal.output.ByteBufferWrappedOutput;
14+
import io.jooby.internal.output.CompsiteByteBufferOutput;
15+
16+
/**
17+
* An output factory backed by {@link ByteBuffer}.
18+
*
19+
* @author edgar
20+
* @since 4.0.0
21+
*/
1022
public class DefaultOutputFactory implements OutputFactory {
1123
@Override
1224
public Output newBufferedOutput(int size) {
13-
return new ByteBufferOut(size);
25+
return new ByteBufferOutput(size);
1426
}
1527

1628
@Override
@@ -19,17 +31,17 @@ public Output newCompositeOutput() {
1931
}
2032

2133
@Override
22-
public Output wrap(ByteBuffer buffer) {
34+
public Output wrap(@NonNull ByteBuffer buffer) {
2335
return new ByteBufferWrappedOutput(buffer);
2436
}
2537

2638
@Override
27-
public Output wrap(byte[] bytes) {
28-
return new ByteBufferWrappedOutput(bytes);
39+
public Output wrap(@NonNull byte[] bytes) {
40+
return new ByteArrayWrappedOutput(bytes);
2941
}
3042

3143
@Override
32-
public Output wrap(byte[] bytes, int offset, int length) {
33-
return new ByteBufferWrappedOutput(bytes, offset, length);
44+
public Output wrap(@NonNull byte[] bytes, int offset, int length) {
45+
return new ByteBufferWrappedOutput(ByteBuffer.wrap(bytes, offset, length));
3446
}
3547
}

0 commit comments

Comments
 (0)