From 2371476218e672d409bc58d66793b114dd1e05e3 Mon Sep 17 00:00:00 2001 From: Mirro Mutth Date: Fri, 15 Mar 2024 20:22:19 +0900 Subject: [PATCH 1/3] Add fast path for determining `Codec` - Add `Codec.getMainClass()` for fast path, it allows users to extend `Codec` to change fast path - Add fast path to default codecs - Merge primitive type fast path into new fast path --- .../mysql/codec/AbstractClassedCodec.java | 5 + .../mysql/codec/AbstractPrimitiveCodec.java | 9 +- .../asyncer/r2dbc/mysql/codec/BlobCodec.java | 5 + .../r2dbc/mysql/codec/BooleanCodec.java | 2 +- .../asyncer/r2dbc/mysql/codec/ByteCodec.java | 2 +- .../asyncer/r2dbc/mysql/codec/ClobCodec.java | 5 + .../io/asyncer/r2dbc/mysql/codec/Codec.java | 15 +- .../r2dbc/mysql/codec/DefaultCodecs.java | 191 +++++++++++------- .../r2dbc/mysql/codec/DoubleCodec.java | 2 +- .../asyncer/r2dbc/mysql/codec/EnumCodec.java | 8 +- .../asyncer/r2dbc/mysql/codec/FloatCodec.java | 2 +- .../r2dbc/mysql/codec/InstantCodec.java | 5 + .../r2dbc/mysql/codec/IntegerCodec.java | 2 +- .../r2dbc/mysql/codec/LocalDateTimeCodec.java | 5 + .../asyncer/r2dbc/mysql/codec/LongCodec.java | 2 +- .../mysql/codec/OffsetDateTimeCodec.java | 5 + .../r2dbc/mysql/codec/PrimitiveCodec.java | 25 --- .../asyncer/r2dbc/mysql/codec/SetCodec.java | 5 + .../asyncer/r2dbc/mysql/codec/ShortCodec.java | 2 +- .../r2dbc/mysql/codec/ZonedDateTimeCodec.java | 5 + .../r2dbc/mysql/codec/EnumCodecTest.java | 3 +- 21 files changed, 196 insertions(+), 109 deletions(-) diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractClassedCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractClassedCodec.java index 2462c46dc..2c22d1105 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractClassedCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractClassedCodec.java @@ -36,5 +36,10 @@ public final boolean canDecode(MySqlReadableMetadata metadata, Class target) return target.isAssignableFrom(this.type) && doCanDecode(metadata); } + @Override + public final Class getMainClass() { + return this.type; + } + protected abstract boolean doCanDecode(MySqlReadableMetadata metadata); } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractPrimitiveCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractPrimitiveCodec.java index 295b0d18d..499a5fc98 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractPrimitiveCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractPrimitiveCodec.java @@ -41,11 +41,18 @@ abstract class AbstractPrimitiveCodec implements PrimitiveCodec { @Override public final boolean canDecode(MySqlReadableMetadata metadata, Class target) { - return target.isAssignableFrom(boxedClass) && canPrimitiveDecode(metadata); + return (target.isAssignableFrom(boxedClass) || target.equals(primitiveClass)) && doCanDecode(metadata); } @Override public final Class getPrimitiveClass() { return primitiveClass; } + + @Override + public final Class getMainClass() { + return boxedClass; + } + + protected abstract boolean doCanDecode(MySqlReadableMetadata metadata); } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BlobCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BlobCodec.java index dc3a75b5e..29ec42b14 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BlobCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BlobCodec.java @@ -49,6 +49,11 @@ final class BlobCodec implements MassiveCodec { private BlobCodec() { } + @Override + public Class getMainClass() { + return Blob.class; + } + @Override public Blob decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context) { diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java index 3b8035c5f..f546ba751 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/BooleanCodec.java @@ -52,7 +52,7 @@ public MySqlParameter encode(Object value, CodecContext context) { } @Override - public boolean canPrimitiveDecode(MySqlReadableMetadata metadata) { + public boolean doCanDecode(MySqlReadableMetadata metadata) { MySqlType type = metadata.getType(); return (type == MySqlType.BIT || type == MySqlType.TINYINT) && Integer.valueOf(1).equals(metadata.getPrecision()); diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ByteCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ByteCodec.java index 649a6266d..e21c029c8 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ByteCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ByteCodec.java @@ -52,7 +52,7 @@ public MySqlParameter encode(Object value, CodecContext context) { } @Override - public boolean canPrimitiveDecode(MySqlReadableMetadata metadata) { + public boolean doCanDecode(MySqlReadableMetadata metadata) { return metadata.getType().isNumeric(); } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ClobCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ClobCodec.java index b3bae3689..84d990a39 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ClobCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ClobCodec.java @@ -52,6 +52,11 @@ final class ClobCodec implements MassiveCodec { private ClobCodec() { } + @Override + public Class getMainClass() { + return Clob.class; + } + @Override public Clob decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context) { diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/Codec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/Codec.java index 9bd116198..744652c74 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/Codec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/Codec.java @@ -41,8 +41,7 @@ public interface Codec { * @return the decoded result. */ @Nullable - T decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, - CodecContext context); + T decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context); /** * Checks if the field value can be decoded as specified {@link Class}. @@ -69,4 +68,16 @@ T decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean * @return encoded {@link MySqlParameter}. */ MySqlParameter encode(Object value, CodecContext context); + + /** + * Gets the main {@link Class} that is handled by this codec. It is used to fast path the codec lookup if it is not + * {@code null}. If same main {@link Class} is handled by multiple codecs, the codec with the highest priority will + * be used. The priority of the fast path is determined by its order in {@link Codecs}. + * + * @return the main {@link Class}, or {@code null} if it is not in fast path. + */ + @Nullable + default Class getMainClass() { + return null; + } } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java index f609af74f..793253858 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java @@ -22,13 +22,15 @@ import io.asyncer.r2dbc.mysql.message.FieldValue; import io.asyncer.r2dbc.mysql.message.LargeFieldValue; import io.asyncer.r2dbc.mysql.message.NormalFieldValue; +import io.r2dbc.spi.Blob; +import io.r2dbc.spi.Clob; import io.r2dbc.spi.Parameter; import org.jetbrains.annotations.Nullable; import javax.annotation.concurrent.GuardedBy; import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -42,7 +44,46 @@ */ final class DefaultCodecs implements Codecs { - private final Codec[] codecs; + private static final List> DEFAULT_CODECS = InternalArrays.asImmutableList( + ByteCodec.INSTANCE, + ShortCodec.INSTANCE, + IntegerCodec.INSTANCE, + LongCodec.INSTANCE, + BigIntegerCodec.INSTANCE, + + BigDecimalCodec.INSTANCE, // Only all decimals + FloatCodec.INSTANCE, // Decimal (precision < 7) or float + DoubleCodec.INSTANCE, // Decimal (precision < 16) or double or float + + BooleanCodec.INSTANCE, + BitSetCodec.INSTANCE, + + ZonedDateTimeCodec.INSTANCE, + LocalDateTimeCodec.INSTANCE, + InstantCodec.INSTANCE, + OffsetDateTimeCodec.INSTANCE, + + LocalDateCodec.INSTANCE, + + LocalTimeCodec.INSTANCE, + DurationCodec.INSTANCE, + OffsetTimeCodec.INSTANCE, + + YearCodec.INSTANCE, + + StringCodec.INSTANCE, + + EnumCodec.INSTANCE, + SetCodec.INSTANCE, + + ClobCodec.INSTANCE, + BlobCodec.INSTANCE, + + ByteBufferCodec.INSTANCE, + ByteArrayCodec.INSTANCE + ); + + private final List> codecs; private final ParameterizedCodec[] parameterizedCodecs; @@ -50,21 +91,28 @@ final class DefaultCodecs implements Codecs { private final MassiveParameterizedCodec[] massiveParameterizedCodecs; - private final Map> primitiveCodecs; + private final Map, Codec> fastPath; - private DefaultCodecs(Codec[] codecs) { - this.codecs = requireNonNull(codecs, "codecs must not be null"); + private DefaultCodecs(List> codecs) { + requireNonNull(codecs, "codecs must not be null"); - Map> primitiveCodecs = new HashMap<>(); + Map, Codec> fastPath = new HashMap<>(); List> parameterizedCodecs = new ArrayList<>(); List> massiveCodecs = new ArrayList<>(); List> massiveParamCodecs = new ArrayList<>(); for (Codec codec : codecs) { + Class mainClass = codec.getMainClass(); + + if (mainClass != null) { + fastPath.putIfAbsent(mainClass, codec); + } + if (codec instanceof PrimitiveCodec) { // Primitive codec must be class-based codec, cannot support ParameterizedType. PrimitiveCodec c = (PrimitiveCodec) codec; - primitiveCodecs.put(c.getPrimitiveClass(), c); + + fastPath.putIfAbsent(c.getPrimitiveClass(), c); } else if (codec instanceof ParameterizedCodec) { parameterizedCodecs.add((ParameterizedCodec) codec); } @@ -78,15 +126,16 @@ private DefaultCodecs(Codec[] codecs) { } } - this.primitiveCodecs = primitiveCodecs; + this.fastPath = fastPath; + this.codecs = codecs; this.massiveCodecs = massiveCodecs.toArray(new MassiveCodec[0]); this.massiveParameterizedCodecs = massiveParamCodecs.toArray(new MassiveParameterizedCodec[0]); this.parameterizedCodecs = parameterizedCodecs.toArray(new ParameterizedCodec[0]); } /** - * Note: this method should NEVER release {@code buf} because of it come from {@code MySqlRow} which will - * release this buffer. + * Note: this method should NEVER release {@code buf} because of it come from {@code MySqlRow} which will release + * this buffer. */ @Override public T decode(FieldValue value, MySqlReadableMetadata metadata, Class type, boolean binary, @@ -104,10 +153,7 @@ public T decode(FieldValue value, MySqlReadableMetadata metadata, Class t Class target = chooseClass(metadata, type); - // Fast map for primitive classes. - if (target.isPrimitive()) { - return decodePrimitive(value, metadata, target, binary, context); - } else if (value instanceof NormalFieldValue) { + if (value instanceof NormalFieldValue) { return decodeNormal((NormalFieldValue) value, metadata, target, binary, context); } else if (value instanceof LargeFieldValue) { return decodeMassive((LargeFieldValue) value, metadata, target, binary, context); @@ -171,11 +217,18 @@ public MySqlParameter encode(Object value, CodecContext context) { requireNonNull(value, "value must not be null"); requireNonNull(context, "context must not be null"); - final Object valueToEncode = getValueToEncode(value); + Object valueToEncode = getValueToEncode(value); + if (null == valueToEncode) { return encodeNull(); } + Codec fast = encodeFast(valueToEncode); + + if (fast != null && fast.canEncode(valueToEncode)) { + return fast.encode(valueToEncode, context); + } + for (Codec codec : codecs) { if (codec.canEncode(valueToEncode)) { return codec.encode(valueToEncode, context); @@ -199,23 +252,46 @@ public MySqlParameter encodeNull() { } @Nullable - private T decodePrimitive(FieldValue value, MySqlReadableMetadata metadata, Class type, - boolean binary, CodecContext context) { - @SuppressWarnings("unchecked") - PrimitiveCodec codec = (PrimitiveCodec) this.primitiveCodecs.get(type); + @SuppressWarnings("unchecked") + private Codec decodeFast(Class type) { + Codec codec = (Codec) fastPath.get(type); + + if (codec == null && type.isEnum()) { + return (Codec) fastPath.get(Enum.class); + } + + return codec; + } - if (codec != null && value instanceof NormalFieldValue && codec.canPrimitiveDecode(metadata)) { - return codec.decode(((NormalFieldValue) value).getBufferSlice(), metadata, type, binary, context); + @Nullable + @SuppressWarnings("unchecked") + private Codec encodeFast(Object value) { + Codec codec = (Codec) fastPath.get(value.getClass()); + + if (codec == null) { + if (value instanceof ByteBuffer) { + return (Codec) fastPath.get(ByteBuffer.class); + } else if (value instanceof Blob) { + return (Codec) fastPath.get(Blob.class); + } else if (value instanceof Clob) { + return (Codec) fastPath.get(Clob.class); + } else if (value instanceof Enum) { + return (Codec) fastPath.get(Enum.class); + } } - // Mismatch, no one else can support this primitive class. - throw new IllegalArgumentException("Cannot decode " + value.getClass().getSimpleName() + " of " + - type + " for " + metadata.getType()); + return codec; } @Nullable private T decodeNormal(NormalFieldValue value, MySqlReadableMetadata metadata, Class type, boolean binary, CodecContext context) { + Codec fast = decodeFast(type); + + if (fast != null && fast.canDecode(metadata, type)) { + return fast.decode(value.getBufferSlice(), metadata, type, binary, context); + } + for (Codec codec : codecs) { if (codec.canDecode(metadata, type)) { @SuppressWarnings("unchecked") @@ -244,6 +320,12 @@ private T decodeNormal(NormalFieldValue value, MySqlReadableMetadata metadat @Nullable private T decodeMassive(LargeFieldValue value, MySqlReadableMetadata metadata, Class type, boolean binary, CodecContext context) { + Codec fast = decodeFast(type); + + if (fast instanceof MassiveCodec && fast.canDecode(metadata, type)) { + return ((MassiveCodec) fast).decodeMassive(value.getBufferSlices(), metadata, type, binary, context); + } + for (MassiveCodec codec : massiveCodecs) { if (codec.canDecode(metadata, type)) { @SuppressWarnings("unchecked") @@ -269,52 +351,19 @@ private T decodeMassive(LargeFieldValue value, MySqlReadableMetadata metadat throw new IllegalArgumentException("Cannot decode massive " + type + " for " + metadata.getType()); } + /** + * Chooses the {@link Class} to use for decoding. It helps to find {@link Codec} on the fast path. e.g. + * {@link Object} -> {@link String} for {@code TEXT}, {@link Number} -> {@link Integer} for {@code INT}, etc. + * + * @param metadata the metadata of the column or the {@code OUT} parameter. + * @param type the {@link Class} specified by the user. + * @return the {@link Class} to use for decoding. + */ private static Class chooseClass(MySqlReadableMetadata metadata, Class type) { Class javaType = metadata.getType().getJavaType(); return type.isAssignableFrom(javaType) ? javaType : type; } - private static Codec[] defaultCodecs() { - return new Codec[] { - ByteCodec.INSTANCE, - ShortCodec.INSTANCE, - IntegerCodec.INSTANCE, - LongCodec.INSTANCE, - BigIntegerCodec.INSTANCE, - - BigDecimalCodec.INSTANCE, // Only all decimals - FloatCodec.INSTANCE, // Decimal (precision < 7) or float - DoubleCodec.INSTANCE, // Decimal (precision < 16) or double or float - - BooleanCodec.INSTANCE, - BitSetCodec.INSTANCE, - - ZonedDateTimeCodec.INSTANCE, - LocalDateTimeCodec.INSTANCE, - InstantCodec.INSTANCE, - OffsetDateTimeCodec.INSTANCE, - - LocalDateCodec.INSTANCE, - - LocalTimeCodec.INSTANCE, - DurationCodec.INSTANCE, - OffsetTimeCodec.INSTANCE, - - YearCodec.INSTANCE, - - StringCodec.INSTANCE, - - EnumCodec.INSTANCE, - SetCodec.INSTANCE, - - ClobCodec.INSTANCE, - BlobCodec.INSTANCE, - - ByteBufferCodec.INSTANCE, - ByteArrayCodec.INSTANCE - }; - } - static final class Builder implements CodecsBuilder { @GuardedBy("lock") @@ -327,12 +376,10 @@ public CodecsBuilder addFirst(Codec codec) { lock.lock(); try { if (codecs.isEmpty()) { - Codec[] defaultCodecs = defaultCodecs(); - - codecs.ensureCapacity(defaultCodecs.length + 1); + codecs.ensureCapacity(DEFAULT_CODECS.size() + 1); // Add first. codecs.add(codec); - codecs.addAll(InternalArrays.asImmutableList(defaultCodecs)); + codecs.addAll(DEFAULT_CODECS); } else { codecs.add(0, codec); } @@ -347,7 +394,7 @@ public CodecsBuilder addLast(Codec codec) { lock.lock(); try { if (codecs.isEmpty()) { - codecs.addAll(InternalArrays.asImmutableList(defaultCodecs())); + codecs.addAll(DEFAULT_CODECS); } codecs.add(codec); } finally { @@ -362,9 +409,9 @@ public Codecs build() { try { try { if (codecs.isEmpty()) { - return new DefaultCodecs(defaultCodecs()); + return new DefaultCodecs(DEFAULT_CODECS); } - return new DefaultCodecs(codecs.toArray(new Codec[0])); + return new DefaultCodecs(InternalArrays.asImmutableList(codecs.toArray(new Codec[0]))); } finally { codecs.clear(); codecs.trimToSize(); diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DoubleCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DoubleCodec.java index fa544965b..986666ab8 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DoubleCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DoubleCodec.java @@ -68,7 +68,7 @@ public MySqlParameter encode(Object value, CodecContext context) { } @Override - public boolean canPrimitiveDecode(MySqlReadableMetadata metadata) { + public boolean doCanDecode(MySqlReadableMetadata metadata) { return metadata.getType().isNumeric(); } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/EnumCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/EnumCodec.java index a658b3598..b1ebf638e 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/EnumCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/EnumCodec.java @@ -29,13 +29,19 @@ /** * Codec for {@code enum class}. */ -final class EnumCodec implements Codec> { +@SuppressWarnings("rawtypes") +final class EnumCodec implements Codec { static final EnumCodec INSTANCE = new EnumCodec(); private EnumCodec() { } + @Override + public Class getMainClass() { + return Enum.class; + } + @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public Enum decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/FloatCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/FloatCodec.java index 91dd20b46..4e45c00e3 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/FloatCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/FloatCodec.java @@ -68,7 +68,7 @@ public MySqlParameter encode(Object value, CodecContext context) { } @Override - public boolean canPrimitiveDecode(MySqlReadableMetadata metadata) { + public boolean doCanDecode(MySqlReadableMetadata metadata) { return metadata.getType().isNumeric(); } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/InstantCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/InstantCodec.java index 0a14c6ee3..78894a24d 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/InstantCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/InstantCodec.java @@ -39,6 +39,11 @@ final class InstantCodec implements Codec { private InstantCodec() { } + @Override + public Class getMainClass() { + return Instant.class; + } + @Override public Instant decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context) { diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/IntegerCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/IntegerCodec.java index 73b9d702c..3ec04aa59 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/IntegerCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/IntegerCodec.java @@ -65,7 +65,7 @@ public MySqlParameter encode(Object value, CodecContext context) { } @Override - public boolean canPrimitiveDecode(MySqlReadableMetadata metadata) { + public boolean doCanDecode(MySqlReadableMetadata metadata) { return metadata.getType().isNumeric(); } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/LocalDateTimeCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/LocalDateTimeCodec.java index 424d969e5..a9567cae6 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/LocalDateTimeCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/LocalDateTimeCodec.java @@ -45,6 +45,11 @@ final class LocalDateTimeCodec implements ParameterizedCodec { private LocalDateTimeCodec() { } + @Override + public Class getMainClass() { + return LocalDateTime.class; + } + @Override public LocalDateTime decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context) { diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/LongCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/LongCodec.java index 30c495db6..d57c57c8f 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/LongCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/LongCodec.java @@ -73,7 +73,7 @@ public MySqlParameter encode(Object value, CodecContext context) { } @Override - public boolean canPrimitiveDecode(MySqlReadableMetadata metadata) { + public boolean doCanDecode(MySqlReadableMetadata metadata) { return metadata.getType().isNumeric(); } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/OffsetDateTimeCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/OffsetDateTimeCodec.java index b99714638..df1ffedf1 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/OffsetDateTimeCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/OffsetDateTimeCodec.java @@ -39,6 +39,11 @@ final class OffsetDateTimeCodec implements Codec { private OffsetDateTimeCodec() { } + @Override + public Class getMainClass() { + return OffsetDateTime.class; + } + @Override public OffsetDateTime decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context) { diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/PrimitiveCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/PrimitiveCodec.java index b8c420777..a880825a3 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/PrimitiveCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/PrimitiveCodec.java @@ -16,9 +16,6 @@ package io.asyncer.r2dbc.mysql.codec; -import io.asyncer.r2dbc.mysql.api.MySqlReadableMetadata; -import io.netty.buffer.ByteBuf; - /** * Base class considers primitive class for {@link Codec} implementations. It should be an internal * abstraction. @@ -29,28 +26,6 @@ */ interface PrimitiveCodec extends Codec { - /** - * Decodes a {@link ByteBuf} as specified {@link Class}. - * - * @param value the {@link ByteBuf}. - * @param metadata the metadata of the column or the {@code OUT} parameter. - * @param target the specified {@link Class}, which can be a primitive type. - * @param binary if the value should be decoded by binary protocol. - * @param context the codec context. - * @return the decoded data that is boxed. - */ - @Override - T decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, - CodecContext context); - - /** - * Checks if the field value can be decoded as a primitive data. - * - * @param metadata the metadata of the column or the {@code OUT} parameter. - * @return if it can decode. - */ - boolean canPrimitiveDecode(MySqlReadableMetadata metadata); - /** * Gets the primitive {@link Class}, such as {@link Integer#TYPE}, etc. * diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/SetCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/SetCodec.java index 82d4f8b2e..1b88e9269 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/SetCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/SetCodec.java @@ -48,6 +48,11 @@ final class SetCodec implements ParameterizedCodec { private SetCodec() { } + @Override + public Class getMainClass() { + return String[].class; + } + @Override public String[] decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context) { diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ShortCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ShortCodec.java index c3c42948c..34bcaf305 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ShortCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ShortCodec.java @@ -59,7 +59,7 @@ public MySqlParameter encode(Object value, CodecContext context) { } @Override - public boolean canPrimitiveDecode(MySqlReadableMetadata metadata) { + public boolean doCanDecode(MySqlReadableMetadata metadata) { return metadata.getType().isNumeric(); } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ZonedDateTimeCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ZonedDateTimeCodec.java index fedad10d8..f16fcc274 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ZonedDateTimeCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/ZonedDateTimeCodec.java @@ -44,6 +44,11 @@ final class ZonedDateTimeCodec implements ParameterizedCodec { private ZonedDateTimeCodec() { } + @Override + public Class getMainClass() { + return ZonedDateTime.class; + } + @Override public ZonedDateTime decode(ByteBuf value, MySqlReadableMetadata metadata, Class target, boolean binary, CodecContext context) { diff --git a/r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/codec/EnumCodecTest.java b/r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/codec/EnumCodecTest.java index d97feca07..87666ddac 100644 --- a/r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/codec/EnumCodecTest.java +++ b/r2dbc-mysql/src/test/java/io/asyncer/r2dbc/mysql/codec/EnumCodecTest.java @@ -27,7 +27,8 @@ /** * Unit tests for {@link EnumCodec}. */ -class EnumCodecTest implements CodecTestSupport> { +@SuppressWarnings("rawtypes") +class EnumCodecTest implements CodecTestSupport { private final Enum[] enums = { // Java has no way to create an element of enum with special character. From d533e1efbabe6235bdd3f0f8848070839691c9a7 Mon Sep 17 00:00:00 2001 From: Mirro Mutth Date: Mon, 18 Mar 2024 10:29:51 +0900 Subject: [PATCH 2/3] Remove PrimitiveCodec --- .../mysql/codec/AbstractPrimitiveCodec.java | 3 +- .../r2dbc/mysql/codec/DefaultCodecs.java | 11 +++--- .../r2dbc/mysql/codec/PrimitiveCodec.java | 35 ------------------- 3 files changed, 6 insertions(+), 43 deletions(-) delete mode 100644 r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/PrimitiveCodec.java diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractPrimitiveCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractPrimitiveCodec.java index 499a5fc98..a3844d28e 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractPrimitiveCodec.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/AbstractPrimitiveCodec.java @@ -25,7 +25,7 @@ * * @param the boxed type of handling primitive data. */ -abstract class AbstractPrimitiveCodec implements PrimitiveCodec { +abstract class AbstractPrimitiveCodec implements Codec { private final Class primitiveClass; @@ -44,7 +44,6 @@ public final boolean canDecode(MySqlReadableMetadata metadata, Class target) return (target.isAssignableFrom(boxedClass) || target.equals(primitiveClass)) && doCanDecode(metadata); } - @Override public final Class getPrimitiveClass() { return primitiveClass; } diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java index 793253858..d76b398e2 100644 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java +++ b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/DefaultCodecs.java @@ -108,9 +108,8 @@ private DefaultCodecs(List> codecs) { fastPath.putIfAbsent(mainClass, codec); } - if (codec instanceof PrimitiveCodec) { - // Primitive codec must be class-based codec, cannot support ParameterizedType. - PrimitiveCodec c = (PrimitiveCodec) codec; + if (codec instanceof AbstractPrimitiveCodec) { + AbstractPrimitiveCodec c = (AbstractPrimitiveCodec) codec; fastPath.putIfAbsent(c.getPrimitiveClass(), c); } else if (codec instanceof ParameterizedCodec) { @@ -230,7 +229,7 @@ public MySqlParameter encode(Object value, CodecContext context) { } for (Codec codec : codecs) { - if (codec.canEncode(valueToEncode)) { + if (codec != fast && codec.canEncode(valueToEncode)) { return codec.encode(valueToEncode, context); } } @@ -293,7 +292,7 @@ private T decodeNormal(NormalFieldValue value, MySqlReadableMetadata metadat } for (Codec codec : codecs) { - if (codec.canDecode(metadata, type)) { + if (codec != fast && codec.canDecode(metadata, type)) { @SuppressWarnings("unchecked") Codec c = (Codec) codec; return c.decode(value.getBufferSlice(), metadata, type, binary, context); @@ -327,7 +326,7 @@ private T decodeMassive(LargeFieldValue value, MySqlReadableMetadata metadat } for (MassiveCodec codec : massiveCodecs) { - if (codec.canDecode(metadata, type)) { + if (codec != fast && codec.canDecode(metadata, type)) { @SuppressWarnings("unchecked") MassiveCodec c = (MassiveCodec) codec; return c.decodeMassive(value.getBufferSlices(), metadata, type, binary, context); diff --git a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/PrimitiveCodec.java b/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/PrimitiveCodec.java deleted file mode 100644 index a880825a3..000000000 --- a/r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/codec/PrimitiveCodec.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2023 asyncer.io projects - * - * 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 - * - * https://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.asyncer.r2dbc.mysql.codec; - -/** - * Base class considers primitive class for {@link Codec} implementations. It should be an internal - * abstraction. - *

- * Primitive types should never return {@code null} when decoding. - * - * @param the boxed type that is handled by this codec. - */ -interface PrimitiveCodec extends Codec { - - /** - * Gets the primitive {@link Class}, such as {@link Integer#TYPE}, etc. - * - * @return the primitive {@link Class}. - */ - Class getPrimitiveClass(); -} From aa90297f3b0c3fd9bf8b6cd9d09e46713eeb5283 Mon Sep 17 00:00:00 2001 From: jchrys Date: Wed, 27 Mar 2024 20:19:23 +0900 Subject: [PATCH 3/3] remove invalid benchmark Motivation: `MySqlNames` is removed by #257 but related bench is not removed properly. Modifications: Removed MySqlNames bench Result: Clean up --- .../mysql/MySqlNamesCompareBenchmark.java | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 r2dbc-mysql/src/jmh/java/io/asyncer/r2dbc/mysql/MySqlNamesCompareBenchmark.java diff --git a/r2dbc-mysql/src/jmh/java/io/asyncer/r2dbc/mysql/MySqlNamesCompareBenchmark.java b/r2dbc-mysql/src/jmh/java/io/asyncer/r2dbc/mysql/MySqlNamesCompareBenchmark.java deleted file mode 100644 index 351e47951..000000000 --- a/r2dbc-mysql/src/jmh/java/io/asyncer/r2dbc/mysql/MySqlNamesCompareBenchmark.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2023 asyncer.io projects - * - * 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 - * - * https://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.asyncer.r2dbc.mysql; - -import org.junit.platform.commons.annotation.Testable; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Threads; -import org.openjdk.jmh.annotations.Timeout; - -/** - * Benchmark between {@link MySqlNames#compare} and {@link String#compareToIgnoreCase}. - */ -@State(Scope.Benchmark) -@Threads(1) -@Timeout(time = 1) -@Testable -public class MySqlNamesCompareBenchmark extends BenchmarkSupport { - - private static final String LEFT = "This is a test message for testing String compare " + - "with case insensitive or case sensitive"; - - private static final String RIGHT = LEFT.toUpperCase(); - - @Benchmark - @Testable - public int compareCi() { - return MySqlNames.compare(LEFT, RIGHT); - } - - @Benchmark - @Testable - public int nativeCompareCi() { - return String.CASE_INSENSITIVE_ORDER.compare(LEFT, RIGHT); - } - - @Benchmark - @Testable - public int compareCs() { - return MySqlNames.compare(LEFT, LEFT); - } - - @SuppressWarnings("EqualsWithItself") - @Benchmark - @Testable - public int nativeCompareCs() { - return String.CASE_INSENSITIVE_ORDER.compare(LEFT, LEFT); - } -}