diff --git a/examples/java-examples/src/main/resources/logback.xml b/examples/java-examples/src/main/resources/logback.xml
new file mode 100644
index 00000000..a6ed88b2
--- /dev/null
+++ b/examples/java-examples/src/main/resources/logback.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/PacketReader.java b/src/driver/src/main/java/com/edgedb/driver/binary/PacketReader.java
index 0e4be577..29f96ddd 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/PacketReader.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/PacketReader.java
@@ -1,11 +1,9 @@
package com.edgedb.driver.binary;
-import com.edgedb.driver.binary.codecs.Codec;
-import com.edgedb.driver.binary.codecs.CodecContext;
-import com.edgedb.driver.binary.packets.shared.Annotation;
-import com.edgedb.driver.binary.packets.shared.KeyValue;
-import com.edgedb.driver.exceptions.EdgeDBException;
+import com.edgedb.driver.binary.protocol.common.Annotation;
+import com.edgedb.driver.binary.protocol.common.KeyValue;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joou.UByte;
@@ -13,7 +11,6 @@
import org.joou.ULong;
import org.joou.UShort;
-import javax.naming.OperationNotSupportedException;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
@@ -25,7 +22,21 @@
import static org.joou.Unsigned.*;
public class PacketReader {
- private final @NotNull ByteBuf buffer;
+ public static final class ScopedReader extends PacketReader implements AutoCloseable {
+ public final boolean isNoData;
+
+ public ScopedReader(@Nullable ByteBuf buffer) {
+ super(buffer == null ? Unpooled.EMPTY_BUFFER : buffer);
+ isNoData = buffer == null;
+ }
+
+ @Override
+ public void close() {
+ buffer.release();
+ }
+ }
+
+ protected final @NotNull ByteBuf buffer;
private static final @NotNull Map, Function> numberReaderMap;
private final int initPos;
@@ -80,20 +91,6 @@ public boolean isEmpty() {
return this.buffer.readableBytes() == 0;
}
- public @Nullable T deserializeByteArray(@NotNull Codec codec, CodecContext context) throws EdgeDBException, OperationNotSupportedException {
- var buff = readByteArray();
-
- if(buff == null) {
- return null;
- }
-
- try {
- return codec.deserialize(new PacketReader(buff), context);
- } finally {
- buff.release();
- }
- }
-
public byte[] consumeByteArray() {
var arr = new byte[this.buffer.readableBytes()];
this.buffer.readBytes(arr);
@@ -171,6 +168,23 @@ public short readInt16() {
return arr;
}
+ /**
+ * Reads the {@code length} number of bytes and creates a new {@linkplain ScopedReader} wrapping the bytes.
+ * @param length The number of bytes to read.
+ * @return A scoped reader whose close method releases the read bytes.
+ */
+ public ScopedReader scopedSlice(int length) {
+ return new ScopedReader(readBytes(length));
+ }
+
+ /**
+ * Calls {@code readByteArray()} and creates a new {@linkplain ScopedReader} wrapping the bytes.
+ * @return A scoped reader whose close method releases the read bytes.
+ */
+ public ScopedReader scopedSlice() {
+ return new ScopedReader(readByteArray());
+ }
+
public @Nullable ByteBuf readByteArray() {
var len = readInt32();
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/PacketSerializer.java b/src/driver/src/main/java/com/edgedb/driver/binary/PacketSerializer.java
index 943e7c30..e97ae852 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/PacketSerializer.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/PacketSerializer.java
@@ -1,8 +1,9 @@
package com.edgedb.driver.binary;
-import com.edgedb.driver.binary.packets.ServerMessageType;
-import com.edgedb.driver.binary.packets.receivable.*;
-import com.edgedb.driver.binary.packets.sendables.Sendable;
+import com.edgedb.driver.binary.protocol.ServerMessageType;
+import com.edgedb.driver.binary.protocol.Receivable;
+import com.edgedb.driver.binary.protocol.Sendable;
+import com.edgedb.driver.clients.EdgeDBBinaryClient;
import com.edgedb.driver.exceptions.ConnectionFailedException;
import com.edgedb.driver.exceptions.EdgeDBException;
import com.edgedb.driver.util.HexUtils;
@@ -24,33 +25,12 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow;
-import java.util.function.Function;
import java.util.stream.Collectors;
public class PacketSerializer {
private static final Logger logger = LoggerFactory.getLogger(PacketSerializer.class);
- private static final @NotNull Map> deserializerMap;
private static final Map, Map>> binaryEnumMap = new HashMap<>();
- static {
- deserializerMap = new HashMap<>();
-
- deserializerMap.put(ServerMessageType.AUTHENTICATION, AuthenticationStatus::new);
- deserializerMap.put(ServerMessageType.COMMAND_COMPLETE, CommandComplete::new);
- deserializerMap.put(ServerMessageType.COMMAND_DATA_DESCRIPTION, CommandDataDescription::new);
- deserializerMap.put(ServerMessageType.DATA, Data::new);
- deserializerMap.put(ServerMessageType.DUMP_BLOCK, DumpBlock::new);
- deserializerMap.put(ServerMessageType.DUMP_HEADER, DumpHeader::new);
- deserializerMap.put(ServerMessageType.ERROR_RESPONSE, ErrorResponse::new);
- deserializerMap.put(ServerMessageType.LOG_MESSAGE, LogMessage::new);
- deserializerMap.put(ServerMessageType.PARAMETER_STATUS, ParameterStatus::new);
- deserializerMap.put(ServerMessageType.READY_FOR_COMMAND, ReadyForCommand::new);
- deserializerMap.put(ServerMessageType.RESTORE_READY, RestoreReady::new);
- deserializerMap.put(ServerMessageType.SERVER_HANDSHAKE, ServerHandshake::new);
- deserializerMap.put(ServerMessageType.SERVER_KEY_DATA, ServerKeyData::new);
- deserializerMap.put(ServerMessageType.STATE_DATA_DESCRIPTION, StateDataDescription::new);
- }
-
public static & BinaryEnum, U extends Number> void registerBinaryEnum(Class cls, T @NotNull [] values) {
binaryEnumMap.put(cls, Arrays.stream(values).collect(Collectors.toMap(BinaryEnum::getValue, v -> v)));
}
@@ -64,7 +44,7 @@ public static & BinaryEnum, U extends Number> T getEnumVal
return (T)binaryEnumMap.get(enumCls).get(raw);
}
- public static @NotNull MessageToMessageDecoder createDecoder() {
+ public static @NotNull MessageToMessageDecoder createDecoder(EdgeDBBinaryClient client) {
return new MessageToMessageDecoder<>() {
private final Map contracts = new HashMap<>();
@@ -96,7 +76,7 @@ protected void decode(@NotNull ChannelHandlerContext ctx, @NotNull ByteBuf msg,
// can we read this packet?
if (msg.readableBytes() >= length) {
- var packet = PacketSerializer.deserialize(type, length, msg.readSlice((int) length));
+ var packet = PacketSerializer.deserialize(client, type, length, msg.readSlice((int) length));
if(packet == null) {
logger.error("Got null result for packet type {}", type);
@@ -175,7 +155,7 @@ public boolean tryComplete(@NotNull ByteBuf other) {
if (data.readableBytes() >= length) {
// read
- packet = PacketSerializer.deserialize(messageType, length, data, false);
+ packet = PacketSerializer.deserialize(client, messageType, length, data, false);
return true;
}
@@ -218,42 +198,36 @@ protected void encode(@NotNull ChannelHandlerContext ctx, @NotNull Sendable msg,
}
public static @Nullable Receivable deserialize(
- ServerMessageType messageType, long length, @NotNull ByteBuf buffer
+ EdgeDBBinaryClient client, ServerMessageType messageType, long length, @NotNull ByteBuf buffer
) {
var reader = new PacketReader(buffer);
- return deserializeSingle(messageType, length, reader, true);
+ return deserializeSingle(client, messageType, length, reader, true);
}
public static @Nullable Receivable deserialize(
- ServerMessageType messageType, long length, @NotNull ByteBuf buffer, boolean verifyEmpty
+ EdgeDBBinaryClient client, ServerMessageType messageType, long length, @NotNull ByteBuf buffer, boolean verifyEmpty
) {
var reader = new PacketReader(buffer);
- return deserializeSingle(messageType, length, reader, verifyEmpty);
+ return deserializeSingle(client, messageType, length, reader, verifyEmpty);
}
- public static @Nullable Receivable deserializeSingle(PacketReader reader) {
+ public static @Nullable Receivable deserializeSingle(EdgeDBBinaryClient client, PacketReader reader) {
var messageType = reader.readEnum(ServerMessageType.class, Byte.TYPE);
var length = reader.readUInt32().longValue();
- return deserializeSingle(messageType, length, reader, false);
+ return deserializeSingle(client, messageType, length, reader, false);
}
public static @Nullable Receivable deserializeSingle(
- ServerMessageType type, long length, @NotNull PacketReader reader,
+ EdgeDBBinaryClient client, ServerMessageType type, long length, @NotNull PacketReader reader,
boolean verifyEmpty
) {
- if(!deserializerMap.containsKey(type)) {
- logger.error("Unknown packet type {}", type);
- reader.skip(length);
- return null;
- }
-
try {
- return deserializerMap.get(type).apply(reader);
+ return client.getProtocolProvider().readPacket(type, (int)length, reader);
}
catch (Exception x) {
logger.error("Failed to deserialize packet", x);
- throw x;
+ return null;
}
finally {
// ensure we read the entire packet
@@ -263,9 +237,15 @@ protected void encode(@NotNull ChannelHandlerContext ctx, @NotNull Sendable msg,
}
}
- public static HttpResponse.BodyHandler> PACKET_BODY_HANDLER = new PacketBodyHandler();
-
+ public static HttpResponse.BodyHandler> createHandler(EdgeDBBinaryClient client) {
+ return new PacketBodyHandler(client);
+ }
private static class PacketBodyHandler implements HttpResponse.BodyHandler> {
+ private final EdgeDBBinaryClient client;
+ public PacketBodyHandler(EdgeDBBinaryClient client) {
+ this.client = client;
+ }
+
@Override
public HttpResponse.BodySubscriber> apply(HttpResponse.ResponseInfo responseInfo) {
// ensure success
@@ -276,7 +256,7 @@ public HttpResponse.BodySubscriber> apply(HttpResponse.Response
: new PacketBodySubscriber(responseInfo.statusCode());
}
- private static class PacketBodySubscriber implements HttpResponse.BodySubscriber> {
+ private class PacketBodySubscriber implements HttpResponse.BodySubscriber> {
private final @Nullable List<@NotNull ByteBuf> buffers;
private final CompletableFuture> promise;
@@ -334,7 +314,7 @@ public void onComplete() {
var data = new ArrayList();
while(completeBuffer.readableBytes() > 0) {
- var packet = deserializeSingle(reader);
+ var packet = deserializeSingle(client, reader);
if(packet == null && completeBuffer.readableBytes() > 0) {
promise.completeExceptionally(
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/builders/CodecBuilder.java b/src/driver/src/main/java/com/edgedb/driver/binary/builders/CodecBuilder.java
index 2269c2f5..2150b6d4 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/builders/CodecBuilder.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/builders/CodecBuilder.java
@@ -1,18 +1,20 @@
package com.edgedb.driver.binary.builders;
import com.edgedb.driver.binary.PacketReader;
-import com.edgedb.driver.binary.codecs.*;
+import com.edgedb.driver.binary.codecs.Codec;
+import com.edgedb.driver.binary.codecs.NullCodec;
import com.edgedb.driver.binary.codecs.scalars.*;
-import com.edgedb.driver.binary.codecs.scalars.complex.BytesCodec;
import com.edgedb.driver.binary.codecs.scalars.complex.DateTimeCodec;
import com.edgedb.driver.binary.codecs.scalars.complex.RelativeDurationCodec;
-import com.edgedb.driver.binary.descriptors.*;
-import com.edgedb.driver.binary.packets.shared.Cardinality;
-import com.edgedb.driver.binary.packets.shared.IOFormat;
-import com.edgedb.driver.datatypes.Range;
+import com.edgedb.driver.binary.protocol.ProtocolProvider;
+import com.edgedb.driver.binary.protocol.ProtocolVersion;
+import com.edgedb.driver.binary.protocol.TypeDescriptorInfo;
+import com.edgedb.driver.binary.protocol.common.Cardinality;
+import com.edgedb.driver.binary.protocol.common.IOFormat;
+import com.edgedb.driver.binary.protocol.common.descriptors.CodecMetadata;
+import com.edgedb.driver.clients.EdgeDBBinaryClient;
import com.edgedb.driver.exceptions.EdgeDBException;
import com.edgedb.driver.exceptions.MissingCodecException;
-import com.edgedb.driver.util.CollectionUtils;
import io.netty.buffer.ByteBuf;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -20,174 +22,145 @@
import org.slf4j.LoggerFactory;
import javax.naming.OperationNotSupportedException;
-import java.lang.reflect.Array;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.function.BiFunction;
+import java.util.function.Function;
import java.util.function.Supplier;
public final class CodecBuilder {
+ private static final class CodecCache {
+ public final ConcurrentMap> cache;
+ public final ConcurrentMap> codecPartsInstanceCache;
+ public final ConcurrentMap queryCodecsCache;
+
+ public final ProtocolVersion version;
+
+
+ private CodecCache(ProtocolVersion version) {
+ this.version = version;
+ this.cache = new ConcurrentHashMap<>(16);
+ this.codecPartsInstanceCache = new ConcurrentHashMap<>(16);
+ this.queryCodecsCache = new ConcurrentHashMap<>(8);
+ }
+ }
+
private static final Logger logger = LoggerFactory.getLogger(CodecBuilder.class);
public static final UUID NULL_CODEC_ID = new UUID(0L, 0L);
public static final UUID INVALID_CODEC_ID = new UUID(Long.MAX_VALUE, Long.MAX_VALUE);
- private static final @NotNull ConcurrentMap> codecPartsInstanceCache;
- private static final @NotNull ConcurrentMap> codecCache;
- private static final @NotNull ConcurrentMap queryCodecCache;
+
+ private static final ConcurrentMap codecCaches;
static {
- codecPartsInstanceCache = new ConcurrentHashMap<>(16);
- codecCache = new ConcurrentHashMap<>(32);
- queryCodecCache = new ConcurrentHashMap<>(16);
+ codecCaches = new ConcurrentHashMap<>(2);
}
@SuppressWarnings("unchecked")
- public static @Nullable Codec getCodec(UUID id, Class ignoredCls) {
- return (Codec) getCodec(id);
+ public static @Nullable Codec getCodec(ProtocolProvider provider, UUID id, Class ignoredCls) {
+ return (Codec) getCodec(provider, id);
}
- public static @Nullable Codec> getCodec(UUID id) {
- var codec = codecCache.get(id);
- return codec != null ? codec : getScalarCodec(id);
+ public static @Nullable Codec> getCodec(ProtocolProvider provider, UUID id) {
+ var codec = codecCaches.computeIfAbsent(provider.getVersion(), CodecCache::new).cache.get(id);
+ return codec != null ? codec : getCachedOrScalarCodec(provider, id);
}
- public static @NotNull Codec> buildCodec(@NotNull UUID id, @Nullable ByteBuf buffer) throws EdgeDBException {
+ public static @NotNull Codec> buildCodec(EdgeDBBinaryClient client, @NotNull UUID id, @Nullable ByteBuf buffer) throws EdgeDBException {
if(id.equals(NULL_CODEC_ID) || buffer == null) {
- return getOrCreateCodec(NULL_CODEC_ID, NullCodec::new);
+ return getOrCreateCodec(client.getProtocolProvider(), NULL_CODEC_ID, NullCodec::new);
}
var reader = new PacketReader(buffer);
- return buildCodec(id, reader);
+ return buildCodec(client, id, reader);
}
@SuppressWarnings("unchecked")
- public static @NotNull Codec buildCodec(@NotNull UUID id, @Nullable ByteBuf buffer, Class cls) throws EdgeDBException, OperationNotSupportedException {
+ public static @NotNull Codec buildCodec(EdgeDBBinaryClient client, @NotNull UUID id, @Nullable ByteBuf buffer, Class cls) throws EdgeDBException, OperationNotSupportedException {
if(id.equals(NULL_CODEC_ID) || buffer == null) {
- return (Codec)getOrCreateCodec(NULL_CODEC_ID, NullCodec::new);
+ return (Codec)getOrCreateCodec(client.getProtocolProvider(), NULL_CODEC_ID, NullCodec::new);
}
var reader = new PacketReader(buffer);
- return buildCodec(id, reader, cls);
+ return buildCodec(client, id, reader, cls);
}
@SuppressWarnings("unchecked")
- public static @NotNull Codec buildCodec(@NotNull UUID id, @NotNull PacketReader reader, Class ignoredCodecResult) throws EdgeDBException {
- return (Codec) buildCodec(id, reader);
+ public static @NotNull Codec buildCodec(EdgeDBBinaryClient client, @NotNull UUID id, @NotNull PacketReader reader, Class ignoredCodecResult) throws EdgeDBException {
+ return (Codec) buildCodec(client, id, reader);
}
- @SuppressWarnings("unchecked")
- public static @NotNull Codec> buildCodec(@NotNull UUID id, @NotNull PacketReader reader) throws EdgeDBException {
+
+ public static @NotNull Codec> buildCodec(EdgeDBBinaryClient client, @NotNull UUID id, @NotNull PacketReader reader) throws EdgeDBException {
try {
if(id.equals(NULL_CODEC_ID)) {
- return getOrCreateCodec(id, NullCodec::new);
+ logger.debug("Returning null codec");
+ return getOrCreateCodec(client.getProtocolProvider(), id, NullCodec::new);
}
- List> codecs = new ArrayList<>();
+ var providerCache = codecCaches.computeIfAbsent(client.getProtocolProvider().getVersion(), CodecCache::new);
+
+ var descriptors = new ArrayList>>();
while(!reader.isEmpty()) {
var start = reader.position();
- var descriptor = TypeDescriptorBuilder.getDescriptor(reader);
+ var descriptor = client.getProtocolProvider().readDescriptor(reader);
var end = reader.position();
logger.trace("{}/{}: read {}, size {}", end, reader.size(), descriptor.type, end-start);
- Codec> codec;
+ descriptors.add(descriptor);
+ }
- if(codecCache.containsKey(descriptor.getId())) {
- codec = codecCache.get(descriptor.getId());
- } else {
- codec = getScalarCodec(descriptor.getId());
- }
+ logger.debug("Read {} descriptors, totaling {} bytes", descriptors.size(), reader.position());
+
+ var codecs = new ArrayList>();
+
+ for(var i = 0; i != descriptors.size(); i++) {
+ var descriptor = descriptors.get(i);
+
+ Codec> codec = providerCache.cache.get(descriptor.getId());;
if(codec != null) {
- codecs.add(codec);
+ logger.debug("Using cached codec {} from ID: {}", codec, descriptor.getId());
+ codecs.add(i, codec);
continue;
}
- switch (descriptor.type) {
- case ARRAY_TYPE_DESCRIPTOR:
- var arrayType = descriptor.as(ArrayTypeDescriptor.class);
-
- codec = getOrCreateCodec(descriptor.getId(), () ->
- new CompilableCodec(
- codecs.get(arrayType.typePosition.intValue()),
- ArrayCodec::new,
- t -> Array.newInstance(t,0).getClass()
- )
- );
- break;
- case SCALAR_TYPE_DESCRIPTOR:
- case BASE_SCALAR_TYPE_DESCRIPTOR:
- // should be resolved by the above case, getting here is an error
- throw new MissingCodecException(String.format("Could not find the scalar type %s", descriptor.getId().toString()));
- case ENUMERATION_TYPE_DESCRIPTOR:
- codec = getOrCreateCodec(descriptor.getId(), TextCodec::new);
- break;
- case INPUT_SHAPE_DESCRIPTOR:
- var inputShape = descriptor.as(InputShapeDescriptor.class);
- var inputShapeCodecs = new Codec[inputShape.shapes.length];
- var inputShapeNames = new String[inputShape.shapes.length];
-
- for (int i = 0; i != inputShape.shapes.length; i++) {
- inputShapeCodecs[i] = codecs.get(inputShape.shapes[i].typePosition.intValue());
- inputShapeNames[i] = inputShape.shapes[i].name;
- }
-
- codec = getOrCreateCodec(descriptor.getId(), () -> new SparseObjectCodec(inputShapeCodecs, inputShapeNames));
- break;
-
-
- case TUPLE_TYPE_DESCRIPTOR:
- var tupleType = descriptor.as(TupleTypeDescriptor.class);
- var innerCodecs = new Codec>[tupleType.elementTypeDescriptorPositions.length];
-
- for(int i = 0; i != innerCodecs.length; i++) {
- innerCodecs[i] = codecs.get(tupleType.elementTypeDescriptorPositions[i].intValue());
- }
-
- codec = getOrCreateCodec(descriptor.getId(), () -> new TupleCodec(innerCodecs));
- break;
- case NAMED_TUPLE_DESCRIPTOR:
- var tupleShape = descriptor.as(NamedTupleTypeDescriptor.class);
- codec = getOrCreateCodec(descriptor.getId(), () -> ObjectCodec.create(codecs::get, tupleShape.elements));
- break;
- case OBJECT_SHAPE_DESCRIPTOR:
- var objectShape = descriptor.as(ObjectShapeDescriptor.class);
- codec = getOrCreateCodec(descriptor.getId(), () -> ObjectCodec.create(codecs::get, objectShape.shapes));
- break;
- case RANGE_TYPE_DESCRIPTOR:
- var rangeType = descriptor.as(RangeTypeDescriptor.class);
-
- codec = getOrCreateCodec(descriptor.getId(), () ->
- new CompilableCodec(
- codecs.get(rangeType.typePosition.intValue()),
- RangeCodec::new,
- t -> Range.empty(t).getClass()
- )
- );
- break;
- case SCALAR_TYPE_NAME_ANNOTATION:
- // ignored
- break;
- case SET_DESCRIPTOR:
- var setTypes = descriptor.as(SetTypeDescriptor.class);
-
- codec = getOrCreateCodec(descriptor.getId(), () ->
- new CompilableCodec(
- codecs.get(setTypes.typePosition.intValue()),
- SetCodec::new,
- t -> Array.newInstance(t, 0).getClass()
- )
- );
- break;
- default:
- throw new MissingCodecException(String.format("Could not find a type descriptor with the type %s", descriptor.getId().toString()));
+ codec = getCachedOrScalarCodec(client.getProtocolProvider(), descriptor.getId());
+
+ if(codec != null) {
+ logger.debug("Using cached codec {} from ID: {}", codec, descriptor.getId());
+ codecs.add(i, codec);
+ continue;
}
- codecs.add(codec);
+ logger.debug("Calling protocol provider for codec construction, descriptor type: {}", descriptor.type);
+
+ codec = client.getProtocolProvider().buildCodec(
+ descriptor,
+ codecs::get,
+ descriptors::get
+ );
+
+ logger.debug("Protocol provider returned {}", codec == null ? "null" : codec);
+
+ codecs.add(i, codec);
+
+ logger.debug("Codec {} added: {}, ID: {}", i, codec, descriptor.getId());
}
- var finalCodec = CollectionUtils.last(codecs);
+ Codec> finalCodec = null;
- codecCache.putIfAbsent(id, finalCodec);
+ for(var i = 1; i != codecs.size() + 1 && finalCodec == null; i++) {
+ finalCodec = codecs.get(codecs.size() - i);
+ }
+
+ if(finalCodec == null) {
+ throw new MissingCodecException("Failed to find end tail of codec tree");
+ }
return finalCodec;
}
@@ -195,33 +168,35 @@ public final class CodecBuilder {
logger.error("Failed to build codec", x);
throw x;
}
-
}
public static @NotNull Long getCacheKey(@NotNull String query, @NotNull Cardinality cardinality, @NotNull IOFormat format) {
return calculateKnuthHash(query) + cardinality.getValue() + format.getValue();
}
- public static @Nullable QueryCodecs getCachedCodecs(long cacheKey) {
- var ids = queryCodecCache.get(cacheKey);
+ public static @Nullable QueryCodecs getCachedCodecs(ProtocolProvider provider, long cacheKey) {
+ var providerCache = codecCaches.computeIfAbsent(provider.getVersion(), CodecCache::new);
+
+ var ids = providerCache.queryCodecsCache.get(cacheKey);
if(ids == null) {
return null;
}
- var inCodec = codecCache.get(ids.inputCodecId);
- var outCodec = codecCache.get(ids.outputCodecId);
+ var inCodec = providerCache.cache.get(ids.inputCodecId);
+ var outCodec = providerCache.cache.get(ids.outputCodecId);
if(inCodec == null || outCodec == null) {
- queryCodecCache.remove(cacheKey);
+ providerCache.queryCodecsCache.remove(cacheKey);
return null;
}
return new QueryCodecs(ids.inputCodecId, inCodec, ids.outputCodecId, outCodec);
}
- public static void updateCachedCodecs(long cacheKey, UUID inCodecId, UUID outCodecId) {
- queryCodecCache.computeIfAbsent(cacheKey, (c) -> new QueryCodecCacheEntry(inCodecId, outCodecId));
+ public static void updateCachedCodecs(ProtocolProvider provider, long cacheKey, UUID inCodecId, UUID outCodecId) {
+ codecCaches.computeIfAbsent(provider.getVersion(), CodecCache::new)
+ .queryCodecsCache.computeIfAbsent(cacheKey, (c) -> new QueryCodecCacheEntry(inCodecId, outCodecId));
}
private static @NotNull Long calculateKnuthHash(@NotNull String str) {
@@ -236,20 +211,62 @@ public static void updateCachedCodecs(long cacheKey, UUID inCodecId, UUID outCod
}
@SuppressWarnings("unchecked")
- private static Codec getOrCreateCodec(UUID id, @NotNull Supplier> constructor) {
- return (Codec) codecPartsInstanceCache.computeIfAbsent(id, v -> constructor.get());
+ public static Codec getOrCreateCodec(
+ ProtocolProvider provider,
+ UUID id,
+ @NotNull Supplier> constructor
+ ) {
+ return (Codec) codecCaches.computeIfAbsent(provider.getVersion(), CodecCache::new)
+ .codecPartsInstanceCache.computeIfAbsent(id, ignored -> constructor.get());
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Codec getOrCreateCodec(
+ ProtocolProvider provider,
+ UUID id,
+ @Nullable CodecMetadata metadata,
+ @NotNull Function<@Nullable CodecMetadata, Codec> constructor
+ ) {
+ return (Codec) codecCaches.computeIfAbsent(provider.getVersion(), CodecCache::new)
+ .codecPartsInstanceCache.computeIfAbsent(id, ignored -> constructor.apply(metadata));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Codec getOrCreateCodec(
+ ProtocolProvider provider,
+ UUID id,
+ @Nullable CodecMetadata metadata,
+ @NotNull BiFunction> constructor
+ ) {
+ if(logger.isDebugEnabled()) {
+ logger.debug(
+ "cache requested id: {}. exists?: {}, metadata: {}",
+ id,
+ codecCaches
+ .computeIfAbsent(provider.getVersion(), CodecCache::new)
+ .codecPartsInstanceCache.containsKey(id),
+ metadata == null
+ ? "none"
+ : metadata.toString()
+ );
+ }
+
+ return (Codec) codecCaches.computeIfAbsent(provider.getVersion(), CodecCache::new)
+ .codecPartsInstanceCache.computeIfAbsent(id, i -> constructor.apply(i, metadata));
}
@SuppressWarnings("unchecked")
- private static @Nullable Codec getScalarCodec(UUID id) {
- return (Codec) codecPartsInstanceCache.computeIfAbsent(id,
- (v) -> scalarCodecFactories.containsKey(v)
- ? scalarCodecFactories.get(v).get()
- : null
+ private static @Nullable Codec getCachedOrScalarCodec(ProtocolProvider provider, UUID id) {
+ return (Codec) codecCaches.computeIfAbsent(provider.getVersion(), CodecCache::new)
+ .codecPartsInstanceCache.computeIfAbsent(
+ id,
+ (v) -> scalarCodecFactories.containsKey(v)
+ ? scalarCodecFactories.get(v).apply(null)
+ : null
);
}
- private static final Map>> scalarCodecFactories = new HashMap<>() {
+ private static final Map>> scalarCodecFactories = new HashMap<>() {
{
put(UUID.fromString("00000000-0000-0000-0000-000000000100"), UUIDCodec::new);
put(UUID.fromString("00000000-0000-0000-0000-000000000101"), TextCodec::new);
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/builders/ObjectBuilder.java b/src/driver/src/main/java/com/edgedb/driver/binary/builders/ObjectBuilder.java
index d7f6623a..61640dd5 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/builders/ObjectBuilder.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/builders/ObjectBuilder.java
@@ -4,11 +4,11 @@
import com.edgedb.driver.binary.codecs.Codec;
import com.edgedb.driver.binary.codecs.ObjectCodec;
import com.edgedb.driver.binary.codecs.visitors.TypeVisitor;
-import com.edgedb.driver.binary.packets.receivable.Data;
import com.edgedb.driver.clients.EdgeDBBinaryClient;
import com.edgedb.driver.exceptions.EdgeDBException;
import com.edgedb.driver.exceptions.NoTypeConverterException;
import com.edgedb.driver.util.TypeUtils;
+import io.netty.buffer.ByteBuf;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,7 +31,7 @@ public interface CollectionConverter> {
}};
}
- public static @Nullable T buildResult(@NotNull EdgeDBBinaryClient client, Codec> codec, @NotNull Data data, @NotNull Class cls) throws EdgeDBException, OperationNotSupportedException {
+ public static @Nullable T buildResult(@NotNull EdgeDBBinaryClient client, Codec> codec, @NotNull ByteBuf data, @NotNull Class cls) throws EdgeDBException, OperationNotSupportedException {
var visitor = new TypeVisitor(client);
visitor.setTargetType(cls);
codec = visitor.visit(codec);
@@ -40,7 +40,7 @@ public interface CollectionConverter> {
return TypeBuilder.buildObject(client, cls, (ObjectCodec)codec, data);
}
- var value = Codec.deserializeFromBuffer(codec, Objects.requireNonNull(data.payloadBuffer), client.getCodecContext());
+ var value = Codec.deserializeFromBuffer(codec, Objects.requireNonNull(data), client.getCodecContext());
return convertTo(cls, value);
}
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/builders/TypeDescriptorBuilder.java b/src/driver/src/main/java/com/edgedb/driver/binary/builders/TypeDescriptorBuilder.java
deleted file mode 100644
index 65c2f723..00000000
--- a/src/driver/src/main/java/com/edgedb/driver/binary/builders/TypeDescriptorBuilder.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.edgedb.driver.binary.builders;
-
-import com.edgedb.driver.binary.PacketReader;
-import com.edgedb.driver.binary.descriptors.*;
-import com.edgedb.driver.exceptions.EdgeDBException;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-import java.util.function.BiFunction;
-
-import static org.joou.Unsigned.ubyte;
-
-public final class TypeDescriptorBuilder {
- @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
- private static final @NotNull Map> typeDescriptorFactories;
-
- static {
- typeDescriptorFactories = new HashMap<>() {
- {
- put(DescriptorType.ARRAY_TYPE_DESCRIPTOR, ArrayTypeDescriptor::new);
- put(DescriptorType.BASE_SCALAR_TYPE_DESCRIPTOR, BaseScalarTypeDescriptor::new);
- put(DescriptorType.ENUMERATION_TYPE_DESCRIPTOR, EnumerationTypeDescriptor::new);
- put(DescriptorType.NAMED_TUPLE_DESCRIPTOR, NamedTupleTypeDescriptor::new);
- put(DescriptorType.OBJECT_SHAPE_DESCRIPTOR, ObjectShapeDescriptor::new);
- put(DescriptorType.SCALAR_TYPE_DESCRIPTOR, ScalarTypeDescriptor::new);
- put(DescriptorType.SCALAR_TYPE_NAME_ANNOTATION, ScalarTypeNameAnnotation::new);
- put(DescriptorType.SET_DESCRIPTOR, SetTypeDescriptor::new);
- put(DescriptorType.TUPLE_TYPE_DESCRIPTOR, TupleTypeDescriptor::new);
- put(DescriptorType.INPUT_SHAPE_DESCRIPTOR, InputShapeDescriptor::new);
- put(DescriptorType.RANGE_TYPE_DESCRIPTOR, RangeTypeDescriptor::new);
- }
- };
- }
-
- public static @NotNull TypeDescriptorResult getDescriptor(final @NotNull PacketReader reader) throws EdgeDBException {
- var type = reader.readEnum(DescriptorType.class, Byte.TYPE);
- var id = reader.readUUID();
-
- var factory = typeDescriptorFactories.get(type);
-
- if(factory != null) {
- return new TypeDescriptorResult(type, factory.apply(id, reader));
- }
-
- var v = ubyte(type.getValue());
-
- if (v.compareTo(ubyte(0x80)) >= 0 && v.compareTo(ubyte(0xfe)) <= 0) {
- return new TypeDescriptorResult(type, new TypeAnnotationDescriptor(type, id, reader));
- }
-
- throw new EdgeDBException(String.format("No descriptor found for type %X", v.byteValue()));
- }
-
- public static class TypeDescriptorResult {
- public final DescriptorType type;
- private final TypeDescriptor descriptor;
-
- public TypeDescriptorResult(DescriptorType type, TypeDescriptor descriptor) {
- this.type = type;
- this.descriptor = descriptor;
- }
-
- public UUID getId() {
- return descriptor.getId();
- }
-
- @SuppressWarnings("unchecked")
- public T as(Class ignored) {
- return (T)descriptor;
- }
- }
-}
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/builders/internal/ObjectEnumeratorImpl.java b/src/driver/src/main/java/com/edgedb/driver/binary/builders/internal/ObjectEnumeratorImpl.java
index 47a7ab3c..d6485995 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/builders/internal/ObjectEnumeratorImpl.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/builders/internal/ObjectEnumeratorImpl.java
@@ -51,17 +51,17 @@ public boolean hasRemaining() {
var element = codec.elements[position];
- var data = reader.readByteArray();
+ try(var elementReader = reader.scopedSlice()) {
+ if(elementReader.isNoData) {
+ return new ObjectEnumerator.ObjectElement(element.name, null, element.codec.getConvertingClass());
+ }
- if(data == null || data.readableBytes() == 0) {
- return new ObjectEnumerator.ObjectElement(element.name, null, element.codec.getConvertingClass());
+ return new ObjectEnumerator.ObjectElement(
+ element.name,
+ element.codec.deserialize(elementReader, context),
+ element.codec.getConvertingClass()
+ );
}
-
- return new ObjectEnumerator.ObjectElement(
- element.name,
- element.codec.deserialize(new PacketReader(data), context),
- element.codec.getConvertingClass()
- );
}
finally {
position++;
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/builders/types/TypeBuilder.java b/src/driver/src/main/java/com/edgedb/driver/binary/builders/types/TypeBuilder.java
index f574fb72..3acaaa4a 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/builders/types/TypeBuilder.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/builders/types/TypeBuilder.java
@@ -3,17 +3,16 @@
import com.edgedb.driver.annotations.EdgeDBType;
import com.edgedb.driver.binary.codecs.Codec;
import com.edgedb.driver.binary.codecs.ObjectCodec;
-import com.edgedb.driver.binary.packets.receivable.Data;
import com.edgedb.driver.clients.EdgeDBBinaryClient;
import com.edgedb.driver.datatypes.Tuple;
import com.edgedb.driver.datatypes.internal.TupleImpl;
import com.edgedb.driver.exceptions.EdgeDBException;
+import io.netty.buffer.ByteBuf;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.naming.OperationNotSupportedException;
import java.util.Map;
-import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -30,7 +29,7 @@ public final class TypeBuilder {
}
@SuppressWarnings("unchecked")
- public static @Nullable T buildObject(@NotNull EdgeDBBinaryClient client, @NotNull Class type, ObjectCodec codec, @NotNull Data data) throws OperationNotSupportedException, EdgeDBException {
+ public static @Nullable T buildObject(@NotNull EdgeDBBinaryClient client, @NotNull Class type, ObjectCodec codec, @NotNull ByteBuf data) throws OperationNotSupportedException, EdgeDBException {
var info = getDeserializerInfo(type);
if(info == null) {
@@ -42,7 +41,7 @@ public final class TypeBuilder {
}
- return (T) Codec.deserializeFromBuffer(codec, Objects.requireNonNull(data.payloadBuffer), client.getCodecContext());
+ return (T) Codec.deserializeFromBuffer(codec, data, client.getCodecContext());
}
@SuppressWarnings("unchecked")
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/builders/types/TypeDeserializerInfo.java b/src/driver/src/main/java/com/edgedb/driver/binary/builders/types/TypeDeserializerInfo.java
index 83815420..13d0eca6 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/builders/types/TypeDeserializerInfo.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/builders/types/TypeDeserializerInfo.java
@@ -1,11 +1,11 @@
package com.edgedb.driver.binary.builders.types;
+import com.edgedb.driver.ObjectEnumerator;
import com.edgedb.driver.annotations.*;
import com.edgedb.driver.binary.builders.ObjectBuilder;
-import com.edgedb.driver.ObjectEnumerator;
import com.edgedb.driver.binary.builders.TypeDeserializerFactory;
import com.edgedb.driver.binary.builders.internal.ObjectEnumeratorImpl;
-import com.edgedb.driver.binary.packets.shared.Cardinality;
+import com.edgedb.driver.binary.protocol.common.Cardinality;
import com.edgedb.driver.exceptions.EdgeDBException;
import com.edgedb.driver.exceptions.NoTypeConverterException;
import com.edgedb.driver.namingstrategies.NamingStrategy;
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/ArrayCodec.java b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/ArrayCodec.java
index f9a6e394..8294e378 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/ArrayCodec.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/ArrayCodec.java
@@ -2,6 +2,7 @@
import com.edgedb.driver.binary.PacketReader;
import com.edgedb.driver.binary.PacketWriter;
+import com.edgedb.driver.binary.protocol.common.descriptors.CodecMetadata;
import com.edgedb.driver.exceptions.EdgeDBException;
import com.edgedb.driver.util.BinaryProtocolUtils;
import org.jetbrains.annotations.NotNull;
@@ -9,6 +10,7 @@
import javax.naming.OperationNotSupportedException;
import java.lang.reflect.Array;
+import java.util.UUID;
public class ArrayCodec extends CodecBase {
private static final byte[] EMPTY_ARRAY = new byte[] {
@@ -22,8 +24,8 @@ public class ArrayCodec extends CodecBase {
private final Codec innerCodec;
@SuppressWarnings("unchecked")
- public ArrayCodec(Class> cls, Codec> codec) {
- super((Class) cls);
+ public ArrayCodec(UUID id, @Nullable CodecMetadata metadata, Class> cls, Codec> codec) {
+ super(id, metadata, (Class) cls);
this.innerCodec = (Codec) codec;
}
@@ -72,7 +74,9 @@ public void serialize(@NotNull PacketWriter writer, T @Nullable [] value, CodecC
var array = (T[])Array.newInstance(innerCodec.getConvertingClass(), numElements);
for(int i = 0; i != numElements; i++) {
- array[i] = reader.deserializeByteArray(innerCodec, context);
+ try(var elementReader = reader.scopedSlice()) {
+ array[i] = innerCodec.deserialize(elementReader, context);
+ }
}
return array;
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/Codec.java b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/Codec.java
index ce8b24af..f74b1e25 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/Codec.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/Codec.java
@@ -2,6 +2,7 @@
import com.edgedb.driver.binary.PacketWriter;
import com.edgedb.driver.binary.PacketReader;
+import com.edgedb.driver.binary.protocol.common.descriptors.CodecMetadata;
import com.edgedb.driver.exceptions.EdgeDBException;
import io.netty.buffer.ByteBuf;
import org.jetbrains.annotations.NotNull;
@@ -9,8 +10,13 @@
import javax.naming.OperationNotSupportedException;
import java.lang.reflect.Type;
+import java.util.UUID;
public interface Codec {
+ UUID getId();
+
+ @Nullable CodecMetadata getMetadata();
+
void serialize(final PacketWriter writer, final @Nullable T value, final CodecContext context) throws OperationNotSupportedException, EdgeDBException;
@Nullable T deserialize(final PacketReader reader, final CodecContext context) throws EdgeDBException, OperationNotSupportedException;
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CodecBase.java b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CodecBase.java
index b0f1f5d5..a60649e8 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CodecBase.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CodecBase.java
@@ -1,14 +1,31 @@
package com.edgedb.driver.binary.codecs;
+import com.edgedb.driver.binary.protocol.common.descriptors.CodecMetadata;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Type;
+import java.util.UUID;
public abstract class CodecBase implements Codec {
+ public final UUID id;
+ public final @Nullable CodecMetadata metadata;
private final Class cls;
- public CodecBase(Class cls) {
+ public CodecBase(UUID id, @Nullable CodecMetadata metadata, Class cls) {
this.cls = cls;
+ this.id = id;
+ this.metadata = metadata;
+ }
+
+ @Override
+ public UUID getId() {
+ return this.id;
+ }
+
+ @Override
+ public @Nullable CodecMetadata getMetadata() {
+ return this.metadata;
}
@Override
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CompilableCodec.java b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CompilableCodec.java
index a6c012ac..c6bf57f2 100644
--- a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CompilableCodec.java
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CompilableCodec.java
@@ -2,30 +2,42 @@
import com.edgedb.driver.binary.PacketReader;
import com.edgedb.driver.binary.PacketWriter;
+import com.edgedb.driver.binary.protocol.common.descriptors.CodecMetadata;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.naming.OperationNotSupportedException;
import java.lang.reflect.Type;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.function.BiFunction;
import java.util.function.Function;
@SuppressWarnings("rawtypes")
public final class CompilableCodec implements Codec {
+ @FunctionalInterface
+ public interface CompilableFactory {
+ Codec> compile(UUID id, @Nullable CodecMetadata metadata, Class> cls, Codec> innerCodec);
+ }
+
private final Codec> innerCodec;
- private final BiFunction, Codec>, Codec>> factory;
+ private final CompilableFactory factory;
private final @NotNull ConcurrentMap, Codec>> instanceCache;
private final Function, Class>> compilableTypeFactory;
+ private final UUID id;
+ private final @Nullable CodecMetadata metadata;
private @Nullable Class> compilableType;
public CompilableCodec(
+ UUID id,
+ @Nullable CodecMetadata metadata,
Codec> innerCodec,
- BiFunction, Codec>, Codec>> factory,
+ CompilableFactory factory,
Function, Class>> compilableTypeFactory
) {
+ this.id = id;
+ this.metadata = metadata;
this.factory = factory;
this.innerCodec = innerCodec;
this.instanceCache = new ConcurrentHashMap<>();
@@ -45,7 +57,7 @@ public Codec> getInnerCodec() {
}
public Codec> compile(Class> cls, Codec> innerCodec) {
- return instanceCache.computeIfAbsent(cls, (c) -> this.factory.apply(c, innerCodec));
+ return instanceCache.computeIfAbsent(cls, (c) -> this.factory.compile(this.id, this.metadata, c, innerCodec));
}
public Class> getInnerType() {
@@ -54,6 +66,16 @@ public Class> getInnerType() {
: this.innerCodec.getConvertingClass();
}
+ @Override
+ public UUID getId() {
+ return this.id;
+ }
+
+ @Override
+ public @Nullable CodecMetadata getMetadata() {
+ return this.metadata;
+ }
+
@Override
public void serialize(PacketWriter writer, @Nullable Object value, CodecContext context) throws OperationNotSupportedException {
throw new OperationNotSupportedException();
diff --git a/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CompoundCodec.java b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CompoundCodec.java
new file mode 100644
index 00000000..aa124946
--- /dev/null
+++ b/src/driver/src/main/java/com/edgedb/driver/binary/codecs/CompoundCodec.java
@@ -0,0 +1,94 @@
+package com.edgedb.driver.binary.codecs;
+
+import com.edgedb.driver.binary.PacketReader;
+import com.edgedb.driver.binary.PacketWriter;
+import com.edgedb.driver.binary.protocol.common.descriptors.CodecMetadata;
+import com.edgedb.driver.binary.protocol.common.descriptors.TypeOperation;
+import com.edgedb.driver.exceptions.EdgeDBException;
+import org.jetbrains.annotations.Nullable;
+
+import javax.naming.OperationNotSupportedException;
+import java.util.Collection;
+import java.util.UUID;
+
+import static com.edgedb.driver.util.BinaryProtocolUtils.INT_SIZE;
+
+public class CompoundCodec extends CodecBase