Skip to content

Commit 0e97918

Browse files
committed
Avoid eager codec serializer allocation for @httpPayload
1 parent da19294 commit 0e97918

3 files changed

Lines changed: 306 additions & 35 deletions

File tree

http/http-binding/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ dependencies {
1212
api(project(":core"))
1313
api(project(":http:http-api"))
1414
implementation(project(":logging"))
15+
16+
testImplementation(project(":codecs:json-codec", configuration = "shadow"))
1517
}

http/http-binding/src/main/java/software/amazon/smithy/java/http/binding/PayloadSerializer.java

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.time.Instant;
1414
import java.util.function.BiConsumer;
1515
import software.amazon.smithy.java.core.schema.Schema;
16+
import software.amazon.smithy.java.core.schema.SerializableShape;
1617
import software.amazon.smithy.java.core.schema.SerializableStruct;
1718
import software.amazon.smithy.java.core.schema.TraitKey;
1819
import software.amazon.smithy.java.core.serde.Codec;
@@ -25,19 +26,35 @@
2526
import software.amazon.smithy.java.io.ByteBufferOutputStream;
2627
import software.amazon.smithy.java.io.datastream.DataStream;
2728

29+
/**
30+
* Buffers the single {@code @httpPayload}-bound member into a {@link ByteBuffer}.
31+
*
32+
* <p>Because a payload binds exactly one member, this serializer receives exactly one top-level
33+
* write call.
34+
*
35+
* <ul>
36+
* <li>blob / data stream / event stream payloads hand the body off by reference via
37+
* {@link HttpBindingSerializer#setHttpPayload};</li>
38+
* <li>scalar payloads (string, number, boolean, timestamp) write their bytes directly into a
39+
* lazily-allocated {@link ByteBufferOutputStream};</li>
40+
* <li>structured payloads (struct, document, list, map) are serialized through the pooled
41+
* {@link Codec#serialize(SerializableShape)} path, which for JSON borrows a serializer from a
42+
* striped pool instead of allocating one.</li>
43+
* </ul>
44+
*/
2845
final class PayloadSerializer implements ShapeSerializer {
2946
private static final byte[] NULL_BYTES = "null".getBytes(StandardCharsets.UTF_8);
3047
private static final byte[] TRUE_BYTES = "true".getBytes(StandardCharsets.UTF_8);
3148
private static final byte[] FALSE_BYTES = "false".getBytes(StandardCharsets.UTF_8);
3249
private final HttpBindingSerializer serializer;
33-
private final ShapeSerializer structSerializer;
34-
private final ByteBufferOutputStream outputStream;
50+
private final Codec codec;
3551
private boolean payloadWritten = false;
52+
private ByteBuffer codecResult;
53+
private ByteBufferOutputStream scalarStream;
3654

3755
PayloadSerializer(HttpBindingSerializer serializer, Codec codec) {
3856
this.serializer = serializer;
39-
this.outputStream = new ByteBufferOutputStream();
40-
this.structSerializer = codec.createSerializer(outputStream);
57+
this.codec = codec;
4158
}
4259

4360
@Override
@@ -47,17 +64,21 @@ public void writeDataStream(Schema schema, DataStream value) {
4764
}
4865

4966
@Override
50-
public void writeEventStream(
51-
Schema schema,
52-
EventStream<? extends SerializableStruct> value
53-
) {
67+
public void writeEventStream(Schema schema, EventStream<? extends SerializableStruct> value) {
5468
payloadWritten = true;
5569
serializer.setEventStream(value.asWriter());
5670
}
5771

72+
private ByteBufferOutputStream scalarStream() {
73+
if (scalarStream == null) {
74+
scalarStream = new ByteBufferOutputStream();
75+
}
76+
return scalarStream;
77+
}
78+
5879
private void write(byte[] bytes) {
5980
try {
60-
outputStream.write(bytes);
81+
scalarStream().write(bytes);
6182
} catch (IOException e) {
6283
throw new SerializationException(e);
6384
}
@@ -78,7 +99,7 @@ public void writeTimestamp(Schema schema, Instant value) {
7899
@Override
79100
public void writeDocument(Schema schema, Document value) {
80101
serializer.writePayloadContentType();
81-
structSerializer.writeDocument(schema, value);
102+
codecResult = codec.serialize(ser -> ser.writeDocument(schema, value));
82103
}
83104

84105
@Override
@@ -89,18 +110,18 @@ public void writeNull(Schema schema) {
89110
@Override
90111
public void writeStruct(Schema schema, SerializableStruct struct) {
91112
serializer.writePayloadContentType();
92-
structSerializer.writeStruct(schema, struct);
113+
codecResult = codec.serialize(ser -> ser.writeStruct(schema, struct));
93114
}
94115

95116
@Override
96117
public <T> void writeList(Schema schema, T listState, int size, BiConsumer<T, ShapeSerializer> consumer) {
97118
serializer.writePayloadContentType();
98-
structSerializer.writeList(schema, listState, size, consumer);
119+
codecResult = codec.serialize(ser -> ser.writeList(schema, listState, size, consumer));
99120
}
100121

101122
@Override
102123
public <T> void writeMap(Schema schema, T mapState, int size, BiConsumer<T, MapSerializer> consumer) {
103-
structSerializer.writeMap(schema, mapState, size, consumer);
124+
codecResult = codec.serialize(ser -> ser.writeMap(schema, mapState, size, consumer));
104125
}
105126

106127
@Override
@@ -110,7 +131,7 @@ public void writeBoolean(Schema schema, boolean value) {
110131

111132
@Override
112133
public void writeByte(Schema schema, byte value) {
113-
outputStream.write(value);
134+
scalarStream().write(value);
114135
}
115136

116137
@Override
@@ -165,31 +186,17 @@ public void writeBlob(Schema schema, ByteBuffer value) {
165186
serializer.setHttpPayload(schema, DataStream.ofByteBuffer(value));
166187
}
167188

168-
@Override
169-
public void flush() {
170-
structSerializer.flush();
171-
try {
172-
outputStream.flush();
173-
} catch (IOException e) {
174-
throw new SerializationException(e);
175-
}
176-
}
177-
178-
@Override
179-
public void close() {
180-
structSerializer.close();
181-
try {
182-
outputStream.close();
183-
} catch (IOException e) {
184-
throw new SerializationException(e);
185-
}
186-
}
187-
188189
public boolean isPayloadWritten() {
189190
return payloadWritten;
190191
}
191192

192193
ByteBuffer toByteBuffer() {
193-
return outputStream.toByteBuffer();
194+
if (codecResult != null) {
195+
return codecResult;
196+
} else if (scalarStream != null) {
197+
return scalarStream.toByteBuffer();
198+
} else {
199+
return ByteBuffer.allocate(0);
200+
}
194201
}
195202
}

0 commit comments

Comments
 (0)