Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.datastax.oss.simulacron.common.codec.CqlMapper Maven / Gradle / Ivy
/*
* Copyright (C) 2017-2017 DataStax Inc.
*
* 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
*
* http://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 com.datastax.oss.simulacron.common.codec;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.ASCII;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.BIGINT;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.BLOB;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.BOOLEAN;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.COUNTER;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.DATE;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.DECIMAL;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.DOUBLE;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.FLOAT;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.INET;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.INT;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.SMALLINT;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.TIME;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.TIMESTAMP;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.TIMEUUID;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.TINYINT;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.UUID;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.VARCHAR;
import static com.datastax.oss.protocol.internal.ProtocolConstants.DataType.VARINT;
import static com.datastax.oss.simulacron.common.codec.CodecUtils.primitive;
import com.datastax.oss.protocol.internal.response.result.RawType;
import com.datastax.oss.protocol.internal.response.result.RawType.RawList;
import com.datastax.oss.protocol.internal.response.result.RawType.RawMap;
import com.datastax.oss.protocol.internal.response.result.RawType.RawSet;
import com.datastax.oss.protocol.internal.response.result.RawType.RawTuple;
import com.datastax.oss.protocol.internal.util.Bytes;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CqlMapper {
private static final Logger logger = LoggerFactory.getLogger(CqlMapper.class);
private final int protocolVersion;
private static final ByteBuffer TRUE = ByteBuffer.wrap(new byte[] {1});
private static final ByteBuffer FALSE = ByteBuffer.wrap(new byte[] {0});
// TODO: Perhaps make these sizebound.
// TODO: Follow optimizations that java driver makes in codec registry.
// For resolving by cqlType.
private final ConcurrentMap> cqlTypeCache = new ConcurrentHashMap<>();
private final TypeFactory typeFactory = TypeFactory.defaultInstance();
private static final ConcurrentMap mapperCache = new ConcurrentHashMap<>();
public static CqlMapper forVersion(int protocolVersion) {
return mapperCache.computeIfAbsent(protocolVersion, CqlMapper::new);
}
private CqlMapper(int protocolVersion) {
this.protocolVersion = protocolVersion;
}
private void register(Codec codec) {
cqlTypeCache.put(codec.getCqlType(), codec);
}
@SuppressWarnings("unchecked")
private Codec> loadCache(RawType key) {
if (key instanceof RawSet) {
RawSet s = (RawSet) key;
Codec> elementCodec = cqlTypeCache.computeIfAbsent(s.elementType, this::loadCache);
return new SetCodec(elementCodec);
} else if (key instanceof RawList) {
RawList l = (RawList) key;
Codec> elementCodec = cqlTypeCache.computeIfAbsent(l.elementType, this::loadCache);
return new ListCodec(elementCodec);
} else if (key instanceof RawMap) {
RawMap m = (RawMap) key;
Codec> keyCodec = cqlTypeCache.computeIfAbsent(m.keyType, this::loadCache);
Codec> valueCodec = cqlTypeCache.computeIfAbsent(m.valueType, this::loadCache);
return new MapCodec(keyCodec, valueCodec);
} else if (key instanceof RawTuple) {
RawTuple t = (RawTuple) key;
List> codecs =
t.fieldTypes
.stream()
.map(f -> (Codec) cqlTypeCache.computeIfAbsent(f, this::loadCache))
.collect(Collectors.toList());
return new TupleCodec(t, codecs);
} else {
// TODO Support UDT
logger.warn("Could not resolve codec for {}, returning null instead.", key);
return null;
}
}
@SuppressWarnings("unchecked")
public Codec codecFor(RawType cqlType) {
return (Codec) cqlTypeCache.computeIfAbsent(cqlType, this::loadCache);
}
abstract class AbstractCodec implements Codec {
private final JavaType javaType;
private final RawType cqlType;
private final int minProtocolVersion;
AbstractCodec(Type type, RawType cqlType) {
this(typeFactory.constructType(type), cqlType, 0);
}
AbstractCodec(Type type, RawType cqlType, int minProtocolVersion) {
this(typeFactory.constructType(type), cqlType, minProtocolVersion, true);
}
AbstractCodec(JavaType javaType, RawType cqlType, int minProtocolVersion, boolean register) {
this.javaType = javaType;
this.cqlType = cqlType;
this.minProtocolVersion = minProtocolVersion;
if (register) {
// only register if requested and meets protocol version requirement.
register(this);
}
}
private void checkProtocolVersion() {
if (minProtocolVersion > protocolVersion) {
throw new ProtocolVersionException(cqlType, protocolVersion, minProtocolVersion);
}
}
@Override
public ByteBuffer encode(T input) {
checkProtocolVersion();
if (input == null) {
return null;
}
return encodeInternal(input);
}
public T decode(ByteBuffer input) {
checkProtocolVersion();
return decodeInternal(input);
}
@SuppressWarnings("unchecked")
public T toNativeType(Object input) {
if (input == null) {
return null;
} else if (javaType.getRawClass().isAssignableFrom(input.getClass())) {
return (T) input;
} else {
return toNativeTypeInternal(input);
}
}
abstract ByteBuffer encodeInternal(T input);
abstract T decodeInternal(ByteBuffer input);
/**
* Specialized {@link #toNativeType} logic for the implementation type. By default doesn't
* handle anything.
*
* @param input Input to convert to native type.
* @return conversion of input into native type, or null if not applicable.
*/
T toNativeTypeInternal(Object input) {
return null;
}
@Override
public JavaType getJavaType() {
return javaType;
}
@Override
public RawType getCqlType() {
return cqlType;
}
}
class StringCodec extends AbstractCodec {
private final String charset;
StringCodec(String charset, RawType cqlType) {
super(String.class, cqlType);
this.charset = charset;
}
@Override
ByteBuffer encodeInternal(String input) {
try {
return ByteBuffer.wrap(input.getBytes(charset));
} catch (UnsupportedEncodingException uee) {
throw new InvalidTypeException("Invalid input for charset " + charset, uee);
}
}
@Override
public String decodeInternal(ByteBuffer input) {
if (input == null) {
return null;
} else if (input.remaining() == 0) {
return "";
} else {
try {
return new String(Bytes.getArray(input), charset);
} catch (UnsupportedEncodingException e) {
throw new InvalidTypeException("Invalid bytes for charset " + charset, e);
}
}
}
/** @return The result of {@link Object#toString()}. */
@Override
String toNativeTypeInternal(Object input) {
return input.toString();
}
}
abstract class CollectionCodec> extends AbstractCodec {
private final Codec elementCodec;
CollectionCodec(JavaType javaType, RawType cqlType, Codec elementCodec) {
super(javaType, cqlType, 1, false);
this.elementCodec = elementCodec;
}
@Override
ByteBuffer encodeInternal(C input) {
ByteBuffer[] bbs = new ByteBuffer[input.size()];
int i = 0;
for (E elt : input) {
if (elt == null) {
throw new NullPointerException("Collection elements cannot be null");
}
ByteBuffer bb;
try {
bb = elementCodec.encode(elt);
} catch (ClassCastException e) {
throw new InvalidTypeException("Invalid type for element", e);
}
bbs[i++] = bb;
}
return pack(bbs, input.size());
}
@Override
C decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return newInstance(0);
try {
ByteBuffer i = input.duplicate();
int size = readSize(i);
C coll = newInstance(size);
for (int pos = 0; pos < size; pos++) {
ByteBuffer databb = readValue(i);
coll.add(elementCodec.decode(databb));
}
return coll;
} catch (BufferUnderflowException e) {
throw new InvalidTypeException("Not enough bytes to deserialize collection", e);
}
}
@Override
@SuppressWarnings("unchecked")
public C toNativeType(Object input) {
if (input instanceof Collection) {
Collection in = (Collection) input;
C transformed = newInstance(in.size());
for (Object o : in) {
E e = elementCodec.toNativeType(o);
transformed.add(e);
}
return transformed;
}
return null;
}
protected abstract C newInstance(int size);
}
class SetCodec extends CollectionCodec> {
SetCodec(Codec elementCodec) {
super(
typeFactory.constructCollectionLikeType(Set.class, elementCodec.getJavaType()),
new RawSet(elementCodec.getCqlType()),
elementCodec);
}
@Override
protected Set newInstance(int size) {
return new LinkedHashSet<>(size);
}
}
class ListCodec extends CollectionCodec> {
ListCodec(Codec elementCodec) {
super(
typeFactory.constructCollectionLikeType(List.class, elementCodec.getJavaType()),
new RawList(elementCodec.getCqlType()),
elementCodec);
}
@Override
protected List newInstance(int size) {
return new ArrayList<>(size);
}
}
class TupleCodec extends AbstractCodec {
private final List> elementCodecs;
private final RawTuple tupleType;
TupleCodec(RawTuple tuple, List> elementCodecs) {
super(Tuple.class, tuple);
this.tupleType = tuple;
this.elementCodecs = elementCodecs;
}
@Override
ByteBuffer encodeInternal(Tuple input) {
int size = 0;
int length = input.getTupleType().fieldTypes.size();
ByteBuffer[] elements = new ByteBuffer[length];
for (int i = 0; i < length; i++) {
elements[i] = elementCodecs.get(i).encode(input.getValues().get(i));
size += 4 + (elements[i] == null ? 0 : elements[i].remaining());
}
ByteBuffer result = ByteBuffer.allocate(size);
for (ByteBuffer bb : elements) {
if (bb == null) {
result.putInt(-1);
} else {
result.putInt(bb.remaining());
result.put(bb.duplicate());
}
}
return (ByteBuffer) result.flip();
}
@Override
Tuple decodeInternal(ByteBuffer input) {
if (input == null) {
return null;
}
ByteBuffer bytes = input.duplicate();
int numberOfValues = tupleType.fieldTypes.size();
List values = new ArrayList<>(tupleType.fieldTypes.size());
try {
for (int i = 0; bytes.hasRemaining() && i < numberOfValues; i++) {
int n = bytes.getInt();
ByteBuffer element = n < 0 ? null : readBytes(bytes, n);
values.add(elementCodecs.get(i).decode(element));
}
} catch (BufferUnderflowException | IllegalArgumentException e) {
throw new InvalidTypeException("Not enough bytes top deserialize a tuple", e);
}
return new Tuple(tupleType, values);
}
@Override
@SuppressWarnings("unchecked")
public Tuple toNativeType(Object input) {
Collection toTransform = null;
if (input instanceof Tuple) {
Tuple inTuple = ((Tuple) input);
if (inTuple.getTupleType().equals(tupleType)) {
toTransform = ((Tuple) input).getValues();
} else {
logger.warn("Attempted to encode mismatching Tuple {} to {}.", inTuple, tupleType);
return null;
}
} else if (input instanceof Collection) {
toTransform = (Collection) input;
if (toTransform.size() != tupleType.fieldTypes.size()) {
logger.warn(
"Attempted to encode mismatching number of elements from {} to {}",
toTransform,
tupleType);
return null;
}
}
if (toTransform != null) {
List values = new ArrayList<>(tupleType.fieldTypes.size());
Iterator inputIt = toTransform.iterator();
for (int i = 0; i < tupleType.fieldTypes.size(); i++) {
Object next = inputIt.next();
values.add(elementCodecs.get(i).toNativeType(next));
}
return new Tuple(tupleType, values);
}
return null;
}
}
class MapCodec extends AbstractCodec> {
private final Codec keyCodec;
private final Codec valueCodec;
MapCodec(Codec keyCodec, Codec valueCodec) {
super(
typeFactory.constructMapLikeType(
Map.class, keyCodec.getJavaType(), valueCodec.getJavaType()),
new RawMap(keyCodec.getCqlType(), valueCodec.getCqlType()));
this.keyCodec = keyCodec;
this.valueCodec = valueCodec;
}
@Override
ByteBuffer encodeInternal(Map input) {
int i = 0;
ByteBuffer[] bbs = new ByteBuffer[2 * input.size()];
for (Map.Entry entry : input.entrySet()) {
ByteBuffer bbk;
K key = entry.getKey();
try {
bbk = keyCodec.encode(key);
} catch (ClassCastException e) {
throw new InvalidTypeException(
String.format(
"Invalid type for map key, expecting %s but got %s",
keyCodec.getJavaType(), key.getClass()),
e);
}
ByteBuffer bbv;
V value = entry.getValue();
if (value == null) {
throw new NullPointerException("Map values cannot be null");
}
try {
bbv = valueCodec.encode(value);
} catch (ClassCastException e) {
throw new InvalidTypeException(
String.format(
"Invalid type for map value, expecting %s but got %s",
valueCodec.getJavaType(), value.getClass()),
e);
}
bbs[i++] = bbk;
bbs[i++] = bbv;
}
return pack(bbs, input.size());
}
@Override
Map decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) {
return new LinkedHashMap<>();
}
try {
ByteBuffer bytes = input.duplicate();
int n = readSize(bytes);
Map m = new LinkedHashMap<>(n);
for (int i = 0; i < n; i++) {
ByteBuffer kbb = readValue(bytes);
ByteBuffer vbb = readValue(bytes);
m.put(keyCodec.decode(kbb), valueCodec.decode(vbb));
}
return m;
} catch (BufferUnderflowException | IllegalArgumentException e) {
throw new InvalidTypeException("Not enough bytes to deserialize a map", e);
}
}
@Override
@SuppressWarnings("unchecked")
public Map toNativeType(Object input) {
if (input instanceof Map) {
Map map = new HashMap<>();
Map inMap = (Map) input;
for (Map.Entry entry : (Set) inMap.entrySet()) {
K k = keyCodec.toNativeType(entry.getKey());
V v = valueCodec.toNativeType(entry.getValue());
map.put(k, v);
}
return map;
}
return null;
}
}
class LongCodec extends AbstractCodec {
LongCodec(RawType cqlType) {
this(cqlType, 0);
}
LongCodec(RawType cqlType, int minProtocolVersion) {
super(Long.class, cqlType, minProtocolVersion);
}
@Override
ByteBuffer encodeInternal(Long input) {
ByteBuffer bb = ByteBuffer.allocate(8);
bb.putLong(0, input);
return bb;
}
@Override
Long decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) {
return 0L;
} else if (input.remaining() != 8) {
throw new InvalidTypeException(
"Invalid 64-bits long value, expecting 8 bytes but got " + input.remaining());
} else {
return input.getLong(input.position());
}
}
@Override
Long toNativeTypeInternal(Object input) {
if (input instanceof String) {
return Long.parseLong(((String) input));
} else if (input instanceof Number) {
return ((Number) input).longValue();
}
return null;
}
}
class UUIDCodec extends AbstractCodec {
UUIDCodec(RawType cqlType) {
super(UUID.class, cqlType);
}
@Override
ByteBuffer encodeInternal(UUID input) {
ByteBuffer buf = ByteBuffer.allocate(16);
buf.putLong(0, input.getMostSignificantBits());
buf.putLong(8, input.getLeastSignificantBits());
return buf;
}
@Override
UUID decodeInternal(ByteBuffer input) {
return input == null || input.remaining() == 0
? null
: new UUID(input.getLong(input.position()), input.getLong(input.position() + 8));
}
@Override
UUID toNativeTypeInternal(Object input) {
if (input instanceof String) {
return java.util.UUID.fromString((String) input);
}
return null;
}
}
public final Codec ascii = new StringCodec("US-ASCII", primitive(ASCII));
public final Codec bigint = new LongCodec(primitive(BIGINT));
public final Codec blob =
new AbstractCodec(ByteBuffer.class, primitive(BLOB)) {
@Override
ByteBuffer toNativeTypeInternal(Object input) {
if (input instanceof String) {
return Bytes.fromHexString((String) input);
}
return null;
}
@Override
ByteBuffer encodeInternal(ByteBuffer input) {
return input.duplicate();
}
@Override
ByteBuffer decodeInternal(ByteBuffer input) {
return encode(input);
}
};
public final Codec bool =
new AbstractCodec(Boolean.class, primitive(BOOLEAN)) {
@Override
Boolean toNativeTypeInternal(Object input) {
if (input instanceof String) {
return Boolean.parseBoolean((String) input);
} else if (input instanceof Number) {
return ((Number) input).intValue() != 0;
}
return null;
}
@Override
ByteBuffer encodeInternal(Boolean input) {
return input ? TRUE.duplicate() : FALSE.duplicate();
}
@Override
Boolean decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) {
return false;
} else if (input.remaining() != 1) {
throw new InvalidTypeException(
"Invalid boolean value, expecting 1 byte but got " + input.remaining());
} else {
return input.get(input.position()) != 0;
}
}
};
public final Codec counter = new LongCodec(primitive(COUNTER));
public final Codec decimal =
new AbstractCodec(BigDecimal.class, primitive(DECIMAL)) {
@Override
BigDecimal toNativeTypeInternal(Object input) {
if (input instanceof String) {
return new BigDecimal((String) input);
} else if (input instanceof Number) {
return new BigDecimal(((Number) input).doubleValue());
}
return null;
}
@Override
ByteBuffer encodeInternal(BigDecimal input) {
BigInteger bi = input.unscaledValue();
int scale = input.scale();
byte[] bibytes = bi.toByteArray();
ByteBuffer bytes = ByteBuffer.allocate(4 + bibytes.length);
bytes.putInt(scale);
bytes.put(bibytes);
bytes.rewind();
return bytes;
}
@Override
BigDecimal decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return null;
if (input.remaining() < 4)
throw new InvalidTypeException(
"Invalid decimal value, expecting at least 4 bytes but got " + input.remaining());
input = input.duplicate();
int scale = input.getInt();
byte[] bibytes = new byte[input.remaining()];
input.get(bibytes);
BigInteger bi = new BigInteger(bibytes);
return new BigDecimal(bi, scale);
}
};
public final Codec cdouble =
new AbstractCodec(Double.class, primitive(DOUBLE)) {
@Override
Double toNativeTypeInternal(Object input) {
if (input instanceof String) {
return Double.parseDouble((String) input);
} else if (input instanceof Number) {
return ((Number) input).doubleValue();
}
return null;
}
@Override
ByteBuffer encodeInternal(Double input) {
ByteBuffer bb = ByteBuffer.allocate(8);
bb.putDouble(0, input);
return bb;
}
@Override
Double decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return 0.0;
if (input.remaining() != 8)
throw new InvalidTypeException(
"Invalid 64-bits double value, expecting 8 bytes but got " + input.remaining());
return input.getDouble(input.position());
}
};
public final Codec cfloat =
new AbstractCodec(Float.class, primitive(FLOAT)) {
@Override
Float toNativeTypeInternal(Object input) {
if (input instanceof String) {
return Float.parseFloat((String) input);
} else if (input instanceof Number) {
return ((Number) input).floatValue();
}
return null;
}
@Override
ByteBuffer encodeInternal(Float input) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putFloat(0, input);
return bb;
}
@Override
Float decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return 0.0f;
if (input.remaining() != 4)
throw new InvalidTypeException(
"Invalid 32-bits float value, expecting 4 bytes but got " + input.remaining());
return input.getFloat(input.position());
}
};
public final Codec cint =
new AbstractCodec(Integer.class, primitive(INT)) {
@Override
Integer toNativeTypeInternal(Object input) {
if (input instanceof String) {
return Integer.parseInt((String) input);
} else if (input instanceof Number) {
return ((Number) input).intValue();
}
return null;
}
@Override
ByteBuffer encodeInternal(Integer input) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putInt(0, input);
return bb;
}
@Override
Integer decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return 0;
if (input.remaining() != 4)
throw new InvalidTypeException(
"Invalid 32-bits integer value, expecting 4 bytes but got " + input.remaining());
return input.getInt(input.position());
}
};
public final Codec timestamp =
new AbstractCodec(Date.class, primitive(TIMESTAMP)) {
@Override
Date toNativeTypeInternal(Object input) {
if (input instanceof String) {
return new Date(Long.parseLong((String) input));
} else if (input instanceof Number) {
return new Date(((Number) input).longValue());
}
return null;
}
@Override
ByteBuffer encodeInternal(Date input) {
return input == null ? null : bigint.encode(input.getTime());
}
@Override
Date decodeInternal(ByteBuffer input) {
return input == null || input.remaining() == 0 ? null : new Date(bigint.decode(input));
}
};
public final Codec uuid = new UUIDCodec(primitive(UUID));
public final Codec varchar = new StringCodec("UTF-8", primitive(VARCHAR));
public final Codec varint =
new AbstractCodec(BigInteger.class, primitive(VARINT)) {
@Override
BigInteger toNativeTypeInternal(Object input) {
if (input instanceof String) {
return new BigInteger((String) input);
} else if (input instanceof Number) {
return BigInteger.valueOf(((Number) input).longValue());
}
return null;
}
@Override
ByteBuffer encodeInternal(BigInteger input) {
return ByteBuffer.wrap(input.toByteArray());
}
@Override
BigInteger decodeInternal(ByteBuffer input) {
return input == null || input.remaining() == 0
? null
: new BigInteger(Bytes.getArray(input));
}
};
public final Codec timeuuid = new UUIDCodec(primitive(TIMEUUID));
public final Codec inet =
new AbstractCodec(InetAddress.class, primitive(INET)) {
@Override
InetAddress toNativeTypeInternal(Object input) {
if (input instanceof String) {
try {
// TODO switch to implementation which doesn't use resolv
return InetAddress.getByName((String) input);
} catch (UnknownHostException e) {
logger.error("Failure resolving host: ", input, e);
}
}
return null;
}
@Override
ByteBuffer encodeInternal(InetAddress input) {
return ByteBuffer.wrap(input.getAddress());
}
@Override
InetAddress decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return null;
try {
return InetAddress.getByAddress(Bytes.getArray(input));
} catch (UnknownHostException e) {
throw new InvalidTypeException(
"Invalid bytes for inet value, got " + input.remaining() + " bytes", e);
}
}
};
public final Codec date =
new AbstractCodec(LocalDate.class, primitive(DATE), 4) {
@Override
LocalDate toNativeTypeInternal(Object input) {
if (input instanceof String) {
return LocalDate.parse((String) input);
} else if (input instanceof Number) {
return LocalDate.ofEpochDay(((Number) input).longValue());
}
return null;
}
@Override
ByteBuffer encodeInternal(LocalDate input) {
int unsigned = (int) input.toEpochDay() - Integer.MIN_VALUE;
return cint.encode(unsigned);
}
@Override
LocalDate decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return null;
int unsigned = cint.decode(input);
int signed = unsigned + Integer.MIN_VALUE;
return LocalDate.ofEpochDay(signed);
}
};
public final Codec time = new LongCodec(primitive(TIME), 4);
public final Codec smallint =
new AbstractCodec(Short.class, primitive(SMALLINT), 4) {
@Override
Short toNativeTypeInternal(Object input) {
if (input instanceof String) {
return Short.parseShort((String) input);
} else if (input instanceof Number) {
return ((Number) input).shortValue();
}
return null;
}
@Override
ByteBuffer encodeInternal(Short input) {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.putShort(0, input);
return bb;
}
@Override
Short decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return 0;
if (input.remaining() != 2)
throw new InvalidTypeException(
"Invalid 16-bits integer value, expecting 2 bytes but got " + input.remaining());
return input.getShort(input.position());
}
};
public final Codec tinyint =
new AbstractCodec(Byte.class, primitive(TINYINT), 4) {
@Override
Byte toNativeTypeInternal(Object input) {
if (input instanceof String) {
return Byte.parseByte((String) input);
} else if (input instanceof Number) {
return ((Number) input).byteValue();
}
return null;
}
@Override
ByteBuffer encodeInternal(Byte input) {
ByteBuffer bb = ByteBuffer.allocate(1);
bb.put(0, input);
return bb;
}
@Override
Byte decodeInternal(ByteBuffer input) {
if (input == null || input.remaining() == 0) return 0;
if (input.remaining() != 1)
throw new InvalidTypeException(
"Invalid 8-bits integer value, expecting 1 byte but got " + input.remaining());
return input.get(input.position());
}
};
private ByteBuffer pack(ByteBuffer[] buffers, int elements) {
int size = 0;
for (ByteBuffer bb : buffers) {
int elemSize = sizeOfValue(bb);
size += elemSize;
}
ByteBuffer result = ByteBuffer.allocate(sizeOfCollectionSize() + size);
writeSize(result, elements);
for (ByteBuffer bb : buffers) {
writeValue(result, bb);
}
return (ByteBuffer) result.flip();
}
private int sizeOfValue(ByteBuffer value) {
return value == null ? 4 : 4 + value.remaining();
}
private int sizeOfCollectionSize() {
return 4;
}
private int readSize(ByteBuffer input) {
return input.getInt();
}
private void writeSize(ByteBuffer output, int size) {
output.putInt(size);
}
private ByteBuffer readValue(ByteBuffer input) {
int size = readSize(input);
return size < 0 ? null : readBytes(input, size);
}
private ByteBuffer readBytes(ByteBuffer bb, int length) {
ByteBuffer copy = bb.duplicate();
copy.limit(copy.position() + length);
bb.position(bb.position() + length);
return copy;
}
private void writeValue(ByteBuffer output, ByteBuffer value) {
output.putInt(value.remaining());
output.put(value.duplicate());
}
}