Skip to content

Commit

Permalink
Merge pull request #21 from esaulpaugh/abi-refactor
Browse files Browse the repository at this point in the history
ABI refactor
  • Loading branch information
esaulpaugh authored Jan 20, 2021
2 parents 19e4d9a + 77bbd1f commit de05423
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 140 deletions.
39 changes: 0 additions & 39 deletions src/main/java/com/esaulpaugh/headlong/abi/ABIType.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
import com.esaulpaugh.headlong.util.Integers;
import com.esaulpaugh.headlong.util.Strings;

import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.function.IntFunction;

import static com.esaulpaugh.headlong.abi.UnitType.UNIT_LENGTH_BYTES;

Expand Down Expand Up @@ -139,43 +137,6 @@ public final J decode(ByteBuffer buffer) {
*/
abstract J decode(ByteBuffer buffer, byte[] unitBuffer);

private static int[] decodeHeads(int len, ByteBuffer bb, byte[] unitBuffer, Object[] elements, IntFunction<ABIType<?>> getType) {
final int[] offsets = new int[len];
for(int i = 0; i < len; i++) {
ABIType<?> t = getType.apply(i);
if(!t.dynamic) {
elements[i] = t.decode(bb, unitBuffer);
} else {
offsets[i] = Encoding.UINT31.decode(bb, unitBuffer);
}
}
return offsets;
}

static void decodeObjects(int len, ByteBuffer bb, byte[] unitBuffer, Object[] elements, IntFunction<ABIType<?>> getType) {
final int start = bb.position(); // save this value before offsets are decoded
final int[] offsets = decodeHeads(len, bb, unitBuffer, elements, getType);
for (int i = 0; i < len; i++) {
final int offset = offsets[i];
if(offset > 0) {
final int jump = start + offset;
final int pos = bb.position();
if(jump != pos) {
/* LENIENT MODE; see https://github.com/ethereum/solidity/commit/3d1ca07e9b4b42355aa9be5db5c00048607986d1 */
if(jump < pos) {
throw new IllegalArgumentException("illegal backwards jump: (" + start + "+" + offset + "=" + jump + ")<" + pos);
}
bb.position(jump); // leniently jump to specified offset
}
try {
elements[i] = getType.apply(i).decode(bb, unitBuffer);
} catch (BufferUnderflowException bue) {
throw new IllegalArgumentException(bue);
}
}
}
}

/**
* Parses and validates a string representation of J.
*
Expand Down
79 changes: 29 additions & 50 deletions src/main/java/com/esaulpaugh/headlong/abi/ArrayType.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,73 +236,52 @@ private int checkLength(final int valueLen, Object value) {

@Override
void encodeTail(Object value, ByteBuffer dest) {
encodeArrayTail(value, dest);
}

private void insert(int len, ByteBuffer dest, Runnable insert) {
if(length == DYNAMIC_LENGTH) {
Encoding.insertInt(len, dest);
}
insert.run();
}

private void encodeArrayTail(Object v, ByteBuffer dest) {
switch (elementType.typeCode()) {
case TYPE_CODE_BOOLEAN: encodeBooleans((boolean[]) v, dest); return;
case TYPE_CODE_BYTE: encodeBytes(decodeIfString(v), dest); return;
case TYPE_CODE_INT: encodeInts((int[]) v, dest); return;
case TYPE_CODE_LONG: encodeLongs((long[]) v, dest); return;
case TYPE_CODE_BOOLEAN: encodeBooleans((boolean[]) value, dest); return;
case TYPE_CODE_BYTE: encodeBytes(decodeIfString(value), dest); return;
case TYPE_CODE_INT: encodeInts((int[]) value, dest); return;
case TYPE_CODE_LONG: encodeLongs((long[]) value, dest); return;
case TYPE_CODE_BIG_INTEGER:
case TYPE_CODE_BIG_DECIMAL:
case TYPE_CODE_ARRAY:
case TYPE_CODE_TUPLE: encodeObjects((Object[]) v, dest); return;
case TYPE_CODE_TUPLE:
Object[] arr = (Object[]) value;
encodeArrayLen(arr.length, dest);
TupleType.encodeObjects(dynamic, arr, dest, (i) -> elementType);
return;
default: throw new Error();
}
}

private void encodeArrayLen(int len, ByteBuffer dest) {
if(length == DYNAMIC_LENGTH) {
Encoding.insertInt(len, dest);
}
}

private void encodeBooleans(boolean[] arr, ByteBuffer dest) {
insert(arr.length, dest, () -> {
for (boolean e : arr) {
BooleanType.encodeBoolean(e, dest);
}
});
encodeArrayLen(arr.length, dest);
for (boolean e : arr) {
BooleanType.encodeBoolean(e, dest);
}
}

private void encodeBytes(byte[] arr, ByteBuffer dest) {
insert(arr.length, dest, () -> Encoding.insertBytesPadded(arr, dest));
encodeArrayLen(arr.length, dest);
Encoding.insertBytesPadded(arr, dest);
}

private void encodeInts(int[] arr, ByteBuffer dest) {
insert(arr.length, dest, () -> {
for (int e : arr) {
Encoding.insertInt(e, dest);
}
});
}

private void encodeLongs(long[] arr, ByteBuffer dest) {
insert(arr.length, dest, () -> {
for (long e : arr) {
Encoding.insertInt(e, dest);
}
});
}

private void encodeObjects(Object[] arr, ByteBuffer dest) {
if(dynamic) {
insert(arr.length, dest, () -> insertOffsets(arr, dest));
}
for (Object object : arr) {
elementType.encodeTail(object, dest);
encodeArrayLen(arr.length, dest);
for (int e : arr) {
Encoding.insertInt(e, dest);
}
}

private void insertOffsets(Object[] objects, ByteBuffer dest) {
if (elementType.dynamic) {
int nextOffset = objects.length * OFFSET_LENGTH_BYTES;
for (Object object : objects) {
nextOffset = Encoding.insertOffset(nextOffset, dest, elementType.byteLength(object));
}
private void encodeLongs(long[] arr, ByteBuffer dest) {
encodeArrayLen(arr.length, dest);
for (long e : arr) {
Encoding.insertInt(e, dest);
}
}

Expand Down Expand Up @@ -377,7 +356,7 @@ private static Object decodeLongs(int len, ByteBuffer bb, LongType longType, byt

private Object decodeObjects(int len, ByteBuffer bb, byte[] unitBuffer) {
Object[] elements = (Object[]) Array.newInstance(elementType.clazz, len); // reflection ftw
decodeObjects(len, bb, unitBuffer, elements, (i) -> elementType);
TupleType.decodeObjects(len, bb, unitBuffer, elements, (i) -> elementType);
return elements;
}

Expand Down
26 changes: 13 additions & 13 deletions src/main/java/com/esaulpaugh/headlong/abi/PackedDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ private static int decode(ABIType<?> type, byte[] buffer, int idx, int end, Obje
case TYPE_CODE_BYTE: elements[i] = buffer[idx]; return type.byteLengthPacked(null);
case TYPE_CODE_INT: return insertInt((IntType) type, buffer, idx, type.byteLengthPacked(null), elements, i);
case TYPE_CODE_LONG: return insertLong((LongType) type, buffer, idx, type.byteLengthPacked(null), elements, i);
case TYPE_CODE_BIG_INTEGER: return insertBigInteger(type.byteLengthPacked(null), buffer, idx, elements, i, (BigIntegerType) type);
case TYPE_CODE_BIG_DECIMAL: return insertBigDecimal(type.byteLengthPacked(null), buffer, idx, elements, i, (BigDecimalType) type);
case TYPE_CODE_BIG_INTEGER: return insertBigInteger((BigIntegerType) type, type.byteLengthPacked(null), buffer, idx, elements, i);
case TYPE_CODE_BIG_DECIMAL: return insertBigDecimal((BigDecimalType) type, type.byteLengthPacked(null), buffer, idx, elements, i);
case TYPE_CODE_ARRAY: return insertArray((ArrayType<? extends ABIType<?>, ?>) type, buffer, idx, end, elements, i);
case TYPE_CODE_TUPLE:
return type.dynamic
Expand Down Expand Up @@ -152,7 +152,7 @@ private static int insertLong(UnitType<? extends Number> type, byte[] buffer, in
return len;
}

private static int insertBigInteger(int elementLen, byte[] buffer, int idx, Object[] dest, int destIdx, BigIntegerType type) {
private static int insertBigInteger(BigIntegerType type, int elementLen, byte[] buffer, int idx, Object[] dest, int destIdx) {
if(type.unsigned) {
byte[] copy = new byte[1 + elementLen];
System.arraycopy(buffer, idx, copy, 1, elementLen);
Expand All @@ -164,7 +164,7 @@ private static int insertBigInteger(int elementLen, byte[] buffer, int idx, Obje
return elementLen;
}

private static int insertBigDecimal(int elementLen, byte[] buffer, int idx, Object[] dest, int destIdx, BigDecimalType type) {
private static int insertBigDecimal(BigDecimalType type, int elementLen, byte[] buffer, int idx, Object[] dest, int destIdx) {
BigInteger unscaled;
if(type.unsigned) {
byte[] copy = new byte[1 + elementLen];
Expand Down Expand Up @@ -194,11 +194,11 @@ private static int insertArray(ArrayType<? extends ABIType<?>, ?> arrayType, byt
final Object array;
switch (elementType.typeCode()) {
case TYPE_CODE_BOOLEAN: array = decodeBooleanArray(arrayLen, buffer, idx); break;
case TYPE_CODE_BYTE: array = arrayType.encodeIfString(decodeByteArray(arrayLen, buffer, idx)); break;
case TYPE_CODE_BYTE: array = decodeByteArray(arrayType, arrayLen, buffer, idx); break;
case TYPE_CODE_INT: array = decodeIntArray((IntType) elementType, elementByteLen, arrayLen, buffer, idx); break;
case TYPE_CODE_LONG: array = decodeLongArray((LongType) elementType, elementByteLen, arrayLen, buffer, idx); break;
case TYPE_CODE_BIG_INTEGER: array = decodeBigIntegerArray(elementByteLen, arrayLen, buffer, idx, (BigIntegerType) elementType); break;
case TYPE_CODE_BIG_DECIMAL: array = decodeBigDecimalArray(elementByteLen, arrayLen, buffer, idx, (BigDecimalType) elementType); break;
case TYPE_CODE_BIG_INTEGER: array = decodeBigIntegerArray((BigIntegerType) elementType, elementByteLen, arrayLen, buffer, idx); break;
case TYPE_CODE_BIG_DECIMAL: array = decodeBigDecimalArray((BigDecimalType) elementType, elementByteLen, arrayLen, buffer, idx); break;
case TYPE_CODE_ARRAY:
case TYPE_CODE_TUPLE: array = decodeObjectArray(arrayLen, elementType, buffer, idx, end); break;
default: throw new Error();
Expand All @@ -215,10 +215,10 @@ private static boolean[] decodeBooleanArray(int arrayLen, byte[] buffer, int idx
return booleans;
}

private static byte[] decodeByteArray(int arrayLen, byte[] buffer, int idx) {
private static Object decodeByteArray(ArrayType<?, ?> arrayType, int arrayLen, byte[] buffer, int idx) {
byte[] bytes = new byte[arrayLen];
System.arraycopy(buffer, idx, bytes, 0, arrayLen);
return bytes;
return arrayType.encodeIfString(bytes);
}

private static int[] decodeIntArray(IntType intType, int elementLen, int arrayLen, byte[] buffer, int idx) {
Expand Down Expand Up @@ -247,18 +247,18 @@ private static long[] decodeLongArray(UnitType<? extends Number> type, int eleme
return longs;
}

private static BigInteger[] decodeBigIntegerArray(int elementLen, int arrayLen, byte[] buffer, int idx, BigIntegerType elementType) {
private static BigInteger[] decodeBigIntegerArray(BigIntegerType elementType, int elementLen, int arrayLen, byte[] buffer, int idx) {
BigInteger[] bigInts = new BigInteger[arrayLen];
for (int i = 0; i < arrayLen; i++) {
idx += insertBigInteger(elementLen, buffer, idx, bigInts, i, elementType);
idx += insertBigInteger(elementType, elementLen, buffer, idx, bigInts, i);
}
return bigInts;
}

private static BigDecimal[] decodeBigDecimalArray(int elementLen, int arrayLen, byte[] buffer, int idx, BigDecimalType elementType) {
private static BigDecimal[] decodeBigDecimalArray(BigDecimalType elementType, int elementLen, int arrayLen, byte[] buffer, int idx) {
BigDecimal[] bigDecimals = new BigDecimal[arrayLen];
for (int i = 0; i < arrayLen; i++) {
idx += insertBigDecimal(elementLen, buffer, idx, bigDecimals, i, elementType);
idx += insertBigDecimal(elementType, elementLen, buffer, idx, bigDecimals, i);
}
return bigDecimals;
}
Expand Down
67 changes: 50 additions & 17 deletions src/main/java/com/esaulpaugh/headlong/abi/TupleType.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.esaulpaugh.headlong.util.SuperSerial;

import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Iterator;
Expand Down Expand Up @@ -125,31 +126,26 @@ public int validate(final Object value) {

@Override
void encodeTail(Object value, ByteBuffer dest) {
final Object[] values = ((Tuple) value).elements;
if(!dynamic) {
encodeHeads(elementTypes, values, dest, -1);
return;
encodeObjects(dynamic, ((Tuple) value).elements, dest, (i) -> elementTypes[i]);
}

static void encodeObjects(boolean dynamic, Object[] values, ByteBuffer dest, IntFunction<ABIType<?>> getType) {
int nextOffset = !dynamic ? -1 : headLengthSum(values, getType);
for (int i = 0; i < values.length; i++) {
nextOffset = getType.apply(i).encodeHead(values[i], dest, nextOffset);
}
final ABIType<?>[] types = elementTypes;
encodeHeads(types, values, dest, headLengthSum(types, values));
for (int i = 0; i < types.length; i++) {
ABIType<?> t = types[i];
for (int i = 0; i < values.length; i++) {
ABIType<?> t = getType.apply(i);
if(t.dynamic) {
t.encodeTail(values[i], dest);
}
}
}

private static void encodeHeads(ABIType<?>[] types, Object[] values, ByteBuffer dest, int nextOffset) {
for (int i = 0; i < types.length; i++) {
nextOffset = types[i].encodeHead(values[i], dest, nextOffset);
}
}

private static int headLengthSum(ABIType<?>[] types, Object[] elements) {
private static int headLengthSum(Object[] elements, IntFunction<ABIType<?>> getType) {
int sum = 0;
for (int i = 0; i < types.length; i++) {
ABIType<?> type = types[i];
for (int i = 0; i < elements.length; i++) {
ABIType<?> type = getType.apply(i);
sum += !type.dynamic ? type.byteLength(elements[i]) : OFFSET_LENGTH_BYTES;
}
return sum;
Expand All @@ -163,6 +159,43 @@ Tuple decode(ByteBuffer bb, byte[] unitBuffer) {
return new Tuple(elements);
}

private static int[] decodeHeads(int len, ByteBuffer bb, byte[] unitBuffer, Object[] elements, IntFunction<ABIType<?>> getType) {
final int[] offsets = new int[len];
for(int i = 0; i < len; i++) {
ABIType<?> t = getType.apply(i);
if(!t.dynamic) {
elements[i] = t.decode(bb, unitBuffer);
} else {
offsets[i] = Encoding.UINT31.decode(bb, unitBuffer);
}
}
return offsets;
}

static void decodeObjects(int len, ByteBuffer bb, byte[] unitBuffer, Object[] elements, IntFunction<ABIType<?>> getType) {
final int start = bb.position(); // save this value before offsets are decoded
final int[] offsets = decodeHeads(len, bb, unitBuffer, elements, getType);
for (int i = 0; i < len; i++) {
final int offset = offsets[i];
if(offset > 0) {
final int jump = start + offset;
final int pos = bb.position();
if(jump != pos) {
/* LENIENT MODE; see https://github.com/ethereum/solidity/commit/3d1ca07e9b4b42355aa9be5db5c00048607986d1 */
if(jump < pos) {
throw new IllegalArgumentException("illegal backwards jump: (" + start + "+" + offset + "=" + jump + ")<" + pos);
}
bb.position(jump); // leniently jump to specified offset
}
try {
elements[i] = getType.apply(i).decode(bb, unitBuffer);
} catch (BufferUnderflowException bue) {
throw new IllegalArgumentException(bue);
}
}
}
}

/**
* Parses RLP Object {@link com.esaulpaugh.headlong.rlp.util.Notation} as a {@link Tuple}.
*
Expand Down
8 changes: 1 addition & 7 deletions src/main/java/com/esaulpaugh/headlong/abi/UnitType.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,9 @@ public int validate(Object value) {
return UNIT_LENGTH_BYTES;
}

@Override
int encodeHead(Object value, ByteBuffer dest, int nextOffset) {
Encoding.insertInt(((Number) value).longValue(), dest);
return nextOffset;
}

@Override
void encodeTail(Object value, ByteBuffer dest) {
encodeHead(value, dest, 0);
Encoding.insertInt(((Number) value).longValue(), dest);
}

final void validatePrimitive(long longVal) {
Expand Down
Loading

0 comments on commit de05423

Please sign in to comment.