Skip to content

Commit b5b0441

Browse files
committed
Custom CBOR codec (for metadata)
For default metadata mime-type, using Jackson CBOR codec, produces more allocations (one buffer allocation per value) which are unnecessary. This was the major driving factor to invest in a _simple_ codec that can reduce allocations and be more suitable for ReactiveSocket design (using thread-local pooled buffers). Although, the codec is intended to be used only for a map type, but it is rather generic to be used for a majority of CBOR data types. If this proves useful, we can extend this for the remaining data types.
1 parent 98dafba commit b5b0441

36 files changed

Lines changed: 3055 additions & 116 deletions

build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ subprojects {
2323
testCompile 'org.mockito:mockito-core:1.10.19'
2424
testRuntime 'org.slf4j:slf4j-simple:1.7.12'
2525
}
26+
27+
test {
28+
testLogging {
29+
showStandardStreams = true
30+
}
31+
}
2632
}
2733

2834
// support for snapshot/final releases via versioned branch names like 1.x

reactivesocket-mime-types/src/main/java/io/reactivesocket/mimetypes/KVMetadata.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package io.reactivesocket.mimetypes;
22

3+
import org.agrona.MutableDirectBuffer;
4+
35
import java.nio.ByteBuffer;
46
import java.nio.charset.Charset;
57
import java.util.Map;
8+
import java.util.function.Function;
69

710
/**
811
* A representation of ReactiveSocket metadata as a key-value pair.
@@ -18,7 +21,17 @@ public interface KVMetadata extends Map<String, ByteBuffer> {
1821
* @param valueEncoding Encoding for the value.
1922
*
2023
* @return Value as a string with the passed {@code valueEncoding}
24+
* @throws NullPointerException If the key does not exist.
2125
*/
2226
String getAsString(String key, Charset valueEncoding);
2327

28+
/**
29+
* Creates a new copy of this metadata.
30+
*
31+
* @param newBufferFactory A factory to create new buffer instances to copy, if required. The argument to the
32+
* function is the capacity of the new buffer.
33+
*
34+
* @return New copy of this metadata.
35+
*/
36+
KVMetadata duplicate(Function<Integer, MutableDirectBuffer> newBufferFactory);
2437
}

