Skip to content

Commit

Permalink
refactor decodePacked
Browse files Browse the repository at this point in the history
  • Loading branch information
esaulpaugh committed Aug 21, 2024
1 parent 1a4606b commit 26b0f7c
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 56 deletions.
13 changes: 9 additions & 4 deletions src/main/java/com/esaulpaugh/headlong/abi/ABIType.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,16 @@ public final J decode(ByteBuffer buffer) {
*/
abstract J decode(ByteBuffer buffer, byte[] unitBuffer);

@SuppressWarnings("unchecked")
public final J decodePacked(byte[] buffer) {
return PackedDecoder.decode(
new TupleType<>('(' + this.canonicalType + ')', dynamic, new ABIType[] { this }, null, null, this.getFlags()),
buffer
).get(0);
PackedDecoder.checkDynamics(this);
final J val = (J) PackedDecoder.decode(this, ByteBuffer.wrap(buffer), buffer.length);
validate(val);
final int decodedLen = byteLengthPacked(val);
if (decodedLen != buffer.length) {
throw new IllegalArgumentException("unconsumed bytes: " + (buffer.length - decodedLen) + " remaining");
}
return val;
}

static byte[] newUnitBuffer() {
Expand Down
60 changes: 24 additions & 36 deletions src/main/java/com/esaulpaugh/headlong/abi/PackedDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,35 +42,25 @@ final class PackedDecoder {

private PackedDecoder() {}

@SuppressWarnings("unchecked")
static <T extends Tuple> T decode(TupleType<T> tupleType, byte[] buffer) {
final int count = countDynamics(tupleType);
if (count <= 1) {
final Tuple tuple = decodeTuple(tupleType, ByteBuffer.wrap(buffer), buffer.length); // can also call decodeTupleStatic if numDynamic == 0
tupleType.validate(tuple);
int decodedLen = tupleType.byteLengthPacked(tuple);
if (decodedLen != buffer.length) {
throw new IllegalArgumentException("unconsumed bytes: " + (buffer.length - decodedLen) + " remaining");
static int checkDynamics(ABIType<?> type) {
int count = 0;
if (type.dynamic) {
if (type instanceof ArrayType) {
final ArrayType<?, ?, ?> at = type.asArrayType();
count = checkDynamics(at.getElementType());
if (DYNAMIC_LENGTH == at.getLength()) {
count++;
}
} else {
for (ABIType<?> e : type.asTupleType().elementTypes) {
count += checkDynamics(e);
}
}
if (count > 1) {
throw new IllegalArgumentException("multiple dynamic elements: " + count);
}
return (T) tuple;
}
throw new IllegalArgumentException("multiple dynamic elements: " + count);
}

static int countDynamics(ABIType<?> type) {
if (!type.dynamic) {
return 0;
}
if (type instanceof ArrayType) {
final ArrayType<?, ?, ?> at = type.asArrayType();
final int count = countDynamics(at.getElementType());
return DYNAMIC_LENGTH == at.getLength() ? count + 1 : count;
}
int numDynamic = 0;
for (ABIType<?> e : type.asTupleType().elementTypes) {
numDynamic += countDynamics(e);
}
return numDynamic;
return count;
}

private static Tuple decodeTuple(TupleType<?> tupleType, ByteBuffer bb, int end) {
Expand Down Expand Up @@ -102,20 +92,18 @@ private static Tuple decodeTuple(TupleType<?> tupleType, ByteBuffer bb, int end)
}
}

if (firstDynamicIndex != -1) {
for (int i = 0; i <= firstDynamicIndex; i++) {
ABIType<Object> t = tupleType.get(i);
bb.position(start);
Object e = decode(t, bb, end);
elements[i] = e;
start += t.byteLengthPacked(e);
}
for (int i = 0; i <= firstDynamicIndex; i++) {
ABIType<Object> t = tupleType.get(i);
bb.position(start);
Object e = decode(t, bb, end);
elements[i] = e;
start += t.byteLengthPacked(e);
}

return Tuple.create(elements);
}

private static Object decode(ABIType<?> type, ByteBuffer bb, int end) {
static Object decode(ABIType<?> type, ByteBuffer bb, int end) {
switch (type.typeCode()) {
case TYPE_CODE_BOOLEAN: return BooleanType.decodeBoolean(bb.get());
case TYPE_CODE_BYTE: return bb.get();
Expand Down
16 changes: 5 additions & 11 deletions src/test/java/com/esaulpaugh/headlong/abi/MonteCarloTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -316,25 +316,19 @@ private void runFuzzPackedDecode(Random r) {
void runPacked() {
final Tuple args = this.argsTuple;
final TupleType<Tuple> tt = this.function.getInputs();
final int numDynamics = PackedDecoder.countDynamics(tt);
if(tt.dynamic ^ (numDynamics != 0)) {
throw new AssertionError();
}
if(tt.canonicalType.contains("int[")) {
if (tt.canonicalType.contains("int[")) {
throw new AssertionError("failed canonicalization!");
}
final ByteBuffer bb = tt.encodePacked(args);
try {
Tuple decoded = tt.decodePacked(bb.array());
PackedDecoder.checkDynamics(tt);
Tuple decoded = tt.decodePacked(tt.encodePacked(args).array());
if (!decoded.equals(args)) {
throw new RuntimeException("not equal: " + tt.canonicalType);
}
} catch (IllegalArgumentException iae) {
final String msg = iae.getMessage();
if(msg.contains("multiple dynamic elements: ")) {
final int parsed = Integer.parseInt(msg.substring(msg.lastIndexOf(' ') + 1));
assertTrue(parsed > 1 && parsed == numDynamics);
} else if(!msg.endsWith("array of dynamic elements")
if(!msg.contains("multiple dynamic elements: ")
&& !msg.endsWith("array of dynamic elements")
&& !"can't decode dynamic number of zero-length elements".equals(msg)) {
throw new RuntimeException(tt.canonicalType + " " + msg, iae);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void testOverflow() throws Throwable {
System.out.println(Strings.encode(bb));
final byte[] corrupt = new byte[32];
corrupt[corrupt.length - 6] = -106;
assertThrown(ArithmeticException.class, "overflow", () -> PackedDecoder.decode(tt, corrupt));
assertThrown(ArithmeticException.class, "overflow", () -> PackedDecoder.decode(tt, ByteBuffer.wrap(corrupt), corrupt.length));
}

@Test
Expand Down Expand Up @@ -128,10 +128,8 @@ public void testHard() {
"0000000000000000000000000000000000000000000000000000000000000004", Strings.encode(bb)
);

final byte[] packed = bb.array();

assertEquals(test, PackedDecoder.decode(tupleType, packed));
Single<Tuple> t = tupleType.decodePacked(packed);
assertEquals(test, PackedDecoder.decode(tupleType, bb, bb.limit()));
Single<Tuple> t = tupleType.decodePacked(bb.array());
assertEquals(test, t);
}

Expand Down

0 comments on commit 26b0f7c

Please sign in to comment.