Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: improve decoding in CallTransaction #2647

Merged
merged 5 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Objects;

/**
Expand Down Expand Up @@ -140,8 +139,8 @@ public byte byteAt(int index) {
}

@Override
public byte[] copyArrayOfRange(int from, int to) {
return Arrays.copyOfRange(byteArray, from, to);
public void arraycopy(int srcPos, byte[] dest, int destPos, int length) {
System.arraycopy(byteArray, srcPos, dest, destPos, length);
}

@Override
Expand Down
84 changes: 77 additions & 7 deletions rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,62 @@

package co.rsk.core.types.bytes;

import java.util.Arrays;

/**
* A {@link BytesSlice} is a subsequence of bytes backed by another broader byte sequence.
*/
public interface BytesSlice extends HexPrintableBytes {

/**
* Copies an array from the {@link BytesSlice} source, beginning at the
* specified position, to the specified position of the destination array.
* A subsequence of array components are copied from this instance to the
* destination array referenced by {@code dest}. The number of components
* copied is equal to the {@code length} argument. The components at
* positions {@code srcPos} through {@code srcPos+length-1} in the source
* array are copied into positions {@code destPos} through
* {@code destPos+length-1}, respectively, of the destination
* array.
* <p>
* If the underlying byte array and {@code dest} argument refer to the
* same array object, then the copying is performed as if the
* components at positions {@code srcPos} through
* {@code srcPos+length-1} were first copied to a temporary
* array with {@code length} components and then the contents of
* the temporary array were copied into positions
* {@code destPos} through {@code destPos+length-1} of the
* destination array.
* <p>
* If {@code dest} is {@code null}, then a
* {@code NullPointerException} is thrown.
* <p>
* Otherwise, if any of the following is true, an
* {@code IndexOutOfBoundsException} is
* thrown and the destination is not modified:
* <ul>
* <li>The {@code srcPos} argument is negative.
* <li>The {@code destPos} argument is negative.
* <li>The {@code length} argument is negative.
* <li>{@code srcPos+length} is greater than
* {@code src.length}, the length of the source array.
* <li>{@code destPos+length} is greater than
* {@code dest.length}, the length of the destination array.
* </ul>
*
* <p>
* Note: this method mimics behaviour of {@link System#arraycopy(Object, int, Object, int, int)}
*
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied.
* @throws IndexOutOfBoundsException if copying would cause
* access of data outside array bounds.
* @throws NullPointerException if {@code dest} is {@code null}.
*/
void arraycopy(int srcPos, byte[] dest, int destPos, int length);

/**
* Copies the specified range of the specified array into a new array.
* The initial index of the range (<tt>from</tt>) must lie between zero
Expand All @@ -37,17 +88,30 @@ public interface BytesSlice extends HexPrintableBytes {
* greater than or equal to <tt>original.length - from</tt>. The length
* of the returned array will be <tt>to - from</tt>.
*
* <p>
* Note: this method mimics behaviour of {@link Arrays#copyOfRange(Object[], int, int)}
*
* @param from the initial index of the range to be copied, inclusive
* @param to the final index of the range to be copied, exclusive.
* (This index may lie outside the array.)
* @return a new array containing the specified range from the original array,
* truncated or padded with zeros to obtain the required length
* @throws ArrayIndexOutOfBoundsException if {@code from < 0}
* @throws IndexOutOfBoundsException if {@code from < 0}
* or {@code from > original.length}
* @throws IllegalArgumentException if <tt>from &gt; to</tt>
* @throws NullPointerException if <tt>original</tt> is null
*/
byte[] copyArrayOfRange(int from, int to);
default byte[] copyArrayOfRange(int from, int to) {
if (from < 0 || from > length()) {
throw new IndexOutOfBoundsException("invalid 'from': " + from);
}
int newLength = to - from;
if (newLength < 0) {
throw new IllegalArgumentException(from + " > " + to);
}
byte[] copy = new byte[newLength];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should copy array length also be Math.min(length() - from, newLength)? in other case it may contain zero bytes at the end which are not present in original array

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VaWheel , the idea here was to mimic the behaviour of Arrays#copyOfRange(Object[], int, int) (pls see this and this javadoc's)

arraycopy(from, copy, 0, Math.min(length() - from, newLength));
return copy;
}

default byte[] copyArray() {
return copyArrayOfRange(0, length());
Expand Down Expand Up @@ -104,11 +168,17 @@ public byte byteAt(int index) {
}

@Override
public byte[] copyArrayOfRange(int from, int to) {
if (from < 0 || from > to || to > length()) {
throw new IndexOutOfBoundsException("invalid 'from' and/or 'to': [" + from + ";" + to + ")");
public void arraycopy(int srcPos, byte[] dest, int destPos, int length) {
if (length < 0) {
throw new IndexOutOfBoundsException("invalid 'length': " + length);
}
if (srcPos < 0 || srcPos + length > length()) {
throw new IndexOutOfBoundsException("invalid 'srcPos' and/or 'length': [" + srcPos + ";" + length + ")");
}
if (destPos < 0 || destPos + length > dest.length) {
throw new IndexOutOfBoundsException("invalid 'destPos' and/or 'length': [" + destPos + ";" + length + ")");
}
return originBytes.copyArrayOfRange(this.from + from, this.from + to);
originBytes.arraycopy(this.from + srcPos, dest, destPos, length);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public String toFormattedString(@Nonnull HexPrintableBytes printableBytes, int o
}

if (length > 32) {
return printableBytes.toHexString(off, 15) + ".." + printableBytes.toHexString(off + length - 15, 15);
return printableBytes.toHexString(off, 16) + ".." + printableBytes.toHexString(off + length - 15, 15);
}
return printableBytes.toHexString(off, length);
}
Expand Down
2 changes: 2 additions & 0 deletions rskj-core/src/main/java/co/rsk/util/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public class StringUtils {

private static final int DEFAULT_MAX_LEN = 64;

private StringUtils() { /* hidden */ }

public static String trim(@Nullable String src) {
return trim(src, DEFAULT_MAX_LEN);
}
Expand Down
23 changes: 12 additions & 11 deletions rskj-core/src/main/java/org/ethereum/core/CallTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
package org.ethereum.core;

import co.rsk.core.RskAddress;
import co.rsk.core.types.bytes.Bytes;
import co.rsk.core.types.bytes.BytesSlice;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
Expand All @@ -36,7 +38,6 @@
import java.util.Arrays;

import static java.lang.String.format;
import static org.apache.commons.lang3.ArrayUtils.subarray;
import static org.apache.commons.lang3.StringUtils.stripEnd;
import static org.ethereum.util.ByteUtil.longToBytesNoLeadZeroes;

Expand Down Expand Up @@ -119,9 +120,9 @@ public static Type getType(String typeName) {
*/
public abstract byte[] encode(Object value);

public abstract Object decode(byte[] encoded, int offset);
public abstract Object decode(BytesSlice encoded, int offset);

public Object decode(byte[] encoded) {
public Object decode(BytesSlice encoded) {
return decode(encoded, 0);
}

Expand Down Expand Up @@ -187,12 +188,12 @@ public byte[] encode(Object value) {
}

@Override
public Object decode(byte[] encoded, int offset) {
public Object decode(BytesSlice encoded, int offset) {
return decodeInt(encoded, offset);
}

public static BigInteger decodeInt(byte[] encoded, int offset) {
return new BigInteger(Arrays.copyOfRange(encoded, offset, offset + 32));
public static BigInteger decodeInt(BytesSlice encoded, int offset) {
return new BigInteger(encoded.copyArrayOfRange(offset, offset + 32));
}

public static byte[] encodeInt(int i) {
Expand Down Expand Up @@ -222,7 +223,7 @@ public byte[] encode(Object value) {
}

@Override
public Object decode(byte[] encoded, int offset) {
public Object decode(BytesSlice encoded, int offset) {
return Boolean.valueOf(((Number) super.decode(encoded, offset)).intValue() != 0);
}
}
Expand Down Expand Up @@ -350,7 +351,7 @@ public Object[] decodeEventData(byte[] encodedData) {
checkFunctionType(FunctionType.event);
Param[] dataInputs = Arrays.stream(inputs).filter(i -> !i.indexed).toArray(Param[]::new);

return decode(encodedData, dataInputs);
return decode(Bytes.of(encodedData), dataInputs);
}

private void checkFunctionType(FunctionType expected) {
Expand Down Expand Up @@ -405,7 +406,7 @@ public byte[] encodeOutputs(Object... args) {
return encodeArguments(outputs, args);
}

private Object[] decode(byte[] encoded, Param[] params) {
private Object[] decode(BytesSlice encoded, Param[] params) {
Object[] ret = new Object[params.length];

int off = 0;
Expand All @@ -421,11 +422,11 @@ private Object[] decode(byte[] encoded, Param[] params) {
}

public Object[] decode(byte[] encoded) {
return decode(subarray(encoded, 4, encoded.length), inputs);
return decode(Bytes.of(encoded).slice(4, encoded.length), inputs);
}

public Object[] decodeResult(byte[] encodedRet) {
return decode(encodedRet, outputs);
return decode(Bytes.of(encodedRet), outputs);
}

public String formatSignature() {
Expand Down
27 changes: 14 additions & 13 deletions rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package org.ethereum.solidity;

import co.rsk.core.types.bytes.Bytes;
import co.rsk.core.types.bytes.BytesSlice;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import org.ethereum.util.ByteUtil;
Expand Down Expand Up @@ -101,9 +102,9 @@ public static SolidityType getType(String typeName) {
*/
public abstract byte[] encode(Object value);

public abstract Object decode(byte[] encoded, int offset);
public abstract Object decode(BytesSlice encoded, int offset);

public Object decode(byte[] encoded) {
public Object decode(BytesSlice encoded) {
return decode(encoded, 0);
}

Expand Down Expand Up @@ -196,7 +197,7 @@ public byte[] encodeList(List l) {
}

@Override
public Object[] decode(byte[] encoded, int offset) {
public Object[] decode(BytesSlice encoded, int offset) {
Utils.validateArrayAllegedSize(encoded, offset, getFixedSize());
Object[] result = new Object[size];
for (int i = 0; i < size; i++) {
Expand Down Expand Up @@ -247,8 +248,8 @@ public byte[] encodeList(List l) {
}

@Override
public Object decode(byte[] encoded, int origOffset) {
if (encoded.length == 0) {
public Object decode(BytesSlice encoded, int origOffset) {
if (encoded.length() == 0) {
return new Object[0];
}
int len = IntType.decodeInt(encoded, origOffset).intValue();
Expand Down Expand Up @@ -299,7 +300,7 @@ public byte[] encode(Object value) {
}

@Override
public Object decode(byte[] encoded, int offset) {
public Object decode(BytesSlice encoded, int offset) {
int len = IntType.decodeInt(encoded, offset).intValue();
offset += IntType.INT_SIZE;
return Utils.safeCopyOfRange(encoded, offset, len);
Expand All @@ -325,7 +326,7 @@ public byte[] encode(Object value) {
}

@Override
public Object decode(byte[] encoded, int offset) {
public Object decode(BytesSlice encoded, int offset) {
return new String((byte[]) super.decode(encoded, offset), StandardCharsets.UTF_8);
}
}
Expand Down Expand Up @@ -357,7 +358,7 @@ public byte[] encode(Object value) {
}

@Override
public Object decode(byte[] encoded, int offset) {
public Object decode(BytesSlice encoded, int offset) {
return Utils.safeCopyOfRange(encoded, offset, getFixedSize());
}
}
Expand All @@ -383,7 +384,7 @@ public byte[] encode(Object value) {
}

@Override
public Object decode(byte[] encoded, int offset) {
public Object decode(BytesSlice encoded, int offset) {
BigInteger asBigInteger = (BigInteger) super.decode(encoded, offset);
return DataWord.valueOf(asBigInteger.toByteArray());
}
Expand Down Expand Up @@ -434,14 +435,14 @@ public byte[] encode(Object value) {
}

@Override
public Object decode(byte[] encoded, int offset) {
public Object decode(BytesSlice encoded, int offset) {
return decodeInt(encoded, offset);
}

public static BigInteger decodeInt(byte[] encoded, int offset) {
public static BigInteger decodeInt(BytesSlice encoded, int offset) {
// This is here because getGasForData might send an empty payload which will produce an exception
// But currently the bridge would return the cost of RELEASE_BTC in this situation
if (encoded.length == 0) {
if (encoded.length() == 0) {
return BigInteger.ZERO;
}
return new BigInteger(Utils.safeCopyOfRange(encoded, offset, INT_SIZE));
Expand Down Expand Up @@ -474,7 +475,7 @@ public byte[] encode(Object value) {
}

@Override
public Object decode(byte[] encoded, int offset) {
public Object decode(BytesSlice encoded, int offset) {
return Boolean.valueOf(((Number) super.decode(encoded, offset)).intValue() != 0);
}
}
Expand Down
Loading
Loading