reactivesocket-mime-types/src/main/java/io/reactivesocket/mimetypes/MimeType.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,15 @@ default <T> T decodeMetadata(Frame toDecode, Class<T> clazz) {
4242
/**
4343
* Decodes the passed buffer to the specified {@code clazz}.
4444
*
45+
* @param <T> Type of the class to which the buffer will be decoded.
46+
*
4547
* @param toDecode buffer to be decoded.
4648
* @param clazz Class to which the buffer will be decoded.
47-
*
48-
* @param <T> Type of the class to which the buffer will be decoded.
49+
* @param offset Offset in the buffer.
4950
*
5051
* @return Instance of the class post decode.
5152
*/
52-
<T> T decodeMetadata(DirectBuffer toDecode, Class<T> clazz);
53+
<T> T decodeMetadata(DirectBuffer toDecode, Class<T> clazz, int offset);
5354

5455
/**
5556
* Encodes passed metadata to a buffer.
@@ -76,12 +77,12 @@ default <T> T decodeMetadata(Frame toDecode, Class<T> clazz) {
7677
/**
7778
* Encodes passed metadata to the passed buffer.
7879
*
80+
* @param <T> Type of the object to encode.
7981
* @param buffer Encodes the metadata to this buffer.
8082
* @param toEncode Metadata to encode.
81-
*
82-
* @param <T> Type of the object to encode.
83+
* @param offset Offset in the buffer to start writing.
8384
*/
84-
<T> void encodeMetadataTo(MutableDirectBuffer buffer, T toEncode);
85+
<T> void encodeMetadataTo(MutableDirectBuffer buffer, T toEncode, int offset);
8586

8687
/**
8788
* Encodes passed metadata to the passed buffer.
@@ -122,14 +123,15 @@ default <T> T decodeData(Frame toDecode, Class<T> clazz) {
122123
/**
123124
* Decodes the passed buffer to the specified {@code clazz}.
124125
*
126+
* @param <T> Type of the class to which the buffer will be decoded.
127+
*
125128
* @param toDecode buffer to be decoded.
126129
* @param clazz Class to which the buffer will be decoded.
127-
*
128-
* @param <T> Type of the class to which the buffer will be decoded.
130+
* @param offset Offset in the buffer.
129131
*
130132
* @return Instance of the class post decode.
131133
*/
132-
<T> T decodeData(DirectBuffer toDecode, Class<T> clazz);
134+
<T> T decodeData(DirectBuffer toDecode, Class<T> clazz, int offset);
133135

134136
/**
135137
* Encodes passed data to a buffer.
@@ -156,12 +158,12 @@ default <T> T decodeData(Frame toDecode, Class<T> clazz) {
156158
/**
157159
* Encodes passed data to the passed buffer.
158160
*
161+
* @param <T> Type of the object to encode.
159162
* @param buffer Encodes the data to this buffer.
160163
* @param toEncode Data to encode.
161-
*
162-
* @param <T> Type of the object to encode.
164+
* @param offset Offset in the buffer to start writing.
163165
*/
164-
<T> void encodeDataTo(MutableDirectBuffer buffer, T toEncode);
166+
<T> void encodeDataTo(MutableDirectBuffer buffer, T toEncode, int offset);
165167

166168
/**
167169
* Encodes passed data to the passed buffer.

reactivesocket-mime-types/src/main/java/io/reactivesocket/mimetypes/MimeTypeFactory.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import io.reactivesocket.ConnectionSetupPayload;
44
import io.reactivesocket.mimetypes.internal.Codec;
5-
import io.reactivesocket.mimetypes.internal.ReactiveSocketDefaultMetadataCodec;
65
import io.reactivesocket.mimetypes.internal.cbor.CborCodec;
6+
import io.reactivesocket.mimetypes.internal.cbor.ReactiveSocketDefaultMetadataCodec;
77
import io.reactivesocket.mimetypes.internal.json.JsonCodec;
88
import org.agrona.DirectBuffer;
99
import org.agrona.MutableDirectBuffer;
@@ -122,8 +122,8 @@ public <T> T decodeMetadata(ByteBuffer toDecode, Class<T> clazz) {
122122
}
123123

124124
@Override
125-
public <T> T decodeMetadata(DirectBuffer toDecode, Class<T> clazz) {
126-
return metaCodec.decode(toDecode, clazz);
125+
public <T> T decodeMetadata(DirectBuffer toDecode, Class<T> clazz, int offset) {
126+
return metaCodec.decode(toDecode, offset, clazz);
127127
}
128128

129129
@Override
@@ -137,8 +137,8 @@ public <T> DirectBuffer encodeMetadataDirect(T toEncode) {
137137
}
138138

139139
@Override
140-
public <T> void encodeMetadataTo(MutableDirectBuffer buffer, T toEncode) {
141-
metaCodec.encodeTo(buffer, toEncode);
140+
public <T> void encodeMetadataTo(MutableDirectBuffer buffer, T toEncode, int offset) {
141+
metaCodec.encodeTo(buffer, toEncode, offset);
142142
}
143143

144144
@Override
@@ -152,8 +152,8 @@ public <T> T decodeData(ByteBuffer toDecode, Class<T> clazz) {
152152
}
153153

154154
@Override
155-
public <T> T decodeData(DirectBuffer toDecode, Class<T> clazz) {
156-
return dataCodec.decode(toDecode, clazz);
155+
public <T> T decodeData(DirectBuffer toDecode, Class<T> clazz, int offset) {
156+
return dataCodec.decode(toDecode, offset, clazz);
157157
}
158158

159159
@Override
@@ -167,8 +167,8 @@ public <T> DirectBuffer encodeDataDirect(T toEncode) {
167167
}
168168

169169
@Override
170-
public <T> void encodeDataTo(MutableDirectBuffer buffer, T toEncode) {
171-
dataCodec.encodeTo(buffer, toEncode);
170+
public <T> void encodeDataTo(MutableDirectBuffer buffer, T toEncode, int offset) {
171+
dataCodec.encodeTo(buffer, toEncode, offset);
172172
}
173173

174174
@Override

reactivesocket-mime-types/src/main/java/io/reactivesocket/mimetypes/internal/AbstractJacksonCodec.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ public <T> T decode(ByteBuffer buffer, Class<T> tClass) {
6464
}
6565

6666
@Override
67-
public <T> T decode(DirectBuffer buffer, Class<T> tClass) {
67+
public <T> T decode(DirectBuffer buffer, int offset, Class<T> tClass) {
6868
DirectBufferInputStream stream = directInWrappers.get();
69-
stream.wrap(buffer);
69+
stream.wrap(buffer, offset, buffer.capacity());
7070
return _decode(stream, tClass);
7171
}
7272

@@ -88,9 +88,9 @@ public <T> void encodeTo(ByteBuffer buffer, T toEncode) {
8888
}
8989

9090
@Override
91-
public <T> void encodeTo(MutableDirectBuffer buffer, T toEncode) {
91+
public <T> void encodeTo(MutableDirectBuffer buffer, T toEncode, int offset) {
9292
MutableDirectBufferOutputStream stream = directOutWrappers.get();
93-
stream.wrap(buffer);
93+
stream.wrap(buffer, offset, buffer.capacity());
9494
_encodeTo(stream, toEncode);
9595
}
9696

reactivesocket-mime-types/src/main/java/io/reactivesocket/mimetypes/internal/Codec.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ public interface Codec {
99

1010
<T> T decode(ByteBuffer buffer, Class<T> tClass);
1111

12-
<T> T decode(DirectBuffer buffer, Class<T> tClass);
12+
<T> T decode(DirectBuffer buffer, int offset, Class<T> tClass);
1313

1414
<T> ByteBuffer encode(T toEncode);
1515

1616
<T> DirectBuffer encodeDirect(T toEncode);
1717

1818
<T> void encodeTo(ByteBuffer buffer, T toEncode);
1919

20-
<T> void encodeTo(MutableDirectBuffer buffer, T toEncode);
20+
<T> void encodeTo(MutableDirectBuffer buffer, T toEncode, int offset);
2121
}

reactivesocket-mime-types/src/main/java/io/reactivesocket/mimetypes/internal/KVMetadataImpl.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package io.reactivesocket.mimetypes.internal;
22

33
import io.reactivesocket.mimetypes.KVMetadata;
4+
import org.agrona.MutableDirectBuffer;
45

56
import java.nio.ByteBuffer;
67
import java.nio.charset.Charset;
78
import java.util.Collection;
89
import java.util.HashMap;
910
import java.util.Map;
1011
import java.util.Set;
12+
import java.util.function.Function;
1113

1214
public class KVMetadataImpl implements KVMetadata {
1315

@@ -37,14 +39,20 @@ public String getAsString(String key, Charset valueEncoding) {
3739
ByteBuffer toReturn = get(key);
3840

3941
if (null != toReturn) {
40-
byte[] dst = new byte[toReturn.limit() - toReturn.position()];
42+
byte[] dst = new byte[toReturn.remaining()];
4143
toReturn.get(dst);
4244
return new String(dst, valueEncoding);
4345
}
4446

4547
return null;
4648
}
4749

50+
@Override
51+
public KVMetadata duplicate(Function<Integer, MutableDirectBuffer> newBufferFactory) {
52+
Map<String, ByteBuffer> copy = new HashMap<>(store);
53+
return new KVMetadataImpl(copy);
54+
}
55+
4856
@Override
4957
public int size() {
5058
return store.size();
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2016 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package io.reactivesocket.mimetypes.internal;
19+
20+
public class MalformedInputException extends RuntimeException {
21+
22+
private static final long serialVersionUID = 3130502874275862715L;
23+
24+
public MalformedInputException(String message) {
25+
super(message);
26+
}
27+
28+
public MalformedInputException(String message, Throwable cause) {
29+
super(message, cause);
30+
}
31+
32+
public MalformedInputException(Throwable cause) {
33+
super(cause);
34+
}
35+
36+
public MalformedInputException(String message, Throwable cause, boolean enableSuppression,
37+
boolean writableStackTrace) {
38+
super(message, cause, enableSuppression, writableStackTrace);
39+
}
40+
}

reactivesocket-mime-types/src/main/java/io/reactivesocket/mimetypes/internal/ReactiveSocketDefaultMetadataCodec.java

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)