From 2a0c794f85a6d8d7b5b7f1c25556da44dc41f93a Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 12 Dec 2019 10:14:56 +0800 Subject: [PATCH 01/25] use jackson-like api --- README.md | 50 ++++++++-- build.gradle | 2 +- src/main/java/org/tdf/rlp/LazyByteArray.java | 2 +- src/main/java/org/tdf/rlp/RLPConstants.java | 12 +-- .../java/org/tdf/rlp/RLPDeserializer.java | 46 ++++----- src/main/java/org/tdf/rlp/RLPElement.java | 84 ++++++++++++++-- src/main/java/org/tdf/rlp/RLPItem.java | 95 ++++++------------- src/main/java/org/tdf/rlp/RLPList.java | 64 +++++++++++-- .../rlp/{RLPReader.java => RLPParser.java} | 30 +++--- src/main/java/org/tdf/rlp/RLPUtils.java | 2 +- src/test/java/org/tdf/rlp/Main.java | 6 +- src/test/java/org/tdf/rlp/Node.java | 6 +- src/test/java/org/tdf/rlp/RLPTest.java | 46 ++++----- 13 files changed, 276 insertions(+), 169 deletions(-) rename src/main/java/org/tdf/rlp/{RLPReader.java => RLPParser.java} (86%) diff --git a/README.md b/README.md index 1a093ab..cdc1a25 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,18 @@ RLP Encoding/Decoding - declaring your pojo ```java +package org.tdf.rlp; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + public class Node{ // RLP annotation specify the order of field in encoded list @RLP(0) public String name; - + @RLP(1) public List children; @@ -32,6 +39,15 @@ public class Node{ children.addAll(nodes); } + private static class Nested{ + @RLP + private List>> nested; + + public Nested() { + } + } + + public static void main(String[] args){ Node root = new Node("1"); root.addChildren(Arrays.asList(new Node("2"), new Node("3"))); @@ -40,15 +56,25 @@ public class Node{ root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); // encode to byte array - byte[] encoded = RLPElement.encode(root).getEncoded(); + byte[] encoded = RLPElement.encodeAsRLPElement(root).getEncoded(); // encode to rlp element - RLPElement el = RLPElement.encode(root); + RLPElement el = RLPElement.encodeAsRLPElement(root); // decode from byte array Node root2 = RLPDeserializer.deserialize(encoded, Node.class); assertTrue(root2.children.get(0).children.get(0).name.equals("4")); assertTrue(root2.children.get(0).children.get(1).name.equals("5")); assertTrue(root2.children.get(1).children.get(0).name.equals("6")); assertTrue(root2.children.get(1).children.get(1).name.equals("7")); + + Nested nested = new Nested(); + nested.nested = new ArrayList<>(); + nested.nested.add(new ArrayList<>()); + nested.nested.get(0).add(new ArrayList<>()); + nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); + encoded = RLPElement.encodeAsRLPElement(nested).getEncoded(); + nested = RLPDeserializer.deserialize(encoded, Nested.class); + assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); + assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); } public static void assertTrue(boolean b){ @@ -60,14 +86,19 @@ public class Node{ - custom encoding/decoding ```java +package org.tdf.rlp; + +import java.util.HashMap; +import java.util.Map; + public class Main{ public static class MapEncoderDecoder implements RLPEncoder>, RLPDecoder> { @Override public Map decode(RLPElement element) { - RLPList list = element.getAsList(); + RLPList list = element.asRLPList(); Map map = new HashMap<>(list.size() / 2); for (int i = 0; i < list.size(); i += 2) { - map.put(list.get(i).getAsItem().getString(), list.get(i+1).getAsItem().getString()); + map.put(list.get(i).asString(), list.get(i+1).asString()); } return map; } @@ -101,7 +132,7 @@ public class Main{ Map m = new HashMap<>(); m.put("a", "1"); m.put("b", "2"); - byte[] encoded = RLPElement.encode(new MapWrapper(m)).getEncoded(); + byte[] encoded = RLPElement.encodeAsRLPElement(new MapWrapper(m)).getEncoded(); MapWrapper decoded = RLPDeserializer.deserialize(encoded, MapWrapper.class); assertTrue(decoded.map.get("a").equals("1")); } @@ -110,6 +141,7 @@ public class Main{ if(!b) throw new RuntimeException("assertion failed"); } } + ``` - supports List and POJO only for rlp is ordered while set is no ordered, generic list could be nested to any deepth @@ -131,10 +163,10 @@ public class Main{ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - byte[] encoded = RLPElement.encode(nested).getEncoded(); + byte[] encoded = RLPElement.encode(nested); nested = RLPDeserializer.deserialize(encoded, Nested.class); - assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); - assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); + assert nested.nested.get(0).get(0).get(0).equals("aaa"); + assert nested.nested.get(0).get(0).get(1).equals("bbb"); } } ``` diff --git a/build.gradle b/build.gradle index 702bec7..e00d0fa 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'org.tdf' -version '1.0.7' +version '1.1.0' sourceCompatibility = 1.8 diff --git a/src/main/java/org/tdf/rlp/LazyByteArray.java b/src/main/java/org/tdf/rlp/LazyByteArray.java index 87b4e2d..b37ebc3 100644 --- a/src/main/java/org/tdf/rlp/LazyByteArray.java +++ b/src/main/java/org/tdf/rlp/LazyByteArray.java @@ -3,7 +3,7 @@ import java.util.Arrays; // reduce byte array copy by lazy loading -class LazyByteArray { +final class LazyByteArray { private byte[] data; private int offset; private int limit; diff --git a/src/main/java/org/tdf/rlp/RLPConstants.java b/src/main/java/org/tdf/rlp/RLPConstants.java index c5b09b2..3e98a25 100644 --- a/src/main/java/org/tdf/rlp/RLPConstants.java +++ b/src/main/java/org/tdf/rlp/RLPConstants.java @@ -1,13 +1,13 @@ package org.tdf.rlp; -public class RLPConstants { +public final class RLPConstants { /** * [0x80] * If a string is 0-55 bytes long, the RLP encoding consists of a single * byte with value 0x80 plus the length of the string followed by the * string. The range of the first byte is thus [0x80, 0xb7]. */ - static final int OFFSET_SHORT_ITEM = 0x80; + public static final int OFFSET_SHORT_ITEM = 0x80; /** * Reason for threshold according to Vitalik Buterin: @@ -18,7 +18,7 @@ public class RLPConstants { * - so 56 and 2^64 space seems like the right place to put the cutoff * - also, that's where Bitcoin's varint does the cutof */ - static final int SIZE_THRESHOLD = 56; + public static final int SIZE_THRESHOLD = 56; /** * [0xb7] @@ -29,7 +29,7 @@ public class RLPConstants { * \xb9\x04\x00 followed by the string. The range of the first byte is thus * [0xb8, 0xbf]. */ - static final int OFFSET_LONG_ITEM = 0xb7; + public static final int OFFSET_LONG_ITEM = 0xb7; /** * [0xc0] @@ -39,7 +39,7 @@ public class RLPConstants { * of the RLP encodings of the items. The range of the first byte is thus * [0xc0, 0xf7]. */ - static final int OFFSET_SHORT_LIST = 0xc0; + public static final int OFFSET_SHORT_LIST = 0xc0; /** * [0xf7] @@ -49,5 +49,5 @@ public class RLPConstants { * followed by the concatenation of the RLP encodings of the items. The * range of the first byte is thus [0xf8, 0xff]. */ - static final int OFFSET_LONG_LIST = 0xf7; + public static final int OFFSET_LONG_LIST = 0xf7; } diff --git a/src/main/java/org/tdf/rlp/RLPDeserializer.java b/src/main/java/org/tdf/rlp/RLPDeserializer.java index a1bf115..8ced4ca 100644 --- a/src/main/java/org/tdf/rlp/RLPDeserializer.java +++ b/src/main/java/org/tdf/rlp/RLPDeserializer.java @@ -7,20 +7,20 @@ import java.util.List; import java.util.stream.Collectors; -public class RLPDeserializer { +public final class RLPDeserializer { public static T deserialize(byte[] data, Class clazz) { RLPElement element = RLPElement.fromEncoded(data); return deserialize(element, clazz); } - public static List deserializeList(RLPElement element, Class elementType){ - return deserializeList(element.getAsList(), 1, elementType); + static List deserializeList(RLPElement element, Class elementType){ + return deserializeList(element.asRLPList(), 1, elementType); } - public static List deserializeList(byte[] data, Class elementType) { + static List deserializeList(byte[] data, Class elementType) { RLPElement element = RLPElement.fromEncoded(data); - return deserializeList(element.getAsList(), 1, elementType); + return deserializeList(element.asRLPList(), 1, elementType); } private static List deserializeList(RLPList list, int level, Class elementType) { @@ -28,13 +28,13 @@ private static List deserializeList(RLPList list, int level, Class elementTyp if (level > 1) { List res = new ArrayList(list.size()); for (int i = 0; i < list.size(); i++) { - res.add(deserializeList(list.get(i).getAsList(), level - 1, elementType)); + res.add(deserializeList(list.get(i).asRLPList(), level - 1, elementType)); } return res; } if (elementType == RLPElement.class) return list; if (elementType == RLPItem.class) { - return list.stream().map(x -> x.getAsItem()).collect(Collectors.toList()); + return list.stream().map(x -> x.asRLPItem()).collect(Collectors.toList()); } List res = new ArrayList<>(list.size()); for (int i = 0; i < list.size(); i++) { @@ -45,47 +45,47 @@ private static List deserializeList(RLPList list, int level, Class elementTyp public static T deserialize(RLPElement element, Class clazz) { if (clazz == RLPElement.class) return (T) element; - if (clazz == RLPList.class) return (T) element.getAsList(); - if (clazz == RLPItem.class) return (T) element.getAsItem(); - if(clazz == boolean.class || clazz == Boolean.class) return (T) Boolean.valueOf(element.getAsItem().getBoolean()); + if (clazz == RLPList.class) return (T) element.asRLPList(); + if (clazz == RLPItem.class) return (T) element.asRLPItem(); + if(clazz == boolean.class || clazz == Boolean.class) return (T) Boolean.valueOf(element.asBoolean()); RLPDecoder decoder = RLPUtils.getAnnotatedRLPDecoder(clazz); if (decoder != null) return (T) decoder.decode(element); // non null terminals if (clazz == Byte.class || clazz == byte.class) { - return (T) Byte.valueOf(element.getAsItem().getByte()); + return (T) Byte.valueOf(element.asByte()); } if (clazz == Short.class || clazz == short.class) { - return (T) Short.valueOf(element.getAsItem().getShort()); + return (T) Short.valueOf(element.asShort()); } if (clazz == Integer.class || clazz == int.class) { - return (T) Integer.valueOf(element.getAsItem().getInt()); + return (T) Integer.valueOf(element.asInt()); } if (clazz == Long.class || clazz == long.class) { - return (T) Long.valueOf(element.getAsItem().getLong()); + return (T) Long.valueOf(element.asLong()); } if (clazz == byte[].class) { - return (T) element.getAsItem().get(); + return (T) element.asBytes(); } // String is non-null, since we cannot differ between null empty string and null if (clazz == String.class) { - return (T) element.getAsItem().getString(); + return (T) element.asString(); } // big integer is non-null, since we cannot differ between zero and null if (clazz == BigInteger.class) { - return (T) element.getAsItem().getBigInteger(); + return (T) element.asBigInteger(); } if (element.isNull()) return null; if (clazz.isArray()) { Class elementType = clazz.getComponentType(); - Object res = Array.newInstance(clazz.getComponentType(), element.getAsList().size()); - for (int i = 0; i < element.getAsList().size(); i++) { - Array.set(res, i, deserialize(element.getAsList().get(i), elementType)); + Object res = Array.newInstance(clazz.getComponentType(), element.asRLPList().size()); + for (int i = 0; i < element.asRLPList().size(); i++) { + Array.set(res, i, deserialize(element.asRLPList().get(i), elementType)); } return (T) res; } // cannot determine generic type at runtime if (clazz == List.class) { - return (T) deserializeList(element.getAsList(), 1, RLPElement.class); + return (T) element.asRLPList(); } Object o; try { @@ -96,7 +96,7 @@ public static T deserialize(RLPElement element, Class clazz) { List fields = RLPUtils.getRLPFields(clazz); if (fields.size() == 0) throw new RuntimeException(clazz + " is not supported not RLP annotation found"); for (int i = 0; i < fields.size(); i++) { - RLPElement el = element.getAsList().get(i); + RLPElement el = element.asRLPList().get(i); Field f = fields.get(i); f.setAccessible(true); RLPDecoder fieldDecoder = RLPUtils.getAnnotatedRLPDecoder(f); @@ -123,7 +123,7 @@ public static T deserialize(RLPElement element, Class clazz) { continue; } RLPUtils.Resolved resolved = RLPUtils.resolveFieldType(f); - f.set(o, deserializeList(el.getAsList(), resolved.level, resolved.type)); + f.set(o, deserializeList(el.asRLPList(), resolved.level, resolved.type)); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/src/main/java/org/tdf/rlp/RLPElement.java b/src/main/java/org/tdf/rlp/RLPElement.java index bec1ef3..58ba80c 100644 --- a/src/main/java/org/tdf/rlp/RLPElement.java +++ b/src/main/java/org/tdf/rlp/RLPElement.java @@ -17,25 +17,43 @@ * A list of items is an item * RLP could encode tree-like object * RLP cannot determine difference between null reference and emtpy byte array - * RLP cannot determine whether a box type is null or zero, e.g. Byte, Short, Integer, Long + * RLP cannot determine whether a box type is null or zero, e.g. Byte, Short, Integer, Long, BigInteger */ public interface RLPElement { - boolean isList(); + boolean isRLPList(); - RLPList getAsList(); + boolean isRLPItem(); - RLPItem getAsItem(); + RLPList asRLPList(); + + RLPItem asRLPItem(); boolean isNull(); byte[] getEncoded(); + byte[] asBytes(); + + byte asByte(); + + short asShort(); + + int asInt(); + + long asLong(); + + BigInteger asBigInteger(); + + String asString(); + + boolean asBoolean(); + static RLPElement fromEncoded(byte[] data) { - return RLPReader.fromEncoded(data); + return RLPParser.fromEncoded(data); } // encode any object as a rlp element - static RLPElement encode(Object t) { + static RLPElement encodeAsRLPElement(Object t) { if (t == null) return NULL; if(t instanceof Boolean || t.getClass() == boolean.class){ return ((Boolean) t) ? ONE : NULL; @@ -64,14 +82,14 @@ static RLPElement encode(Object t) { if (t.getClass().isArray()) { RLPList list = RLPList.createEmpty(Array.getLength(t)); for (int i = 0; i < Array.getLength(t); i++) { - list.add(encode(Array.get(t, i))); + list.add(encodeAsRLPElement(Array.get(t, i))); } return list; } if (t instanceof Collection) { RLPList list = RLPList.createEmpty(((Collection) t).size()); for (Object o : ((Collection) t)) { - list.add(encode(o)); + list.add(encodeAsRLPElement(o)); } return list; } @@ -89,10 +107,58 @@ static RLPElement encode(Object t) { } } try { - return encode(f.get(t)); + return encodeAsRLPElement(f.get(t)); } catch (Exception e) { throw new RuntimeException(e); } }).collect(Collectors.toList())); } + + static byte[] encode(Object o){ + return encodeAsRLPElement(o).getEncoded(); + } + + static byte[] encodeBoolean(boolean b) { + return RLPItem.fromBoolean(b).getEncoded(); + } + + static boolean decodeBoolean(byte[] encoded) { + return fromEncoded(encoded).asBoolean(); + } + + static byte[] encodeByte(byte b) { + return RLPItem.fromByte(b).getEncoded(); + } + + static byte[] encodeShort(short s) { + return RLPItem.fromShort(s).getEncoded(); + } + + static byte[] encodeInt(int n) { + return RLPItem.fromInt(n).getEncoded(); + } + + static byte[] encodeBigInteger(BigInteger bigInteger) { + return RLPItem.fromBigInteger(bigInteger).getEncoded(); + } + + static byte[] encodeString(String s) { + return RLPItem.fromString(s).getEncoded(); + } + + static int decodeInt(byte[] encoded) { + return fromEncoded(encoded).asInt(); + } + + static short decodeShort(byte[] encoded) { + return fromEncoded(encoded).asShort(); + } + + static long decodeLong(byte[] encoded) { + return fromEncoded(encoded).asLong(); + } + + static String decodeString(byte[] encoded) { + return fromEncoded(encoded).asString(); + } } diff --git a/src/main/java/org/tdf/rlp/RLPItem.java b/src/main/java/org/tdf/rlp/RLPItem.java index d9b4d89..3099c93 100644 --- a/src/main/java/org/tdf/rlp/RLPItem.java +++ b/src/main/java/org/tdf/rlp/RLPItem.java @@ -25,7 +25,7 @@ void setEncoded(LazyByteArray encoded) { this.encoded = encoded; } - public byte[] get() { + public byte[] asBytes() { return data.get(); } @@ -92,17 +92,17 @@ private static byte[] asUnsignedByteArray( } @Override - public boolean isList() { + public boolean isRLPList() { return false; } @Override - public RLPList getAsList() { + public RLPList asRLPList() { throw new RuntimeException("not a rlp list"); } @Override - public RLPItem getAsItem() { + public RLPItem asRLPItem() { return this; } @@ -110,25 +110,25 @@ public boolean isNull() { return this == NULL || data.size() == 0; } - public byte getByte() { - if (Long.compareUnsigned(getLong(), 0xffL) > 0) throw new RuntimeException("invalid byte, overflow"); - return (byte) getLong(); + public byte asByte() { + if (Long.compareUnsigned(asLong(), 0xffL) > 0) throw new RuntimeException("invalid byte, overflow"); + return (byte) asLong(); } - public short getShort() { - if (Long.compareUnsigned(getLong(), 0xffff) > 0) throw new RuntimeException("invalid short, overflow"); - return (short) getLong(); + public short asShort() { + if (Long.compareUnsigned(asLong(), 0xffff) > 0) throw new RuntimeException("invalid short, overflow"); + return (short) asLong(); } - public int getInt() { - if (Long.compareUnsigned(getLong(), 0xffffffff) > 0) throw new RuntimeException("invalid int, overflow"); - return (int) getLong(); + public int asInt() { + if (Long.compareUnsigned(asLong(), 0xffffffff) > 0) throw new RuntimeException("invalid int, overflow"); + return (int) asLong(); } - public long getLong() { + public long asLong() { if (longNumber != null) return longNumber; // numbers are ont starts with zero byte - byte[] data = get(); + byte[] data = asBytes(); if (data.length > 0 && data[0] == 0) throw new RuntimeException("not a number"); if (isNull()) { longNumber = 0L; @@ -139,25 +139,25 @@ public long getLong() { return longNumber; } - public BigInteger getBigInteger() { - byte[] data = get(); + public BigInteger asBigInteger() { + byte[] data = asBytes(); if (data[0] == 0) throw new RuntimeException("not a number"); if (isNull()) return BigInteger.ZERO; return new BigInteger(1, data); } - public String getString() { - return new String(get(), StandardCharsets.UTF_8); + public String asString() { + return new String(asBytes(), StandardCharsets.UTF_8); } - public boolean getBoolean() { - if (getLong() > 1) throw new RuntimeException("not a boolean"); - return getLong() == 1; + public boolean asBoolean() { + if (asLong() > 1) throw new RuntimeException("not a boolean"); + return asLong() == 1; } public byte[] getEncoded() { if (isNull()) return NULL_ENCODED; - if (encoded == null) encoded = new LazyByteArray(encodeElement(get())); + if (encoded == null) encoded = new LazyByteArray(encodeElement(asBytes())); return encoded.get(); } @@ -207,50 +207,6 @@ public static byte[] encodeElement(byte[] srcData) { return data; } - public static byte[] encodeBoolean(boolean b) { - return fromBoolean(b).getEncoded(); - } - - public static boolean decodeBoolean(byte[] encoded) { - return RLPElement.fromEncoded(encoded).getAsItem().getBoolean(); - } - - public static byte[] encodeByte(byte b) { - return fromByte(b).getEncoded(); - } - - public static byte[] encodeShort(short s) { - return fromShort(s).getEncoded(); - } - - public static byte[] encodeInt(int n) { - return fromInt(n).getEncoded(); - } - - public static byte[] encodeBigInteger(BigInteger bigInteger) { - return fromBigInteger(bigInteger).getEncoded(); - } - - public static byte[] encodeString(String s) { - return fromString(s).getEncoded(); - } - - public static int decodeInt(byte[] encoded) { - return RLPElement.fromEncoded(encoded).getAsItem().getInt(); - } - - public static short decodeShort(byte[] encoded) { - return RLPElement.fromEncoded(encoded).getAsItem().getShort(); - } - - public static long decodeLong(byte[] encoded) { - return RLPElement.fromEncoded(encoded).getAsItem().getLong(); - } - - public static String decodeString(byte[] encoded) { - return RLPElement.fromEncoded(encoded).getAsItem().getString(); - } - /** * Returns the values from each provided array combined into a single array. For example, {@code * concat(new byte[] {a, b}, new byte[] {}, new byte[] {c}} returns the array {@code {a, b, c}}. @@ -271,4 +227,9 @@ private static byte[] concat(byte[]... arrays) { } return result; } + + @Override + public boolean isRLPItem() { + return true; + } } diff --git a/src/main/java/org/tdf/rlp/RLPList.java b/src/main/java/org/tdf/rlp/RLPList.java index 9810bb6..4a3ed9c 100644 --- a/src/main/java/org/tdf/rlp/RLPList.java +++ b/src/main/java/org/tdf/rlp/RLPList.java @@ -2,6 +2,7 @@ import lombok.NonNull; +import java.math.BigInteger; import java.util.*; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -47,29 +48,29 @@ private RLPList() { } @Override - public boolean isList() { + public boolean isRLPList() { return true; } @Override - public RLPList getAsList() { + public RLPList asRLPList() { return this; } @Override - public RLPItem getAsItem() { + public RLPItem asRLPItem() { throw new RuntimeException("not a rlp item"); } @Override public byte[] getEncoded() { - if(size() == 0) return EMPTY_ENCODED_LIST; - if(encoded != null) return encoded.get(); + if (size() == 0) return EMPTY_ENCODED_LIST; + if (encoded != null) return encoded.get(); encoded = new LazyByteArray( - encodeList( - stream().map(RLPElement::getEncoded) - .collect(Collectors.toList()) - ) + encodeList( + stream().map(RLPElement::getEncoded) + .collect(Collectors.toList()) + ) ); return encoded.get(); } @@ -277,4 +278,49 @@ public RLPList subList(int fromIndex, int toIndex) { public Spliterator spliterator() { return elements.spliterator(); } + + @Override + public boolean isRLPItem() { + return false; + } + + @Override + public byte[] asBytes() { + throw new RuntimeException("not a rlp item"); + } + + @Override + public byte asByte() { + throw new RuntimeException("not a rlp item"); + } + + @Override + public short asShort() { + throw new RuntimeException("not a rlp item"); + } + + @Override + public long asLong() { + throw new RuntimeException("not a rlp item"); + } + + @Override + public BigInteger asBigInteger() { + throw new RuntimeException("not a rlp item"); + } + + @Override + public String asString() { + throw new RuntimeException("not a rlp item"); + } + + @Override + public boolean asBoolean() { + throw new RuntimeException("not a rlp item"); + } + + @Override + public int asInt() { + throw new RuntimeException("not a rlp item"); + } } diff --git a/src/main/java/org/tdf/rlp/RLPReader.java b/src/main/java/org/tdf/rlp/RLPParser.java similarity index 86% rename from src/main/java/org/tdf/rlp/RLPReader.java rename to src/main/java/org/tdf/rlp/RLPParser.java index 08cb83a..4f9d7ee 100644 --- a/src/main/java/org/tdf/rlp/RLPReader.java +++ b/src/main/java/org/tdf/rlp/RLPParser.java @@ -8,7 +8,7 @@ import static org.tdf.rlp.RLPConstants.*; -class RLPReader { +final class RLPParser { private static int byteArrayToInt(byte[] b) { if (b == null || b.length == 0) return 0; @@ -22,29 +22,29 @@ private static int byteArrayToInt(byte[] b) { private int limit; static RLPElement fromEncoded(@NonNull byte[] data) { - RLPReader reader = new RLPReader(data); - if (reader.estimateSize() != data.length) { + RLPParser parser = new RLPParser(data); + if (parser.estimateSize() != data.length) { throw new RuntimeException("invalid encoding"); } - return reader.readElement(); + return parser.readElement(); } - private RLPReader(byte[] data) { + private RLPParser(byte[] data) { this.raw = data; this.limit = data.length; } - private RLPReader(byte[] data, int offset, int limit) { + private RLPParser(byte[] data, int offset, int limit) { this.raw = data; this.offset = offset; this.limit = limit; } - private RLPReader readAsReader(int length) { + private RLPParser readAsParser(int length) { if (offset + length > limit) throw new RuntimeException("read overflow"); - RLPReader reader = new RLPReader(raw, offset, offset + length); + RLPParser parser = new RLPParser(raw, offset, offset + length); offset += length; - return reader; + return parser; } private int estimateSize() { @@ -96,20 +96,20 @@ private RLPList readList() { int offset = this.offset; int prefix = read(); RLPList list = RLPList.createEmpty(); - RLPReader reader; + RLPParser parser; if (prefix <= OFFSET_LONG_LIST) { int len = prefix - OFFSET_SHORT_LIST; // length of length the encoded bytes // skip preifx if (len == 0) return list; - reader = readAsReader(len); + parser = readAsParser(len); } else { int lenlen = prefix - OFFSET_LONG_LIST; // length of length the encoded list int lenlist = byteArrayToInt(read(lenlen)); // length of encoded bytes - reader = readAsReader(lenlist); + parser = readAsParser(lenlist); } - int limit = reader.limit; - while (reader.hasRemaining()) { - list.add(reader.readElement()); + int limit = parser.limit; + while (parser.hasRemaining()) { + list.add(parser.readElement()); } list.setEncoded(new LazyByteArray(raw, offset, limit)); return list; diff --git a/src/main/java/org/tdf/rlp/RLPUtils.java b/src/main/java/org/tdf/rlp/RLPUtils.java index 44f4708..725a488 100644 --- a/src/main/java/org/tdf/rlp/RLPUtils.java +++ b/src/main/java/org/tdf/rlp/RLPUtils.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.stream.Collectors; -class RLPUtils { +final class RLPUtils { static RLPEncoder getAnnotatedRLPEncoder(AnnotatedElement element) { if (!element.isAnnotationPresent(RLPEncoding.class)) { return null; diff --git a/src/test/java/org/tdf/rlp/Main.java b/src/test/java/org/tdf/rlp/Main.java index 6f5abaf..332c516 100644 --- a/src/test/java/org/tdf/rlp/Main.java +++ b/src/test/java/org/tdf/rlp/Main.java @@ -7,10 +7,10 @@ public class Main{ public static class MapEncoderDecoder implements RLPEncoder>, RLPDecoder> { @Override public Map decode(RLPElement element) { - RLPList list = element.getAsList(); + RLPList list = element.asRLPList(); Map map = new HashMap<>(list.size() / 2); for (int i = 0; i < list.size(); i += 2) { - map.put(list.get(i).getAsItem().getString(), list.get(i+1).getAsItem().getString()); + map.put(list.get(i).asString(), list.get(i+1).asString()); } return map; } @@ -44,7 +44,7 @@ public static void main(String[] args){ Map m = new HashMap<>(); m.put("a", "1"); m.put("b", "2"); - byte[] encoded = RLPElement.encode(new MapWrapper(m)).getEncoded(); + byte[] encoded = RLPElement.encodeAsRLPElement(new MapWrapper(m)).getEncoded(); MapWrapper decoded = RLPDeserializer.deserialize(encoded, MapWrapper.class); assertTrue(decoded.map.get("a").equals("1")); } diff --git a/src/test/java/org/tdf/rlp/Node.java b/src/test/java/org/tdf/rlp/Node.java index 8a015b6..cbe04cb 100644 --- a/src/test/java/org/tdf/rlp/Node.java +++ b/src/test/java/org/tdf/rlp/Node.java @@ -47,9 +47,9 @@ public static void main(String[] args){ root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); // encode to byte array - byte[] encoded = RLPElement.encode(root).getEncoded(); + byte[] encoded = RLPElement.encodeAsRLPElement(root).getEncoded(); // encode to rlp element - RLPElement el = RLPElement.encode(root); + RLPElement el = RLPElement.encodeAsRLPElement(root); // decode from byte array Node root2 = RLPDeserializer.deserialize(encoded, Node.class); assertTrue(root2.children.get(0).children.get(0).name.equals("4")); @@ -62,7 +62,7 @@ public static void main(String[] args){ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - encoded = RLPElement.encode(nested).getEncoded(); + encoded = RLPElement.encodeAsRLPElement(nested).getEncoded(); nested = RLPDeserializer.deserialize(encoded, Nested.class); assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 6f38681..a79f42e 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -15,7 +15,9 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.tdf.rlp.RLPItem.*; +import static org.tdf.rlp.RLPElement.*; +import static org.tdf.rlp.RLPItem.NULL; +import static org.tdf.rlp.RLPItem.encodeElement; @RunWith(JUnit4.class) public class RLPTest { @@ -59,7 +61,7 @@ public static class RLPSerializer { public static RLPSerializer SERIALIZER = new RLPSerializer(); public byte[] serialize(Object o) { - return RLPElement.encode(o).getEncoded(); + return RLPElement.encodeAsRLPElement(o).getEncoded(); } } @@ -112,7 +114,7 @@ public void test1() { assert serializer.strings.get(0).equals("1"); assert serializer.strings.get(1).equals("2"); assert serializer.strings.get(2).equals("3"); - RLPList list = RLPElement.fromEncoded(data).getAsList(); + RLPList list = RLPElement.fromEncoded(data).asRLPList(); list.setEncoded(null); assertArrayEquals(data, list.getEncoded()); } @@ -132,7 +134,7 @@ public void testEncodeList() { expected = "cc83646f6783676f6483636174"; encoderesult = RLPSerializer.SERIALIZER.serialize(test); assertEquals(expected, HexBytes.encode(encoderesult)); - assertArrayEquals(RLPElement.fromEncoded(encoderesult).getAsList().stream().map(x -> x.getAsItem().getString()).toArray(), test); + assertArrayEquals(RLPElement.fromEncoded(encoderesult).asRLPList().stream().map(x -> x.asRLPItem().asString()).toArray(), test); } @Test @@ -405,9 +407,9 @@ public void testEncodeShortString() throws Exception { String expected = "83646f67"; byte[] encoderesult = encodeString(test); assertEquals(expected, HexBytes.encode(encoderesult)); - assert RLPElement.fromEncoded(HexBytes.decode(expected)).getAsItem().getString().equals(test); + assert RLPElement.fromEncoded(HexBytes.decode(expected)).asRLPItem().asString().equals(test); - byte[] decodeResult = RLPElement.fromEncoded(encoderesult).getAsItem().get(); + byte[] decodeResult = RLPElement.fromEncoded(encoderesult).asRLPItem().asBytes(); assertEquals(test, new String(decodeResult, StandardCharsets.US_ASCII)); } @@ -429,13 +431,13 @@ public void performanceDecode() throws Exception { long start1 = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - list = RLPElement.fromEncoded(payload).getAsList(); + list = RLPElement.fromEncoded(payload).asRLPList(); } long end1 = System.currentTimeMillis(); long start2 = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - list = RLPElement.fromEncoded(payload).getAsList(); + list = RLPElement.fromEncoded(payload).asRLPList(); } long end2 = System.currentTimeMillis(); @@ -460,7 +462,7 @@ public void encodeEdgeShortList() throws Exception { assertArrayEquals(RLPElement.fromEncoded(HexBytes.decode(expectedOutput)).getEncoded(), HexBytes.decode(expectedOutput)); assertArrayEquals( RLPElement.fromEncoded(HexBytes.decode(expectedOutput)) - .getAsList().stream().map(x -> x.getEncoded()).toArray(), + .asRLPList().stream().map(x -> x.getEncoded()).toArray(), new byte[][]{rlpKeysList, rlpValuesList, rlpCode} ); } @@ -470,7 +472,7 @@ public void encodeBigIntegerEdge_1() { BigInteger integer = new BigInteger("80", 10); byte[] encodedData = encodeBigInteger(integer); - assert RLPElement.fromEncoded(encodedData).getAsItem().getInt() == 80; + assert RLPElement.fromEncoded(encodedData).asRLPItem().asInt() == 80; } @Test @@ -478,7 +480,7 @@ public void testEncodeInt_7f() throws Exception { String result = HexBytes.encode(encodeInt(0x7f)); String expected = "7f"; assertEquals(expected, result); - assert RLPElement.fromEncoded(HexBytes.decode(expected)).getAsItem().getInt() == 0x7f; + assert RLPElement.fromEncoded(HexBytes.decode(expected)).asRLPItem().asInt() == 0x7f; } @Test @@ -486,7 +488,7 @@ public void testEncodeInt_80() throws Exception { String result = HexBytes.encode(encodeInt(0x80)); String expected = "8180"; assertEquals(expected, result); - assert RLPElement.fromEncoded(HexBytes.decode(expected)).getAsItem().getInt() == 0x80; + assert RLPElement.fromEncoded(HexBytes.decode(expected)).asRLPItem().asInt() == 0x80; } @@ -495,7 +497,7 @@ public void testEncode_ED() throws Exception { String result = HexBytes.encode(encodeInt(0xED)); String expected = "81ed"; assertEquals(expected, result); - assert RLPElement.fromEncoded(HexBytes.decode(result)).getAsItem().getInt() == 0xED; + assert RLPElement.fromEncoded(HexBytes.decode(result)).asRLPItem().asInt() == 0xED; } // encode a binary tree @@ -508,7 +510,7 @@ public void testTreeLike() { root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); byte[] encoded = RLPSerializer.SERIALIZER.serialize(root); - RLPElement el = RLPElement.encode(root); + RLPElement el = RLPElement.encodeAsRLPElement(root); Node root2 = RLPDeserializer.deserialize(encoded, Node.class); assert root2.children.get(0).children.get(0).name.equals("4"); assert root2.children.get(0).children.get(1).name.equals("5"); @@ -522,10 +524,10 @@ public static class MapEncoderDecoder implements RLPEncoder> @Override public Map decode(RLPElement element) { - RLPList list = element.getAsList(); + RLPList list = element.asRLPList(); Map map = new HashMap<>(list.size() / 2); for (int i = 0; i < list.size(); i += 2) { - map.put(list.get(i).getAsItem().getString(), list.get(i + 1).getAsItem().getString()); + map.put(list.get(i).asRLPItem().asString(), list.get(i + 1).asRLPItem().asString()); } return map; } @@ -588,7 +590,7 @@ public void rlpEncodedLength() throws Exception { byte[] rlpBomb = HexBytes.decode(rlpBombStr); boolean isProtected = false; try { - RLPList list = RLPElement.fromEncoded(rlpBomb).getAsList(); + RLPList list = RLPElement.fromEncoded(rlpBomb).asRLPList(); } catch (Throwable cause) { // decode2 is protected! while (cause != null) { @@ -601,7 +603,7 @@ public void rlpEncodedLength() throws Exception { isProtected = false; try { - RLPList list = RLPElement.fromEncoded(rlpBomb).getAsList(); + RLPList list = RLPElement.fromEncoded(rlpBomb).asRLPList(); } catch (Throwable cause) { // decode is protected now too! // decode2 is protected! @@ -1150,7 +1152,7 @@ public void testNestedListsBomb() { @Test(expected = Exception.class) public void testEncodeFail() { - RLPElement.encode(new Foo()); + RLPElement.encodeAsRLPElement(new Foo()); } static class Foo { @@ -1161,7 +1163,7 @@ static class Foo { @Test public void testListCache() { byte[] encoded = RLPList.of(RLPItem.fromInt(1), RLPItem.fromInt(2)).getEncoded(); - RLPList list = RLPElement.fromEncoded(encoded).getAsList(); + RLPList list = RLPElement.fromEncoded(encoded).asRLPList(); byte[] encoded2 = list.getEncoded(); list.setEncoded(null); byte[] encoded3 = list.getEncoded(); @@ -1172,7 +1174,7 @@ public void testListCache() { @Test public void testItemCache() { byte[] encoded = RLPItem.fromString("hello world").getEncoded(); - RLPItem item = RLPElement.fromEncoded(encoded).getAsItem(); + RLPItem item = RLPElement.fromEncoded(encoded).asRLPItem(); byte[] encoded2 = item.getEncoded(); item.setEncoded(null); byte[] encoded3 = item.getEncoded(); @@ -1255,7 +1257,7 @@ public void testDecode3(){ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - byte[] encoded = RLPSerializer.SERIALIZER.serialize(nested); + byte[] encoded = RLPElement.encode(nested); nested = RLPDeserializer.deserialize(encoded, Nested.class); assert nested.nested.get(0).get(0).get(0).equals("aaa"); assert nested.nested.get(0).get(0).get(1).equals("bbb"); From 2e7e93a1266a6cf6f5a3db4d3ee2d72f45f5b97f Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 12 Dec 2019 10:27:31 +0800 Subject: [PATCH 02/25] more jackson-like --- build.gradle | 2 +- src/main/java/org/tdf/rlp/RLPElement.java | 12 ++++++------ src/test/java/org/tdf/rlp/Main.java | 2 +- src/test/java/org/tdf/rlp/Node.java | 6 +++--- src/test/java/org/tdf/rlp/RLPTest.java | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index e00d0fa..937a783 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'org.tdf' -version '1.1.0' +version '1.1.1' sourceCompatibility = 1.8 diff --git a/src/main/java/org/tdf/rlp/RLPElement.java b/src/main/java/org/tdf/rlp/RLPElement.java index 58ba80c..8643fb7 100644 --- a/src/main/java/org/tdf/rlp/RLPElement.java +++ b/src/main/java/org/tdf/rlp/RLPElement.java @@ -52,8 +52,8 @@ static RLPElement fromEncoded(byte[] data) { return RLPParser.fromEncoded(data); } - // encode any object as a rlp element - static RLPElement encodeAsRLPElement(Object t) { + // convert any object as a rlp tree + static RLPElement readRLPTree(Object t) { if (t == null) return NULL; if(t instanceof Boolean || t.getClass() == boolean.class){ return ((Boolean) t) ? ONE : NULL; @@ -82,14 +82,14 @@ static RLPElement encodeAsRLPElement(Object t) { if (t.getClass().isArray()) { RLPList list = RLPList.createEmpty(Array.getLength(t)); for (int i = 0; i < Array.getLength(t); i++) { - list.add(encodeAsRLPElement(Array.get(t, i))); + list.add(readRLPTree(Array.get(t, i))); } return list; } if (t instanceof Collection) { RLPList list = RLPList.createEmpty(((Collection) t).size()); for (Object o : ((Collection) t)) { - list.add(encodeAsRLPElement(o)); + list.add(readRLPTree(o)); } return list; } @@ -107,7 +107,7 @@ static RLPElement encodeAsRLPElement(Object t) { } } try { - return encodeAsRLPElement(f.get(t)); + return readRLPTree(f.get(t)); } catch (Exception e) { throw new RuntimeException(e); } @@ -115,7 +115,7 @@ static RLPElement encodeAsRLPElement(Object t) { } static byte[] encode(Object o){ - return encodeAsRLPElement(o).getEncoded(); + return readRLPTree(o).getEncoded(); } static byte[] encodeBoolean(boolean b) { diff --git a/src/test/java/org/tdf/rlp/Main.java b/src/test/java/org/tdf/rlp/Main.java index 332c516..e94f80d 100644 --- a/src/test/java/org/tdf/rlp/Main.java +++ b/src/test/java/org/tdf/rlp/Main.java @@ -44,7 +44,7 @@ public static void main(String[] args){ Map m = new HashMap<>(); m.put("a", "1"); m.put("b", "2"); - byte[] encoded = RLPElement.encodeAsRLPElement(new MapWrapper(m)).getEncoded(); + byte[] encoded = RLPElement.encode(new MapWrapper(m)); MapWrapper decoded = RLPDeserializer.deserialize(encoded, MapWrapper.class); assertTrue(decoded.map.get("a").equals("1")); } diff --git a/src/test/java/org/tdf/rlp/Node.java b/src/test/java/org/tdf/rlp/Node.java index cbe04cb..36978de 100644 --- a/src/test/java/org/tdf/rlp/Node.java +++ b/src/test/java/org/tdf/rlp/Node.java @@ -47,9 +47,9 @@ public static void main(String[] args){ root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); // encode to byte array - byte[] encoded = RLPElement.encodeAsRLPElement(root).getEncoded(); + byte[] encoded = RLPElement.encode(root); // encode to rlp element - RLPElement el = RLPElement.encodeAsRLPElement(root); + RLPElement el = RLPElement.readRLPTree(root); // decode from byte array Node root2 = RLPDeserializer.deserialize(encoded, Node.class); assertTrue(root2.children.get(0).children.get(0).name.equals("4")); @@ -62,7 +62,7 @@ public static void main(String[] args){ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - encoded = RLPElement.encodeAsRLPElement(nested).getEncoded(); + encoded = RLPElement.encode(nested); nested = RLPDeserializer.deserialize(encoded, Nested.class); assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index a79f42e..562c8aa 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -61,7 +61,7 @@ public static class RLPSerializer { public static RLPSerializer SERIALIZER = new RLPSerializer(); public byte[] serialize(Object o) { - return RLPElement.encodeAsRLPElement(o).getEncoded(); + return RLPElement.readRLPTree(o).getEncoded(); } } @@ -510,7 +510,7 @@ public void testTreeLike() { root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); byte[] encoded = RLPSerializer.SERIALIZER.serialize(root); - RLPElement el = RLPElement.encodeAsRLPElement(root); + RLPElement el = RLPElement.readRLPTree(root); Node root2 = RLPDeserializer.deserialize(encoded, Node.class); assert root2.children.get(0).children.get(0).name.equals("4"); assert root2.children.get(0).children.get(1).name.equals("5"); @@ -1152,7 +1152,7 @@ public void testNestedListsBomb() { @Test(expected = Exception.class) public void testEncodeFail() { - RLPElement.encodeAsRLPElement(new Foo()); + RLPElement.readRLPTree(new Foo()); } static class Foo { From 64610af3c158606749ebbab696490706470f71bb Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 12 Dec 2019 10:30:19 +0800 Subject: [PATCH 03/25] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cdc1a25..dbb0a12 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,9 @@ public class Node{ root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); // encode to byte array - byte[] encoded = RLPElement.encodeAsRLPElement(root).getEncoded(); + byte[] encoded = RLPElement.encode(root); // encode to rlp element - RLPElement el = RLPElement.encodeAsRLPElement(root); + RLPElement el = RLPElement.readRLPTree(root); // decode from byte array Node root2 = RLPDeserializer.deserialize(encoded, Node.class); assertTrue(root2.children.get(0).children.get(0).name.equals("4")); @@ -71,7 +71,7 @@ public class Node{ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - encoded = RLPElement.encodeAsRLPElement(nested).getEncoded(); + encoded = RLPElement.encode(nested); nested = RLPDeserializer.deserialize(encoded, Nested.class); assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); @@ -132,7 +132,7 @@ public class Main{ Map m = new HashMap<>(); m.put("a", "1"); m.put("b", "2"); - byte[] encoded = RLPElement.encodeAsRLPElement(new MapWrapper(m)).getEncoded(); + byte[] encoded = RLPElement.encode(new MapWrapper(m)); MapWrapper decoded = RLPDeserializer.deserialize(encoded, MapWrapper.class); assertTrue(decoded.map.get("a").equals("1")); } @@ -155,6 +155,7 @@ public class Nested{ } } ``` + ```java public class Main{ public static void main(String[] args){ From 2d73f031858d306b7a414cfebb33f6a99cf236ad Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 12 Dec 2019 10:45:06 +0800 Subject: [PATCH 04/25] Update RLPEncoder.java --- src/main/java/org/tdf/rlp/RLPEncoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/tdf/rlp/RLPEncoder.java b/src/main/java/org/tdf/rlp/RLPEncoder.java index 7cdb034..5952426 100644 --- a/src/main/java/org/tdf/rlp/RLPEncoder.java +++ b/src/main/java/org/tdf/rlp/RLPEncoder.java @@ -3,7 +3,7 @@ public interface RLPEncoder { RLPElement encode(T o); - class None implements RLPEncoder{ + class None implements RLPEncoder{ @Override public RLPElement encode(Object o) { return null; From 1ab5586a2999bcaf9cb1ec5eac01e2759e5012d1 Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 12 Dec 2019 13:15:41 +0800 Subject: [PATCH 05/25] Update RLPDeserializer.java --- src/main/java/org/tdf/rlp/RLPDeserializer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/tdf/rlp/RLPDeserializer.java b/src/main/java/org/tdf/rlp/RLPDeserializer.java index 8ced4ca..83f7eed 100644 --- a/src/main/java/org/tdf/rlp/RLPDeserializer.java +++ b/src/main/java/org/tdf/rlp/RLPDeserializer.java @@ -88,6 +88,11 @@ public static T deserialize(RLPElement element, Class clazz) { return (T) element.asRLPList(); } Object o; + try{ + clazz.getConstructor(); + }catch (Exception e){ + throw new RuntimeException(clazz + " should has a no arguments constructor"); + } try { o = clazz.newInstance(); } catch (Exception e) { From 8db1377b97a2833f9e91aa8613a1c7f823d67d9b Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Thu, 12 Dec 2019 21:21:38 +0800 Subject: [PATCH 06/25] rename apis --- .../{RLPDeserializer.java => RLPCodec.java} | 76 +++++++++++++++---- src/main/java/org/tdf/rlp/RLPElement.java | 46 ----------- src/test/java/org/tdf/rlp/Main.java | 6 +- src/test/java/org/tdf/rlp/Node.java | 11 ++- src/test/java/org/tdf/rlp/RLPTest.java | 50 ++++++------ 5 files changed, 98 insertions(+), 91 deletions(-) rename src/main/java/org/tdf/rlp/{RLPDeserializer.java => RLPCodec.java} (66%) diff --git a/src/main/java/org/tdf/rlp/RLPDeserializer.java b/src/main/java/org/tdf/rlp/RLPCodec.java similarity index 66% rename from src/main/java/org/tdf/rlp/RLPDeserializer.java rename to src/main/java/org/tdf/rlp/RLPCodec.java index 83f7eed..3a21e0f 100644 --- a/src/main/java/org/tdf/rlp/RLPDeserializer.java +++ b/src/main/java/org/tdf/rlp/RLPCodec.java @@ -7,28 +7,28 @@ import java.util.List; import java.util.stream.Collectors; -public final class RLPDeserializer { +public final class RLPCodec { - public static T deserialize(byte[] data, Class clazz) { + public static T decode(byte[] data, Class clazz) { RLPElement element = RLPElement.fromEncoded(data); - return deserialize(element, clazz); + return decode(element, clazz); } - static List deserializeList(RLPElement element, Class elementType){ - return deserializeList(element.asRLPList(), 1, elementType); + static List decodeList(RLPElement element, Class elementType){ + return decodeList(element.asRLPList(), 1, elementType); } - static List deserializeList(byte[] data, Class elementType) { + static List decodeList(byte[] data, Class elementType) { RLPElement element = RLPElement.fromEncoded(data); - return deserializeList(element.asRLPList(), 1, elementType); + return decodeList(element.asRLPList(), 1, elementType); } - private static List deserializeList(RLPList list, int level, Class elementType) { + private static List decodeList(RLPList list, int level, Class elementType) { if (level == 0) throw new RuntimeException("level should be positive"); if (level > 1) { List res = new ArrayList(list.size()); for (int i = 0; i < list.size(); i++) { - res.add(deserializeList(list.get(i).asRLPList(), level - 1, elementType)); + res.add(decodeList(list.get(i).asRLPList(), level - 1, elementType)); } return res; } @@ -38,12 +38,12 @@ private static List deserializeList(RLPList list, int level, Class elementTyp } List res = new ArrayList<>(list.size()); for (int i = 0; i < list.size(); i++) { - res.add(deserialize(list.get(i), elementType)); + res.add(decode(list.get(i), elementType)); } return res; } - public static T deserialize(RLPElement element, Class clazz) { + public static T decode(RLPElement element, Class clazz) { if (clazz == RLPElement.class) return (T) element; if (clazz == RLPList.class) return (T) element.asRLPList(); if (clazz == RLPItem.class) return (T) element.asRLPItem(); @@ -79,7 +79,7 @@ public static T deserialize(RLPElement element, Class clazz) { Class elementType = clazz.getComponentType(); Object res = Array.newInstance(clazz.getComponentType(), element.asRLPList().size()); for (int i = 0; i < element.asRLPList().size(); i++) { - Array.set(res, i, deserialize(element.asRLPList().get(i), elementType)); + Array.set(res, i, decode(element.asRLPList().get(i), elementType)); } return (T) res; } @@ -116,7 +116,7 @@ public static T deserialize(RLPElement element, Class clazz) { if (!f.getType().equals(List.class)) { try { - f.set(o, deserialize(el, f.getType())); + f.set(o, decode(el, f.getType())); } catch (Exception e) { throw new RuntimeException(e); } @@ -128,11 +128,59 @@ public static T deserialize(RLPElement element, Class clazz) { continue; } RLPUtils.Resolved resolved = RLPUtils.resolveFieldType(f); - f.set(o, deserializeList(el.asRLPList(), resolved.level, resolved.type)); + f.set(o, decodeList(el.asRLPList(), resolved.level, resolved.type)); } catch (Exception e) { throw new RuntimeException(e); } } return (T) o; } + + static byte[] encodeBoolean(boolean b) { + return RLPItem.fromBoolean(b).getEncoded(); + } + + static boolean decodeBoolean(byte[] encoded) { + return RLPElement.fromEncoded(encoded).asBoolean(); + } + + static byte[] encodeByte(byte b) { + return RLPItem.fromByte(b).getEncoded(); + } + + static byte[] encodeShort(short s) { + return RLPItem.fromShort(s).getEncoded(); + } + + static byte[] encodeInt(int n) { + return RLPItem.fromInt(n).getEncoded(); + } + + static byte[] encodeBigInteger(BigInteger bigInteger) { + return RLPItem.fromBigInteger(bigInteger).getEncoded(); + } + + static byte[] encodeString(String s) { + return RLPItem.fromString(s).getEncoded(); + } + + static int decodeInt(byte[] encoded) { + return RLPElement.fromEncoded(encoded).asInt(); + } + + static short decodeShort(byte[] encoded) { + return RLPElement.fromEncoded(encoded).asShort(); + } + + static long decodeLong(byte[] encoded) { + return RLPElement.fromEncoded(encoded).asLong(); + } + + static String decodeString(byte[] encoded) { + return RLPElement.fromEncoded(encoded).asString(); + } + + static byte[] encode(Object o){ + return RLPElement.readRLPTree(o).getEncoded(); + } } diff --git a/src/main/java/org/tdf/rlp/RLPElement.java b/src/main/java/org/tdf/rlp/RLPElement.java index 8643fb7..2c39a92 100644 --- a/src/main/java/org/tdf/rlp/RLPElement.java +++ b/src/main/java/org/tdf/rlp/RLPElement.java @@ -114,51 +114,5 @@ static RLPElement readRLPTree(Object t) { }).collect(Collectors.toList())); } - static byte[] encode(Object o){ - return readRLPTree(o).getEncoded(); - } - - static byte[] encodeBoolean(boolean b) { - return RLPItem.fromBoolean(b).getEncoded(); - } - - static boolean decodeBoolean(byte[] encoded) { - return fromEncoded(encoded).asBoolean(); - } - - static byte[] encodeByte(byte b) { - return RLPItem.fromByte(b).getEncoded(); - } - - static byte[] encodeShort(short s) { - return RLPItem.fromShort(s).getEncoded(); - } - - static byte[] encodeInt(int n) { - return RLPItem.fromInt(n).getEncoded(); - } - static byte[] encodeBigInteger(BigInteger bigInteger) { - return RLPItem.fromBigInteger(bigInteger).getEncoded(); - } - - static byte[] encodeString(String s) { - return RLPItem.fromString(s).getEncoded(); - } - - static int decodeInt(byte[] encoded) { - return fromEncoded(encoded).asInt(); - } - - static short decodeShort(byte[] encoded) { - return fromEncoded(encoded).asShort(); - } - - static long decodeLong(byte[] encoded) { - return fromEncoded(encoded).asLong(); - } - - static String decodeString(byte[] encoded) { - return fromEncoded(encoded).asString(); - } } diff --git a/src/test/java/org/tdf/rlp/Main.java b/src/test/java/org/tdf/rlp/Main.java index e94f80d..00f9469 100644 --- a/src/test/java/org/tdf/rlp/Main.java +++ b/src/test/java/org/tdf/rlp/Main.java @@ -3,6 +3,8 @@ import java.util.HashMap; import java.util.Map; +import static org.tdf.rlp.RLPCodec.encode; + public class Main{ public static class MapEncoderDecoder implements RLPEncoder>, RLPDecoder> { @Override @@ -44,8 +46,8 @@ public static void main(String[] args){ Map m = new HashMap<>(); m.put("a", "1"); m.put("b", "2"); - byte[] encoded = RLPElement.encode(new MapWrapper(m)); - MapWrapper decoded = RLPDeserializer.deserialize(encoded, MapWrapper.class); + byte[] encoded = encode(new MapWrapper(m)); + MapWrapper decoded = RLPCodec.decode(encoded, MapWrapper.class); assertTrue(decoded.map.get("a").equals("1")); } diff --git a/src/test/java/org/tdf/rlp/Node.java b/src/test/java/org/tdf/rlp/Node.java index 36978de..27f9e86 100644 --- a/src/test/java/org/tdf/rlp/Node.java +++ b/src/test/java/org/tdf/rlp/Node.java @@ -5,6 +5,9 @@ import java.util.Collection; import java.util.List; +import static org.tdf.rlp.RLPCodec.decode; +import static org.tdf.rlp.RLPCodec.encode; + public class Node{ // RLP annotation specify the order of field in encoded list @RLP(0) @@ -47,11 +50,11 @@ public static void main(String[] args){ root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); // encode to byte array - byte[] encoded = RLPElement.encode(root); + byte[] encoded = encode(root); // encode to rlp element RLPElement el = RLPElement.readRLPTree(root); // decode from byte array - Node root2 = RLPDeserializer.deserialize(encoded, Node.class); + Node root2 = decode(encoded, Node.class); assertTrue(root2.children.get(0).children.get(0).name.equals("4")); assertTrue(root2.children.get(0).children.get(1).name.equals("5")); assertTrue(root2.children.get(1).children.get(0).name.equals("6")); @@ -62,8 +65,8 @@ public static void main(String[] args){ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - encoded = RLPElement.encode(nested); - nested = RLPDeserializer.deserialize(encoded, Nested.class); + encoded = encode(nested); + nested = decode(encoded, Nested.class); assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); } diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 562c8aa..dfb75a8 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -15,7 +15,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.tdf.rlp.RLPElement.*; +import static org.tdf.rlp.RLPCodec.*; import static org.tdf.rlp.RLPItem.NULL; import static org.tdf.rlp.RLPItem.encodeElement; @@ -82,35 +82,35 @@ public static byte[] decode(String s) throws Exception { @Test public void test0() { byte[] data = RLPSerializer.SERIALIZER.serialize(-1L); - assert RLPDeserializer.deserialize(data, Long.class) == -1L; + assert RLPCodec.decode(data, Long.class) == -1L; data = RLPSerializer.SERIALIZER.serialize(0L); - assert RLPDeserializer.deserialize(data, Long.class) == 0; + assert RLPCodec.decode(data, Long.class) == 0; data = RLPSerializer.SERIALIZER.serialize(Long.MAX_VALUE); - assert RLPDeserializer.deserialize(data, Long.class) == Long.MAX_VALUE; + assert RLPCodec.decode(data, Long.class) == Long.MAX_VALUE; data = RLPSerializer.SERIALIZER.serialize(Long.MIN_VALUE); - assert RLPDeserializer.deserialize(data, Long.class) == Long.MIN_VALUE; + assert RLPCodec.decode(data, Long.class) == Long.MIN_VALUE; data = RLPSerializer.SERIALIZER.serialize(Integer.valueOf(0)); - assert RLPDeserializer.deserialize(data, Integer.class) == 0; + assert RLPCodec.decode(data, Integer.class) == 0; data = RLPSerializer.SERIALIZER.serialize(Integer.MIN_VALUE); - assert RLPDeserializer.deserialize(data, Integer.class) == Integer.MIN_VALUE; + assert RLPCodec.decode(data, Integer.class) == Integer.MIN_VALUE; data = RLPSerializer.SERIALIZER.serialize(Integer.MAX_VALUE); - assert RLPDeserializer.deserialize(data, Integer.class) == Integer.MAX_VALUE; + assert RLPCodec.decode(data, Integer.class) == Integer.MAX_VALUE; data = RLPSerializer.SERIALIZER.serialize(Integer.valueOf(-1)); - assert RLPDeserializer.deserialize(data, Integer.class) == -1; + assert RLPCodec.decode(data, Integer.class) == -1; data = RLPSerializer.SERIALIZER.serialize(Short.valueOf((short) 0)); - assert RLPDeserializer.deserialize(data, Short.class) == 0; + assert RLPCodec.decode(data, Short.class) == 0; data = RLPSerializer.SERIALIZER.serialize(Short.MIN_VALUE); - assert RLPDeserializer.deserialize(data, Short.class) == Short.MIN_VALUE; + assert RLPCodec.decode(data, Short.class) == Short.MIN_VALUE; data = RLPSerializer.SERIALIZER.serialize(Short.MAX_VALUE); - assert RLPDeserializer.deserialize(data, Short.class) == Short.MAX_VALUE; + assert RLPCodec.decode(data, Short.class) == Short.MAX_VALUE; } @Test public void test1() { byte[] data = RLPSerializer.SERIALIZER.serialize(new TestSerializer(Arrays.asList("1", "2", "3"))); - TestSerializer serializer = RLPDeserializer.deserialize(data, TestSerializer.class); + TestSerializer serializer = RLPCodec.decode(data, TestSerializer.class); assert serializer.strings.get(0).equals("1"); assert serializer.strings.get(1).equals("2"); assert serializer.strings.get(2).equals("3"); @@ -127,7 +127,7 @@ public void testEncodeList() { byte[] encoderesult = RLPSerializer.SERIALIZER.serialize(test); assertEquals(expected, HexBytes.encode(encoderesult)); - String[] decodedTest = RLPDeserializer.deserialize(encoderesult, String[].class); + String[] decodedTest = RLPCodec.decode(encoderesult, String[].class); assertArrayEquals(decodedTest, test); test = new String[]{"dog", "god", "cat"}; @@ -511,7 +511,7 @@ public void testTreeLike() { byte[] encoded = RLPSerializer.SERIALIZER.serialize(root); RLPElement el = RLPElement.readRLPTree(root); - Node root2 = RLPDeserializer.deserialize(encoded, Node.class); + Node root2 = RLPCodec.decode(encoded, Node.class); assert root2.children.get(0).children.get(0).name.equals("4"); assert root2.children.get(0).children.get(1).name.equals("5"); assert root2.children.get(1).children.get(0).name.equals("6"); @@ -563,7 +563,7 @@ public void testMap() { m.put("a", "1"); m.put("b", "2"); byte[] encoded = RLPSerializer.SERIALIZER.serialize(new MapWrapper(m)); - MapWrapper decoded = RLPDeserializer.deserialize(encoded, MapWrapper.class); + MapWrapper decoded = RLPCodec.decode(encoded, MapWrapper.class); assert decoded.map.get("a").equals("1"); byte[] encoded2 = MapEncoderDecoder.CODEC.encode(m).getEncoded(); Map m2 = MapEncoderDecoder.CODEC.decode(RLPElement.fromEncoded(encoded2)); @@ -1245,7 +1245,7 @@ public void testDecode2(){ nested.nested.addAll(Arrays.asList("aaa", "bbb")); byte[] encoded = RLPSerializer.SERIALIZER.serialize(nested); - NoNested noNested = RLPDeserializer.deserialize(encoded, NoNested.class); + NoNested noNested = RLPCodec.decode(encoded, NoNested.class); assert noNested.nested.get(0).equals("aaa"); assert noNested.nested.get(1).equals("bbb"); } @@ -1257,8 +1257,8 @@ public void testDecode3(){ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - byte[] encoded = RLPElement.encode(nested); - nested = RLPDeserializer.deserialize(encoded, Nested.class); + byte[] encoded = RLPCodec.encode(nested); + nested = RLPCodec.decode(encoded, Nested.class); assert nested.nested.get(0).get(0).get(0).equals("aaa"); assert nested.nested.get(0).get(0).get(1).equals("bbb"); } @@ -1268,7 +1268,7 @@ public void testNestedString(){ RLPList li1 = RLPList.of(RLPItem.fromString("aa"), RLPItem.fromString("bbb")); RLPList li2 = RLPList.of(RLPItem.fromString("aa"), RLPItem.fromString("bbb")); byte[] encoded = RLPList.of(li1, li2).getEncoded(); - String[][] strs = RLPDeserializer.deserialize(encoded, String[][].class); + String[][] strs = RLPCodec.decode(encoded, String[][].class); assert strs[0][0].equals("aa"); assert strs[0][1].equals("bbb"); assert strs[1][0].equals("aa"); @@ -1277,25 +1277,25 @@ public void testNestedString(){ @Test public void testBoolean(){ - assert !RLPDeserializer.deserialize(NULL, Boolean.class); - assert RLPDeserializer.deserialize(RLPItem.fromBoolean(true), Boolean.class); + assert !RLPCodec.decode(NULL, Boolean.class); + assert RLPCodec.decode(RLPItem.fromBoolean(true), Boolean.class); List elements = Stream.of(1, 1, 1).map(RLPItem::fromInt).collect(Collectors.toList()); RLPList list = RLPList.fromElements(elements); - assert RLPDeserializer.deserializeList( + assert RLPCodec.decodeList( list, Boolean.class ).stream().allMatch(x -> x); } @Test(expected = RuntimeException.class) public void testBooleanFailed(){ - RLPDeserializer.deserialize(RLPItem.fromInt(2), Boolean.class); + RLPCodec.decode(RLPItem.fromInt(2), Boolean.class); } @Test(expected = RuntimeException.class) public void testBooleanFailed2(){ List elements = Stream.of(1, 2, 3).map(RLPItem::fromInt).collect(Collectors.toList()); RLPList list = RLPList.fromElements(elements); - RLPDeserializer.deserializeList( + RLPCodec.decodeList( list, Boolean.class ); } From 14168b3dd1468b5f963271d3ec6c8f63fe6d8acf Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Thu, 12 Dec 2019 21:21:58 +0800 Subject: [PATCH 07/25] Update build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 937a783..5ca789d 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'org.tdf' -version '1.1.1' +version '1.1.2' sourceCompatibility = 1.8 From 0518a4e0f33fb30ef0ae46999eeef758cb5201ec Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Thu, 12 Dec 2019 21:23:44 +0800 Subject: [PATCH 08/25] Update README.md --- README.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index dbb0a12..83902e9 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import static org.tdf.rlp.RLPCodec.decode; +import static org.tdf.rlp.RLPCodec.encode; + public class Node{ // RLP annotation specify the order of field in encoded list @RLP(0) @@ -56,11 +59,11 @@ public class Node{ root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); // encode to byte array - byte[] encoded = RLPElement.encode(root); + byte[] encoded = encode(root); // encode to rlp element RLPElement el = RLPElement.readRLPTree(root); // decode from byte array - Node root2 = RLPDeserializer.deserialize(encoded, Node.class); + Node root2 = decode(encoded, Node.class); assertTrue(root2.children.get(0).children.get(0).name.equals("4")); assertTrue(root2.children.get(0).children.get(1).name.equals("5")); assertTrue(root2.children.get(1).children.get(0).name.equals("6")); @@ -71,8 +74,8 @@ public class Node{ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - encoded = RLPElement.encode(nested); - nested = RLPDeserializer.deserialize(encoded, Nested.class); + encoded = encode(nested); + nested = decode(encoded, Nested.class); assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); } @@ -91,6 +94,8 @@ package org.tdf.rlp; import java.util.HashMap; import java.util.Map; +import static org.tdf.rlp.RLPCodec.encode; + public class Main{ public static class MapEncoderDecoder implements RLPEncoder>, RLPDecoder> { @Override @@ -132,8 +137,8 @@ public class Main{ Map m = new HashMap<>(); m.put("a", "1"); m.put("b", "2"); - byte[] encoded = RLPElement.encode(new MapWrapper(m)); - MapWrapper decoded = RLPDeserializer.deserialize(encoded, MapWrapper.class); + byte[] encoded = encode(new MapWrapper(m)); + MapWrapper decoded = RLPCodec.decode(encoded, MapWrapper.class); assertTrue(decoded.map.get("a").equals("1")); } @@ -164,8 +169,8 @@ public class Main{ nested.nested.add(new ArrayList<>()); nested.nested.get(0).add(new ArrayList<>()); nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - byte[] encoded = RLPElement.encode(nested); - nested = RLPDeserializer.deserialize(encoded, Nested.class); + byte[] encoded = RLPCodec.encode(nested); + nested = RLPCodec.decode(encoded, Nested.class); assert nested.nested.get(0).get(0).get(0).equals("aaa"); assert nested.nested.get(0).get(0).get(1).equals("bbb"); } From c417d93d2e43775f7afce10cf9add4a875dad83d Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Thu, 12 Dec 2019 21:26:07 +0800 Subject: [PATCH 09/25] Update README.md --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index 83902e9..1843ebe 100644 --- a/README.md +++ b/README.md @@ -68,16 +68,6 @@ public class Node{ assertTrue(root2.children.get(0).children.get(1).name.equals("5")); assertTrue(root2.children.get(1).children.get(0).name.equals("6")); assertTrue(root2.children.get(1).children.get(1).name.equals("7")); - - Nested nested = new Nested(); - nested.nested = new ArrayList<>(); - nested.nested.add(new ArrayList<>()); - nested.nested.get(0).add(new ArrayList<>()); - nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - encoded = encode(nested); - nested = decode(encoded, Nested.class); - assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); - assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); } public static void assertTrue(boolean b){ From a0457a6cb79f620033e55d8e2fbd3a87acbef640 Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Thu, 12 Dec 2019 23:55:37 +0800 Subject: [PATCH 10/25] fix bug when as big integer --- build.gradle | 2 +- src/main/java/org/tdf/rlp/RLPCodec.java | 119 +++++++++++++++++++--- src/main/java/org/tdf/rlp/RLPDecoder.java | 2 +- src/main/java/org/tdf/rlp/RLPElement.java | 4 + src/main/java/org/tdf/rlp/RLPItem.java | 62 ++--------- src/main/java/org/tdf/rlp/RLPList.java | 50 +-------- src/test/java/org/tdf/rlp/HashUtil.java | 4 +- src/test/java/org/tdf/rlp/RLPTest.java | 43 +++++--- 8 files changed, 159 insertions(+), 127 deletions(-) diff --git a/build.gradle b/build.gradle index 5ca789d..4ae9b09 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'org.tdf' -version '1.1.2' +version '1.1.3' sourceCompatibility = 1.8 diff --git a/src/main/java/org/tdf/rlp/RLPCodec.java b/src/main/java/org/tdf/rlp/RLPCodec.java index 3a21e0f..aa6c89c 100644 --- a/src/main/java/org/tdf/rlp/RLPCodec.java +++ b/src/main/java/org/tdf/rlp/RLPCodec.java @@ -1,12 +1,18 @@ package org.tdf.rlp; +import lombok.NonNull; + import java.lang.reflect.Array; import java.lang.reflect.Field; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import static org.tdf.rlp.RLPConstants.*; + public final class RLPCodec { public static T decode(byte[] data, Class clazz) { @@ -136,51 +142,140 @@ public static T decode(RLPElement element, Class clazz) { return (T) o; } - static byte[] encodeBoolean(boolean b) { + // rlp primitives encoding/decoding + public static byte[] encodeBoolean(boolean b) { return RLPItem.fromBoolean(b).getEncoded(); } - static boolean decodeBoolean(byte[] encoded) { + public static boolean decodeBoolean(byte[] encoded) { return RLPElement.fromEncoded(encoded).asBoolean(); } - static byte[] encodeByte(byte b) { + public static byte[] encodeByte(byte b) { return RLPItem.fromByte(b).getEncoded(); } - static byte[] encodeShort(short s) { + public static byte[] encodeShort(short s) { return RLPItem.fromShort(s).getEncoded(); } - static byte[] encodeInt(int n) { + public static byte[] encodeInt(int n) { return RLPItem.fromInt(n).getEncoded(); } - static byte[] encodeBigInteger(BigInteger bigInteger) { + public static byte[] encodeBigInteger(BigInteger bigInteger) { return RLPItem.fromBigInteger(bigInteger).getEncoded(); } - static byte[] encodeString(String s) { + public static byte[] encodeString(String s) { return RLPItem.fromString(s).getEncoded(); } - static int decodeInt(byte[] encoded) { + public static int decodeInt(byte[] encoded) { return RLPElement.fromEncoded(encoded).asInt(); } - static short decodeShort(byte[] encoded) { + public static short decodeShort(byte[] encoded) { return RLPElement.fromEncoded(encoded).asShort(); } - static long decodeLong(byte[] encoded) { + public static long decodeLong(byte[] encoded) { return RLPElement.fromEncoded(encoded).asLong(); } - static String decodeString(byte[] encoded) { + public static String decodeString(byte[] encoded) { return RLPElement.fromEncoded(encoded).asString(); } - static byte[] encode(Object o){ + public static byte[] encode(Object o){ return RLPElement.readRLPTree(o).getEncoded(); } + + // rlp list encode + public static byte[] encodeBytes(byte[] srcData) { + // [0x80] + if (srcData == null || srcData.length == 0) { + return new byte[]{(byte) OFFSET_SHORT_ITEM}; + // [0x00] + } + if (srcData.length == 1 && (srcData[0] & 0xFF) < OFFSET_SHORT_ITEM) { + return srcData; + // [0x80, 0xb7], 0 - 55 bytes + } + if (srcData.length < SIZE_THRESHOLD) { + // length = 8X + byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length); + byte[] data = Arrays.copyOf(srcData, srcData.length + 1); + System.arraycopy(data, 0, data, 1, srcData.length); + data[0] = length; + + return data; + // [0xb8, 0xbf], 56+ bytes + } + // length of length = BX + // prefix = [BX, [length]] + int tmpLength = srcData.length; + byte lengthOfLength = 0; + while (tmpLength != 0) { + ++lengthOfLength; + tmpLength = tmpLength >> 8; + } + + // set length Of length at first byte + byte[] data = new byte[1 + lengthOfLength + srcData.length]; + data[0] = (byte) (OFFSET_LONG_ITEM + lengthOfLength); + + // copy length after first byte + tmpLength = srcData.length; + for (int i = lengthOfLength; i > 0; --i) { + data[i] = (byte) (tmpLength & 0xFF); + tmpLength = tmpLength >> 8; + } + + // at last copy the number bytes after its length + System.arraycopy(srcData, 0, data, 1 + lengthOfLength, srcData.length); + + return data; + } + + public static byte[] encodeElements(@NonNull Collection elements) { + int totalLength = 0; + for (byte[] element1 : elements) { + totalLength += element1.length; + } + + byte[] data; + int copyPos; + if (totalLength < SIZE_THRESHOLD) { + + data = new byte[1 + totalLength]; + data[0] = (byte) (OFFSET_SHORT_LIST + totalLength); + copyPos = 1; + } else { + // length of length = BX + // prefix = [BX, [length]] + int tmpLength = totalLength; + byte byteNum = 0; + while (tmpLength != 0) { + ++byteNum; + tmpLength = tmpLength >> 8; + } + tmpLength = totalLength; + byte[] lenBytes = new byte[byteNum]; + for (int i = 0; i < byteNum; ++i) { + lenBytes[byteNum - 1 - i] = (byte) ((tmpLength >> (8 * i)) & 0xFF); + } + // first byte = F7 + bytes.length + data = new byte[1 + lenBytes.length + totalLength]; + data[0] = (byte) (OFFSET_LONG_LIST + byteNum); + System.arraycopy(lenBytes, 0, data, 1, lenBytes.length); + + copyPos = lenBytes.length + 1; + } + for (byte[] element : elements) { + System.arraycopy(element, 0, data, copyPos, element.length); + copyPos += element.length; + } + return data; + } } diff --git a/src/main/java/org/tdf/rlp/RLPDecoder.java b/src/main/java/org/tdf/rlp/RLPDecoder.java index 596f06e..c45c837 100644 --- a/src/main/java/org/tdf/rlp/RLPDecoder.java +++ b/src/main/java/org/tdf/rlp/RLPDecoder.java @@ -3,7 +3,7 @@ public interface RLPDecoder { T decode(RLPElement element); - class None implements RLPDecoder{ + class None implements RLPDecoder{ @Override public Object decode(RLPElement element) { return null; diff --git a/src/main/java/org/tdf/rlp/RLPElement.java b/src/main/java/org/tdf/rlp/RLPElement.java index 2c39a92..c8f9ba8 100644 --- a/src/main/java/org/tdf/rlp/RLPElement.java +++ b/src/main/java/org/tdf/rlp/RLPElement.java @@ -48,6 +48,10 @@ public interface RLPElement { boolean asBoolean(); + default T as(Class clazz){ + return RLPCodec.decode(this, clazz); + } + static RLPElement fromEncoded(byte[] data) { return RLPParser.fromEncoded(data); } diff --git a/src/main/java/org/tdf/rlp/RLPItem.java b/src/main/java/org/tdf/rlp/RLPItem.java index 3099c93..7114597 100644 --- a/src/main/java/org/tdf/rlp/RLPItem.java +++ b/src/main/java/org/tdf/rlp/RLPItem.java @@ -6,13 +6,13 @@ import java.util.Arrays; import static org.tdf.rlp.LazyByteArray.EMPTY; -import static org.tdf.rlp.RLPConstants.*; +import static org.tdf.rlp.RLPCodec.encodeBytes; /** * immutable rlp item */ public final class RLPItem implements RLPElement { - private static byte[] NULL_ENCODED = encodeElement(null); + private static byte[] NULL_ENCODED = encodeBytes(null); public static final RLPItem ONE = new RLPItem(new LazyByteArray(new byte[]{1})); private LazyByteArray data; @@ -126,23 +126,24 @@ public int asInt() { } public long asLong() { + if (isNull()) { + return 0; + } + if(this == ONE) return 1; if (longNumber != null) return longNumber; // numbers are ont starts with zero byte byte[] data = asBytes(); if (data.length > 0 && data[0] == 0) throw new RuntimeException("not a number"); - if (isNull()) { - longNumber = 0L; - return longNumber; - } if (data.length > Long.BYTES) throw new RuntimeException("not a number"); longNumber = ByteBuffer.wrap(concat(new byte[Long.BYTES - data.length], data)).getLong(); return longNumber; } public BigInteger asBigInteger() { + if (isNull()) return BigInteger.ZERO; + if(this == ONE) return BigInteger.ONE; byte[] data = asBytes(); if (data[0] == 0) throw new RuntimeException("not a number"); - if (isNull()) return BigInteger.ZERO; return new BigInteger(1, data); } @@ -157,55 +158,10 @@ public boolean asBoolean() { public byte[] getEncoded() { if (isNull()) return NULL_ENCODED; - if (encoded == null) encoded = new LazyByteArray(encodeElement(asBytes())); + if (encoded == null) encoded = new LazyByteArray(encodeBytes(asBytes())); return encoded.get(); } - public static byte[] encodeElement(byte[] srcData) { - // [0x80] - if (srcData == null || srcData.length == 0) { - return new byte[]{(byte) OFFSET_SHORT_ITEM}; - // [0x00] - } - if (srcData.length == 1 && (srcData[0] & 0xFF) < OFFSET_SHORT_ITEM) { - return srcData; - // [0x80, 0xb7], 0 - 55 bytes - } - if (srcData.length < SIZE_THRESHOLD) { - // length = 8X - byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length); - byte[] data = Arrays.copyOf(srcData, srcData.length + 1); - System.arraycopy(data, 0, data, 1, srcData.length); - data[0] = length; - - return data; - // [0xb8, 0xbf], 56+ bytes - } - // length of length = BX - // prefix = [BX, [length]] - int tmpLength = srcData.length; - byte lengthOfLength = 0; - while (tmpLength != 0) { - ++lengthOfLength; - tmpLength = tmpLength >> 8; - } - - // set length Of length at first byte - byte[] data = new byte[1 + lengthOfLength + srcData.length]; - data[0] = (byte) (OFFSET_LONG_ITEM + lengthOfLength); - - // copy length after first byte - tmpLength = srcData.length; - for (int i = lengthOfLength; i > 0; --i) { - data[i] = (byte) (tmpLength & 0xFF); - tmpLength = tmpLength >> 8; - } - - // at last copy the number bytes after its length - System.arraycopy(srcData, 0, data, 1 + lengthOfLength, srcData.length); - - return data; - } /** * Returns the values from each provided array combined into a single array. For example, {@code diff --git a/src/main/java/org/tdf/rlp/RLPList.java b/src/main/java/org/tdf/rlp/RLPList.java index 4a3ed9c..5648d19 100644 --- a/src/main/java/org/tdf/rlp/RLPList.java +++ b/src/main/java/org/tdf/rlp/RLPList.java @@ -1,23 +1,19 @@ package org.tdf.rlp; -import lombok.NonNull; - import java.math.BigInteger; import java.util.*; import java.util.function.UnaryOperator; import java.util.stream.Collectors; -import static org.tdf.rlp.RLPConstants.*; - public final class RLPList implements RLPElement, List { - static byte[] EMPTY_ENCODED_LIST = encodeList(new ArrayList<>()); + static byte[] EMPTY_ENCODED_LIST = RLPCodec.encodeElements(new ArrayList<>()); public static RLPList of(RLPElement... elements) { return new RLPList(Arrays.asList(elements)); } public static RLPList fromElements(Collection elements) { - return new RLPList(elements.stream().collect(Collectors.toList())); + return new RLPList(new ArrayList<>(elements)); } public static RLPList createEmpty() { @@ -67,7 +63,7 @@ public byte[] getEncoded() { if (size() == 0) return EMPTY_ENCODED_LIST; if (encoded != null) return encoded.get(); encoded = new LazyByteArray( - encodeList( + RLPCodec.encodeElements( stream().map(RLPElement::getEncoded) .collect(Collectors.toList()) ) @@ -80,46 +76,6 @@ public boolean isNull() { return false; } - public static byte[] encodeList(@NonNull Collection elements) { - int totalLength = 0; - for (byte[] element1 : elements) { - totalLength += element1.length; - } - - byte[] data; - int copyPos; - if (totalLength < SIZE_THRESHOLD) { - - data = new byte[1 + totalLength]; - data[0] = (byte) (OFFSET_SHORT_LIST + totalLength); - copyPos = 1; - } else { - // length of length = BX - // prefix = [BX, [length]] - int tmpLength = totalLength; - byte byteNum = 0; - while (tmpLength != 0) { - ++byteNum; - tmpLength = tmpLength >> 8; - } - tmpLength = totalLength; - byte[] lenBytes = new byte[byteNum]; - for (int i = 0; i < byteNum; ++i) { - lenBytes[byteNum - 1 - i] = (byte) ((tmpLength >> (8 * i)) & 0xFF); - } - // first byte = F7 + bytes.length - data = new byte[1 + lenBytes.length + totalLength]; - data[0] = (byte) (OFFSET_LONG_LIST + byteNum); - System.arraycopy(lenBytes, 0, data, 1, lenBytes.length); - - copyPos = lenBytes.length + 1; - } - for (byte[] element : elements) { - System.arraycopy(element, 0, data, copyPos, element.length); - copyPos += element.length; - } - return data; - } @Override public int size() { diff --git a/src/test/java/org/tdf/rlp/HashUtil.java b/src/test/java/org/tdf/rlp/HashUtil.java index 2c43158..9f6f3f2 100644 --- a/src/test/java/org/tdf/rlp/HashUtil.java +++ b/src/test/java/org/tdf/rlp/HashUtil.java @@ -5,6 +5,8 @@ import java.security.Provider; import java.security.Security; +import static org.tdf.rlp.RLPCodec.encodeBytes; + public class HashUtil { @@ -25,7 +27,7 @@ public class HashUtil { HASH_512_ALGORITHM_NAME = "ETH-KECCAK-512"; EMPTY_DATA_HASH = sha3(EMPTY_BYTE_ARRAY); EMPTY_LIST_HASH = sha3(RLPList.createEmpty().getEncoded()); - EMPTY_TRIE_HASH = sha3(RLPItem.encodeElement(EMPTY_BYTE_ARRAY)); + EMPTY_TRIE_HASH = sha3(encodeBytes(EMPTY_BYTE_ARRAY)); } /** diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index dfb75a8..244320f 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -17,7 +17,7 @@ import static org.junit.Assert.assertEquals; import static org.tdf.rlp.RLPCodec.*; import static org.tdf.rlp.RLPItem.NULL; -import static org.tdf.rlp.RLPItem.encodeElement; +import static org.tdf.rlp.RLPItem.ONE; @RunWith(JUnit4.class) public class RLPTest { @@ -320,7 +320,7 @@ public void test8() throws Exception { String expected = "b840" + byteArr; - assertEquals(expected, HexBytes.encode(encodeElement(byteArray))); + assertEquals(expected, HexBytes.encode(encodeBytes(byteArray))); assertEquals(expected, HexBytes.encode(RLPItem.fromBytes(byteArray).getEncoded())); assertEquals(expected, HexBytes.encode( RLPElement.fromEncoded(HexBytes.decode(expected)).getEncoded() @@ -337,26 +337,26 @@ public void test9() { @Test /** encode null value */ - public void testEncodeElementNull() { + public void testencodeBytesNull() { - byte[] actuals = encodeElement(null); + byte[] actuals = encodeBytes(null); assertArrayEquals(new byte[]{(byte) 0x80}, actuals); } @Test /** encode single byte 0x00 */ - public void testEncodeElementZero() { + public void testencodeBytesZero() { - byte[] actuals = encodeElement(new byte[]{0x00}); + byte[] actuals = encodeBytes(new byte[]{0x00}); assertArrayEquals(new byte[]{0x00}, actuals); } @Test /** encode single byte 0x01 */ - public void testEncodeElementOne() { + public void testencodeBytesOne() { - byte[] actuals = encodeElement(new byte[]{0x01}); + byte[] actuals = encodeBytes(new byte[]{0x01}); assertArrayEquals(new byte[]{(byte) 0x01}, actuals); } @@ -371,7 +371,7 @@ public void test10() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - prevHash = encodeElement(prevHash); + prevHash = encodeBytes(prevHash); /* 2 */ byte[] uncleList = HashUtil.sha3(RLPList.createEmpty().getEncoded()); @@ -381,9 +381,9 @@ public void test10() { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - coinbase = encodeElement(coinbase); + coinbase = encodeBytes(coinbase); - byte[] header = RLPList.encodeList( + byte[] header = encodeElements( Arrays.asList(prevHash, uncleList, coinbase)); assertEquals("f856a000000000000000000000000000000000000000000000000000000000000000001dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000", @@ -456,7 +456,7 @@ public void encodeEdgeShortList() throws Exception { byte[] rlpKeysList = HexBytes.decode("c0"); byte[] rlpValuesList = HexBytes.decode("c0"); byte[] rlpCode = HexBytes.decode("b4600160003556601359506301000000600035040f6018590060005660805460016080530160005760003560805760203560003557"); - byte[] output = RLPList.encodeList(Arrays.asList(rlpKeysList, rlpValuesList, rlpCode)); + byte[] output = encodeElements(Arrays.asList(rlpKeysList, rlpValuesList, rlpCode)); assertEquals(expectedOutput, HexBytes.encode(output)); assertArrayEquals(RLPElement.fromEncoded(HexBytes.decode(expectedOutput)).getEncoded(), HexBytes.decode(expectedOutput)); @@ -1299,4 +1299,23 @@ public void testBooleanFailed2(){ list, Boolean.class ); } + + @Test + public void test(){ + assert !NULL.as(boolean.class); + assert ONE.as(boolean.class); + assert NULL.asBigInteger().compareTo(BigInteger.ZERO) == 0; + assert RLPItem.fromBoolean(true) == ONE; + assert RLPItem.fromLong(1) == ONE; + assert RLPItem.fromInt(0) == NULL; + assert !NULL.isRLPList(); + assert NULL.isRLPItem(); + assert (NULL.getEncoded()[0] & 0xff) == RLPConstants.OFFSET_SHORT_ITEM; + assert RLPItem.fromInt(2).asBigInteger().compareTo(BigInteger.valueOf(2)) == 0; + } + + @Test(expected = RuntimeException.class) + public void test2(){ + NULL.asRLPList(); + } } From 531c63e215703e68b58c8162748f7c9e2180738e Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Fri, 13 Dec 2019 07:14:56 +0800 Subject: [PATCH 11/25] reduce memory usage by caching --- src/main/java/org/tdf/rlp/RLPItem.java | 10 +++++++--- src/test/java/org/tdf/rlp/Main2.java | 21 +++++++++++++++++++++ src/test/java/org/tdf/rlp/RLPTest.java | 6 ++---- 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 src/test/java/org/tdf/rlp/Main2.java diff --git a/src/main/java/org/tdf/rlp/RLPItem.java b/src/main/java/org/tdf/rlp/RLPItem.java index 7114597..60decac 100644 --- a/src/main/java/org/tdf/rlp/RLPItem.java +++ b/src/main/java/org/tdf/rlp/RLPItem.java @@ -57,17 +57,21 @@ public static RLPItem fromLong(long l) { } public static RLPItem fromString(String s) { + if(s == null) return NULL; return fromBytes(s.getBytes(StandardCharsets.UTF_8)); } public static RLPItem fromBytes(byte[] data) { if (data == null || data.length == 0) return NULL; + if(data.length == 1 && (Byte.toUnsignedInt(data[0]) == 1)) return ONE; return new RLPItem(new LazyByteArray(data)); } public static RLPItem fromBigInteger(BigInteger bigInteger) { + if (bigInteger == null || bigInteger.equals(BigInteger.ZERO)) return NULL; + if (bigInteger.equals(BigInteger.ONE)) return ONE; if (bigInteger.compareTo(BigInteger.ZERO) < 0) throw new RuntimeException("negative numbers are not allowed"); - if (bigInteger.equals(BigInteger.ZERO)) return NULL; + return fromBytes(asUnsignedByteArray(bigInteger)); } @@ -129,7 +133,7 @@ public long asLong() { if (isNull()) { return 0; } - if(this == ONE) return 1; + if (this == ONE) return 1; if (longNumber != null) return longNumber; // numbers are ont starts with zero byte byte[] data = asBytes(); @@ -141,7 +145,7 @@ public long asLong() { public BigInteger asBigInteger() { if (isNull()) return BigInteger.ZERO; - if(this == ONE) return BigInteger.ONE; + if (this == ONE) return BigInteger.ONE; byte[] data = asBytes(); if (data[0] == 0) throw new RuntimeException("not a number"); return new BigInteger(1, data); diff --git a/src/test/java/org/tdf/rlp/Main2.java b/src/test/java/org/tdf/rlp/Main2.java new file mode 100644 index 0000000..0d3fa44 --- /dev/null +++ b/src/test/java/org/tdf/rlp/Main2.java @@ -0,0 +1,21 @@ +package org.tdf.rlp; + +public class Main2 { + public static void main(String[] args) { + int n = 1000000; + RLPList list = RLPList.createEmpty(n); + byte[] bytes = new byte[]{1}; + for (int i = 0; i < n; i++) { + list.add(RLPItem.fromBytes(bytes)); + } + long start = System.currentTimeMillis(); + byte[] encoded = list.getEncoded(); + long end = System.currentTimeMillis(); + System.out.println("encode " + (n * 32) + " bytes in " + (end - start) + " ms"); + + start = System.currentTimeMillis(); + RLPElement decoded = RLPElement.fromEncoded(encoded); + end = System.currentTimeMillis(); + System.out.println("decode " + (n * 32) + " bytes in " + (end - start) + " ms"); + } +} diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 244320f..d43a327 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -1182,18 +1182,16 @@ public void testItemCache() { assertArrayEquals(encoded2, encoded3); } - @Ignore @Test // ethereumJ: encode 320000 bytes in 127 ms // ethereumJ: decode 320000 bytes in 159 ms // our: encode 320000 bytes in 109 ms // our: decode 320000 bytes in 4 ms public void testBomb() { - int n = 10000; - SecureRandom sr = new SecureRandom(); + int n = 1000000; RLPList list = RLPList.createEmpty(n); + byte[] bytes = new byte[]{1}; for (int i = 0; i < n; i++) { - byte[] bytes = new byte[10000]; list.add(RLPItem.fromBytes(bytes)); } long start = System.currentTimeMillis(); From a1abf47b8f1348648ea68ab311727e330bd65590 Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Fri, 13 Dec 2019 07:42:13 +0800 Subject: [PATCH 12/25] Update RLPTest.java --- src/test/java/org/tdf/rlp/RLPTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index d43a327..8715ae7 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -1197,12 +1197,12 @@ public void testBomb() { long start = System.currentTimeMillis(); byte[] encoded = list.getEncoded(); long end = System.currentTimeMillis(); - System.out.println("encode " + (n * 32) + " bytes in " + (end - start) + " ms"); + System.out.println("encode " + (n) + " bytes in " + (end - start) + " ms"); start = System.currentTimeMillis(); RLPElement decoded = RLPElement.fromEncoded(encoded); end = System.currentTimeMillis(); - System.out.println("decode " + (n * 32) + " bytes in " + (end - start) + " ms"); + System.out.println("decode " + (n) + " bytes in " + (end - start) + " ms"); } private static class Nested{ From ca23b6dce1b07743c9aae1a723e104c36b0836bf Mon Sep 17 00:00:00 2001 From: Sal Date: Fri, 13 Dec 2019 10:10:49 +0800 Subject: [PATCH 13/25] parse rlp elements lazy --- build.gradle | 2 +- src/main/java/org/tdf/rlp/LazyElement.java | 126 +++++++++++++++++++++ src/main/java/org/tdf/rlp/RLPElement.java | 12 +- src/main/java/org/tdf/rlp/RLPItem.java | 15 +++ src/main/java/org/tdf/rlp/RLPParser.java | 27 +++-- src/test/java/org/tdf/rlp/RLPTest.java | 15 ++- 6 files changed, 183 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/tdf/rlp/LazyElement.java diff --git a/build.gradle b/build.gradle index 4ae9b09..3f9de7e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'org.tdf' -version '1.1.3' +version '1.1.4' sourceCompatibility = 1.8 diff --git a/src/main/java/org/tdf/rlp/LazyElement.java b/src/main/java/org/tdf/rlp/LazyElement.java new file mode 100644 index 0000000..6f19af6 --- /dev/null +++ b/src/main/java/org/tdf/rlp/LazyElement.java @@ -0,0 +1,126 @@ +package org.tdf.rlp; + +import java.math.BigInteger; + +public class LazyElement implements RLPElement{ + private RLPElement delegate; + + private RLPParser parser; + + public LazyElement(RLPParser parser) { + this.parser = parser; + } + + private void parse(){ + if (delegate != null) return; + delegate = parser.readElement(); + // release gc + parser = null; + } + + @Override + public boolean isRLPList() { + return parser.peekIsList(); + } + + @Override + public boolean isRLPItem() { + return !isRLPList(); + } + + @Override + public RLPList asRLPList() { + parse(); + return delegate.asRLPList(); + } + + @Override + public RLPItem asRLPItem() { + parse(); + return delegate.asRLPItem(); + } + + @Override + public boolean isNull() { + parse(); + return delegate.isNull(); + } + + @Override + public byte[] getEncoded() { + parse(); + return delegate.getEncoded(); + } + + @Override + public byte[] asBytes() { + parse(); + return delegate.asBytes(); + } + + @Override + public byte asByte() { + parse(); + return delegate.asByte(); + } + + @Override + public short asShort() { + parse(); + return delegate.asShort(); + } + + @Override + public int asInt() { + parse(); + return delegate.asInt(); + } + + @Override + public long asLong() { + parse(); + return delegate.asLong(); + } + + @Override + public BigInteger asBigInteger() { + parse(); + return delegate.asBigInteger(); + } + + @Override + public String asString() { + parse(); + return delegate.asString(); + } + + @Override + public boolean asBoolean() { + parse(); + return delegate.asBoolean(); + } + + @Override + public T as(Class clazz) { + parse(); + return delegate.as(clazz); + } + + @Override + public RLPElement get(int index) { + parse(); + return delegate.get(index); + } + + @Override + public boolean add(RLPElement element) { + parse(); + return delegate.add(element); + } + + @Override + public RLPElement set(int index, RLPElement element) { + parse(); + return delegate.set(index, element); + } +} diff --git a/src/main/java/org/tdf/rlp/RLPElement.java b/src/main/java/org/tdf/rlp/RLPElement.java index c8f9ba8..60f7663 100644 --- a/src/main/java/org/tdf/rlp/RLPElement.java +++ b/src/main/java/org/tdf/rlp/RLPElement.java @@ -42,6 +42,12 @@ public interface RLPElement { long asLong(); + RLPElement get(int index); + + boolean add(RLPElement element); + + RLPElement set(int index, RLPElement element); + BigInteger asBigInteger(); String asString(); @@ -53,7 +59,11 @@ default T as(Class clazz){ } static RLPElement fromEncoded(byte[] data) { - return RLPParser.fromEncoded(data); + return fromEncoded(data, true); + } + + static RLPElement fromEncoded(byte[] data, boolean lazy) { + return RLPParser.fromEncoded(data, lazy); } // convert any object as a rlp tree diff --git a/src/main/java/org/tdf/rlp/RLPItem.java b/src/main/java/org/tdf/rlp/RLPItem.java index 60decac..26c98b8 100644 --- a/src/main/java/org/tdf/rlp/RLPItem.java +++ b/src/main/java/org/tdf/rlp/RLPItem.java @@ -192,4 +192,19 @@ private static byte[] concat(byte[]... arrays) { public boolean isRLPItem() { return true; } + + @Override + public RLPElement get(int index) { + throw new RuntimeException("not a rlp list"); + } + + @Override + public boolean add(RLPElement element) { + throw new RuntimeException("not a rlp list"); + } + + @Override + public RLPElement set(int index, RLPElement element) { + throw new RuntimeException("not a rlp list"); + } } diff --git a/src/main/java/org/tdf/rlp/RLPParser.java b/src/main/java/org/tdf/rlp/RLPParser.java index 4f9d7ee..7e06efb 100644 --- a/src/main/java/org/tdf/rlp/RLPParser.java +++ b/src/main/java/org/tdf/rlp/RLPParser.java @@ -21,12 +21,12 @@ private static int byteArrayToInt(byte[] b) { private int limit; - static RLPElement fromEncoded(@NonNull byte[] data) { + static RLPElement fromEncoded(@NonNull byte[] data, boolean lazy) { RLPParser parser = new RLPParser(data); if (parser.estimateSize() != data.length) { throw new RuntimeException("invalid encoding"); } - return parser.readElement(); + return lazy ? parser.readLazy() : parser.readElement(); } private RLPParser(byte[] data) { @@ -58,13 +58,13 @@ private int estimateSize() { if (prefix < OFFSET_SHORT_LIST) { // skip return byteArrayToInt( - Arrays.copyOfRange(raw, 1, 1 + prefix - OFFSET_LONG_ITEM) + Arrays.copyOfRange(raw, offset + 1, offset + 1 + prefix - OFFSET_LONG_ITEM) ) + 1 + prefix - OFFSET_LONG_ITEM; } if (prefix <= OFFSET_LONG_LIST) { return prefix - OFFSET_SHORT_LIST + 1; } - return byteArrayToInt(Arrays.copyOfRange(raw, 1, 1 + prefix - OFFSET_LONG_LIST)) + 1 + prefix - OFFSET_LONG_LIST; + return byteArrayToInt(Arrays.copyOfRange(raw, offset + 1, offset + 1 + prefix - OFFSET_LONG_LIST)) + 1 + prefix - OFFSET_LONG_LIST; } private int read() { @@ -88,11 +88,11 @@ private int peek() { } - private boolean peekIsList() { + boolean peekIsList() { return peek() >= OFFSET_SHORT_LIST; } - private RLPList readList() { + private RLPList readList(boolean lazy) { int offset = this.offset; int prefix = read(); RLPList list = RLPList.createEmpty(); @@ -109,18 +109,27 @@ private RLPList readList() { } int limit = parser.limit; while (parser.hasRemaining()) { - list.add(parser.readElement()); + list.add(lazy ? parser.readLazyElement() : parser.readElement()); } list.setEncoded(new LazyByteArray(raw, offset, limit)); return list; } - private RLPElement readElement() { - if (peekIsList()) return readList(); + RLPElement readElement() { + if (peekIsList()) return readList(false); return readItem(); } + RLPElement readLazy() { + if (peekIsList()) return readList(true); + return readItem(); + } + + LazyElement readLazyElement() { + return new LazyElement(readAsParser(estimateSize())); + } + private RLPItem readItem() { int initOffset = this.offset; int prefix = read(); diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 8715ae7..e37fc2d 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -431,13 +431,13 @@ public void performanceDecode() throws Exception { long start1 = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - list = RLPElement.fromEncoded(payload).asRLPList(); + list = RLPElement.fromEncoded(payload, false).asRLPList(); } long end1 = System.currentTimeMillis(); long start2 = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - list = RLPElement.fromEncoded(payload).asRLPList(); + list = RLPElement.fromEncoded(payload, false).asRLPList(); } long end2 = System.currentTimeMillis(); @@ -1200,7 +1200,7 @@ public void testBomb() { System.out.println("encode " + (n) + " bytes in " + (end - start) + " ms"); start = System.currentTimeMillis(); - RLPElement decoded = RLPElement.fromEncoded(encoded); + RLPElement decoded = RLPElement.fromEncoded(encoded, false); end = System.currentTimeMillis(); System.out.println("decode " + (n) + " bytes in " + (end - start) + " ms"); } @@ -1316,4 +1316,13 @@ public void test(){ public void test2(){ NULL.asRLPList(); } + + @Test + public void testLazyParse() throws Exception{ + String expected = "c88363617483646f67"; + + RLPElement el = RLPElement.fromEncoded(Hex.decodeHex(expected)).asRLPList(); + assert el.asRLPList().stream().allMatch(x -> x instanceof LazyElement); + el.get(0).asString(); + } } From 8a036dfca45d28a7cf18aad961eb89d8ba96c4d7 Mon Sep 17 00:00:00 2001 From: Sal Date: Fri, 13 Dec 2019 10:17:57 +0800 Subject: [PATCH 14/25] Update RLPTest.java --- src/test/java/org/tdf/rlp/RLPTest.java | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index e37fc2d..092492c 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -437,12 +437,12 @@ public void performanceDecode() throws Exception { long start2 = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - list = RLPElement.fromEncoded(payload, false).asRLPList(); + list = RLPElement.fromEncoded(payload, true).asRLPList(); } long end2 = System.currentTimeMillis(); - System.out.println("Result RLPElement.fromEncoded\t: " + (end1 - start1) + "ms and\t " + " bytes for each resulting object list"); - System.out.println("Result RLPElement.fromEncoded\t: " + (end2 - start2) + "ms and\t " + " bytes for each resulting object list"); + System.out.println("Result RLPElement.fromEncoded\t: " + (end1 - start1) + "ms and\t " + (payload.length * ITERATIONS) + " bytes for each resulting object list"); + System.out.println("Result RLPElement.fromEncoded lazy\t: " + (end2 - start2) + "ms and\t " + (payload.length * ITERATIONS) + " bytes for each resulting object list"); } else { System.out.println("Performance test for RLP.decode() disabled"); } @@ -1205,7 +1205,7 @@ public void testBomb() { System.out.println("decode " + (n) + " bytes in " + (end - start) + " ms"); } - private static class Nested{ + private static class Nested { @RLP private List>> nested; @@ -1216,7 +1216,7 @@ public Nested() { } @Test - public void testNested() throws Exception{ + public void testNested() throws Exception { RLPUtils.Resolved resolved = RLPUtils.resolveFieldType(Nested.class.getDeclaredField("nested")); assert resolved.level == 3; assert resolved.type == String.class; @@ -1228,7 +1228,7 @@ public void testNested() throws Exception{ assert resolved.type == String.class; } - private static class NoNested{ + private static class NoNested { @RLP private List nested; @@ -1237,7 +1237,7 @@ public NoNested() { } @Test - public void testDecode2(){ + public void testDecode2() { NoNested nested = new NoNested(); nested.nested = new ArrayList<>(); @@ -1249,7 +1249,7 @@ public void testDecode2(){ } @Test - public void testDecode3(){ + public void testDecode3() { Nested nested = new Nested(); nested.nested = new ArrayList<>(); nested.nested.add(new ArrayList<>()); @@ -1262,7 +1262,7 @@ public void testDecode3(){ } @Test - public void testNestedString(){ + public void testNestedString() { RLPList li1 = RLPList.of(RLPItem.fromString("aa"), RLPItem.fromString("bbb")); RLPList li2 = RLPList.of(RLPItem.fromString("aa"), RLPItem.fromString("bbb")); byte[] encoded = RLPList.of(li1, li2).getEncoded(); @@ -1274,7 +1274,7 @@ public void testNestedString(){ } @Test - public void testBoolean(){ + public void testBoolean() { assert !RLPCodec.decode(NULL, Boolean.class); assert RLPCodec.decode(RLPItem.fromBoolean(true), Boolean.class); List elements = Stream.of(1, 1, 1).map(RLPItem::fromInt).collect(Collectors.toList()); @@ -1285,12 +1285,12 @@ public void testBoolean(){ } @Test(expected = RuntimeException.class) - public void testBooleanFailed(){ + public void testBooleanFailed() { RLPCodec.decode(RLPItem.fromInt(2), Boolean.class); } @Test(expected = RuntimeException.class) - public void testBooleanFailed2(){ + public void testBooleanFailed2() { List elements = Stream.of(1, 2, 3).map(RLPItem::fromInt).collect(Collectors.toList()); RLPList list = RLPList.fromElements(elements); RLPCodec.decodeList( @@ -1299,7 +1299,7 @@ public void testBooleanFailed2(){ } @Test - public void test(){ + public void test() { assert !NULL.as(boolean.class); assert ONE.as(boolean.class); assert NULL.asBigInteger().compareTo(BigInteger.ZERO) == 0; @@ -1313,12 +1313,12 @@ public void test(){ } @Test(expected = RuntimeException.class) - public void test2(){ + public void test2() { NULL.asRLPList(); } @Test - public void testLazyParse() throws Exception{ + public void testLazyParse() throws Exception { String expected = "c88363617483646f67"; RLPElement el = RLPElement.fromEncoded(Hex.decodeHex(expected)).asRLPList(); From 892ede59f0162663b81745102512662d149491a6 Mon Sep 17 00:00:00 2001 From: Sal Date: Fri, 13 Dec 2019 11:39:14 +0800 Subject: [PATCH 15/25] add size method --- build.gradle | 2 +- src/main/java/org/tdf/rlp/LazyElement.java | 6 ++++++ src/main/java/org/tdf/rlp/RLPElement.java | 2 ++ src/main/java/org/tdf/rlp/RLPItem.java | 5 +++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3f9de7e..1bf9a1b 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'org.tdf' -version '1.1.4' +version '1.1.5' sourceCompatibility = 1.8 diff --git a/src/main/java/org/tdf/rlp/LazyElement.java b/src/main/java/org/tdf/rlp/LazyElement.java index 6f19af6..87ca165 100644 --- a/src/main/java/org/tdf/rlp/LazyElement.java +++ b/src/main/java/org/tdf/rlp/LazyElement.java @@ -123,4 +123,10 @@ public RLPElement set(int index, RLPElement element) { parse(); return delegate.set(index, element); } + + @Override + public int size() { + parse(); + return delegate.size(); + } } diff --git a/src/main/java/org/tdf/rlp/RLPElement.java b/src/main/java/org/tdf/rlp/RLPElement.java index 60f7663..2f73bd5 100644 --- a/src/main/java/org/tdf/rlp/RLPElement.java +++ b/src/main/java/org/tdf/rlp/RLPElement.java @@ -42,6 +42,8 @@ public interface RLPElement { long asLong(); + int size(); + RLPElement get(int index); boolean add(RLPElement element); diff --git a/src/main/java/org/tdf/rlp/RLPItem.java b/src/main/java/org/tdf/rlp/RLPItem.java index 26c98b8..d969912 100644 --- a/src/main/java/org/tdf/rlp/RLPItem.java +++ b/src/main/java/org/tdf/rlp/RLPItem.java @@ -207,4 +207,9 @@ public boolean add(RLPElement element) { public RLPElement set(int index, RLPElement element) { throw new RuntimeException("not a rlp list"); } + + @Override + public int size() { + throw new RuntimeException("not a rlp list"); + } } From 124ecc89ae0ba1562bc7baf90dc20a17d423d2bf Mon Sep 17 00:00:00 2001 From: Sal Date: Fri, 13 Dec 2019 11:50:28 +0800 Subject: [PATCH 16/25] update readme --- README.md | 24 +++++++++--------------- src/test/java/org/tdf/rlp/Main.java | 7 ++----- src/test/java/org/tdf/rlp/Node.java | 27 +++++++-------------------- 3 files changed, 18 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 1843ebe..4607707 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import static org.tdf.rlp.RLPCodec.decode; -import static org.tdf.rlp.RLPCodec.encode; - public class Node{ // RLP annotation specify the order of field in encoded list @RLP(0) @@ -59,15 +56,15 @@ public class Node{ root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); // encode to byte array - byte[] encoded = encode(root); - // encode to rlp element + byte[] encoded = RLPCodec.encode(root); + // read as rlp tree RLPElement el = RLPElement.readRLPTree(root); // decode from byte array - Node root2 = decode(encoded, Node.class); - assertTrue(root2.children.get(0).children.get(0).name.equals("4")); - assertTrue(root2.children.get(0).children.get(1).name.equals("5")); - assertTrue(root2.children.get(1).children.get(0).name.equals("6")); - assertTrue(root2.children.get(1).children.get(1).name.equals("7")); + Node root2 = RLPCodec.decode(encoded, Node.class); + el = RLPElement.fromEncoded(encoded); + // decode from rlp element + root2 = el.as(Node.class); + root2 = RLPCodec.decode(el, Node.class); } public static void assertTrue(boolean b){ @@ -84,13 +81,10 @@ package org.tdf.rlp; import java.util.HashMap; import java.util.Map; -import static org.tdf.rlp.RLPCodec.encode; - public class Main{ public static class MapEncoderDecoder implements RLPEncoder>, RLPDecoder> { @Override - public Map decode(RLPElement element) { - RLPList list = element.asRLPList(); + public Map decode(RLPElement list) { Map map = new HashMap<>(list.size() / 2); for (int i = 0; i < list.size(); i += 2) { map.put(list.get(i).asString(), list.get(i+1).asString()); @@ -127,7 +121,7 @@ public class Main{ Map m = new HashMap<>(); m.put("a", "1"); m.put("b", "2"); - byte[] encoded = encode(new MapWrapper(m)); + byte[] encoded = RLPCodec.encode(new MapWrapper(m)); MapWrapper decoded = RLPCodec.decode(encoded, MapWrapper.class); assertTrue(decoded.map.get("a").equals("1")); } diff --git a/src/test/java/org/tdf/rlp/Main.java b/src/test/java/org/tdf/rlp/Main.java index 00f9469..68163c2 100644 --- a/src/test/java/org/tdf/rlp/Main.java +++ b/src/test/java/org/tdf/rlp/Main.java @@ -3,13 +3,10 @@ import java.util.HashMap; import java.util.Map; -import static org.tdf.rlp.RLPCodec.encode; - public class Main{ public static class MapEncoderDecoder implements RLPEncoder>, RLPDecoder> { @Override - public Map decode(RLPElement element) { - RLPList list = element.asRLPList(); + public Map decode(RLPElement list) { Map map = new HashMap<>(list.size() / 2); for (int i = 0; i < list.size(); i += 2) { map.put(list.get(i).asString(), list.get(i+1).asString()); @@ -46,7 +43,7 @@ public static void main(String[] args){ Map m = new HashMap<>(); m.put("a", "1"); m.put("b", "2"); - byte[] encoded = encode(new MapWrapper(m)); + byte[] encoded = RLPCodec.encode(new MapWrapper(m)); MapWrapper decoded = RLPCodec.decode(encoded, MapWrapper.class); assertTrue(decoded.map.get("a").equals("1")); } diff --git a/src/test/java/org/tdf/rlp/Node.java b/src/test/java/org/tdf/rlp/Node.java index 27f9e86..fb76ff9 100644 --- a/src/test/java/org/tdf/rlp/Node.java +++ b/src/test/java/org/tdf/rlp/Node.java @@ -5,9 +5,6 @@ import java.util.Collection; import java.util.List; -import static org.tdf.rlp.RLPCodec.decode; -import static org.tdf.rlp.RLPCodec.encode; - public class Node{ // RLP annotation specify the order of field in encoded list @RLP(0) @@ -50,25 +47,15 @@ public static void main(String[] args){ root.children.get(1).addChildren(Arrays.asList(new Node("6"), new Node("7"))); // encode to byte array - byte[] encoded = encode(root); - // encode to rlp element + byte[] encoded = RLPCodec.encode(root); + // read as rlp tree RLPElement el = RLPElement.readRLPTree(root); // decode from byte array - Node root2 = decode(encoded, Node.class); - assertTrue(root2.children.get(0).children.get(0).name.equals("4")); - assertTrue(root2.children.get(0).children.get(1).name.equals("5")); - assertTrue(root2.children.get(1).children.get(0).name.equals("6")); - assertTrue(root2.children.get(1).children.get(1).name.equals("7")); - - Nested nested = new Nested(); - nested.nested = new ArrayList<>(); - nested.nested.add(new ArrayList<>()); - nested.nested.get(0).add(new ArrayList<>()); - nested.nested.get(0).get(0).addAll(Arrays.asList("aaa", "bbb")); - encoded = encode(nested); - nested = decode(encoded, Nested.class); - assertTrue(nested.nested.get(0).get(0).get(0).equals("aaa")); - assertTrue(nested.nested.get(0).get(0).get(1).equals("bbb")); + Node root2 = RLPCodec.decode(encoded, Node.class); + el = RLPElement.fromEncoded(encoded); + // decode from rlp element + root2 = el.as(Node.class); + root2 = RLPCodec.decode(el, Node.class); } public static void assertTrue(boolean b){ From 49542af4795dc135473b170800bda469581721f3 Mon Sep 17 00:00:00 2001 From: Sal Date: Fri, 13 Dec 2019 17:29:28 +0800 Subject: [PATCH 17/25] Update RLPTest.java --- src/test/java/org/tdf/rlp/RLPTest.java | 42 ++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 092492c..030d9ac 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -1,14 +1,12 @@ package org.tdf.rlp; import org.apache.commons.codec.binary.Hex; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -1325,4 +1323,44 @@ public void testLazyParse() throws Exception { assert el.asRLPList().stream().allMatch(x -> x instanceof LazyElement); el.get(0).asString(); } + + @Test(expected = RuntimeException.class) + public void testByteOverFlow(){ + RLPItem.fromLong(0xffL + 1).asByte(); + } + + @Test(expected = RuntimeException.class) + public void testShortOverFlow(){ + RLPItem.fromLong(0xffffL + 1).asByte(); + } + + @Test(expected = RuntimeException.class) + public void testIntOverFlow(){ + RLPItem.fromLong(0xffffffffL + 1).asByte(); + } + + @Test(expected = RuntimeException.class) + public void testItemAsList1(){ + NULL.get(0); + } + + @Test(expected = RuntimeException.class) + public void testItemAsList2(){ + NULL.add(NULL); + } + + @Test(expected = RuntimeException.class) + public void testItemAsList3(){ + NULL.set(0, NULL); + } + + @Test(expected = RuntimeException.class) + public void testItemAsList4(){ + NULL.size(); + } + + @Test + public void testAsByteSuccess(){ + assert RLPItem.fromLong(0xffL).asByte() == (byte) 0xff; + } } From 8e91a814b19a6869045570d932e17b30aff0c5a1 Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Fri, 13 Dec 2019 21:15:31 +0800 Subject: [PATCH 18/25] support encode set with contentorder and map with key order --- src/main/java/org/tdf/rlp/RLPCodec.java | 27 +++++----- src/main/java/org/tdf/rlp/RLPDecoding.java | 3 ++ src/main/java/org/tdf/rlp/RLPElement.java | 35 +++++++++++-- src/main/java/org/tdf/rlp/RLPEncoding.java | 12 +++++ src/main/java/org/tdf/rlp/RLPUtils.java | 32 ++++++++++-- src/test/java/org/tdf/rlp/RLPTest.java | 61 +++++++++++++++++++--- 6 files changed, 140 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/tdf/rlp/RLPCodec.java b/src/main/java/org/tdf/rlp/RLPCodec.java index aa6c89c..9188c8d 100644 --- a/src/main/java/org/tdf/rlp/RLPCodec.java +++ b/src/main/java/org/tdf/rlp/RLPCodec.java @@ -3,12 +3,10 @@ import lombok.NonNull; import java.lang.reflect.Array; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; import static org.tdf.rlp.RLPConstants.*; @@ -93,17 +91,16 @@ public static T decode(RLPElement element, Class clazz) { if (clazz == List.class) { return (T) element.asRLPList(); } - Object o; - try{ - clazz.getConstructor(); - }catch (Exception e){ - throw new RuntimeException(clazz + " should has a no arguments constructor"); - } + T o; + try { - o = clazz.newInstance(); + Constructor con = clazz.getDeclaredConstructor(); + con.setAccessible(true); + o = con.newInstance(); } catch (Exception e) { - throw new RuntimeException(e); + throw new RuntimeException(clazz + " should has a no arguments constructor"); } + List fields = RLPUtils.getRLPFields(clazz); if (fields.size() == 0) throw new RuntimeException(clazz + " is not supported not RLP annotation found"); for (int i = 0; i < fields.size(); i++) { @@ -139,7 +136,7 @@ public static T decode(RLPElement element, Class clazz) { throw new RuntimeException(e); } } - return (T) o; + return o; } // rlp primitives encoding/decoding @@ -278,4 +275,8 @@ public static byte[] encodeElements(@NonNull Collection elements) { } return data; } + + static RLPElement encodeCollection(Collection col, Comparator ordering){ + return RLPElement.readRLPTree(col.stream().sorted(ordering).collect(Collectors.toList())); + } } diff --git a/src/main/java/org/tdf/rlp/RLPDecoding.java b/src/main/java/org/tdf/rlp/RLPDecoding.java index e63b252..5261c4a 100644 --- a/src/main/java/org/tdf/rlp/RLPDecoding.java +++ b/src/main/java/org/tdf/rlp/RLPDecoding.java @@ -4,9 +4,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.*; @Target({ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RLPDecoding { Class value() default RLPDecoder.None.class; + Class as() default Void.class; + } diff --git a/src/main/java/org/tdf/rlp/RLPElement.java b/src/main/java/org/tdf/rlp/RLPElement.java index 2f73bd5..28b2c9b 100644 --- a/src/main/java/org/tdf/rlp/RLPElement.java +++ b/src/main/java/org/tdf/rlp/RLPElement.java @@ -3,9 +3,9 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.math.BigInteger; -import java.util.Collection; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.tdf.rlp.RLPItem.NULL; import static org.tdf.rlp.RLPItem.ONE; @@ -56,7 +56,7 @@ public interface RLPElement { boolean asBoolean(); - default T as(Class clazz){ + default T as(Class clazz) { return RLPCodec.decode(this, clazz); } @@ -71,7 +71,7 @@ static RLPElement fromEncoded(byte[] data, boolean lazy) { // convert any object as a rlp tree static RLPElement readRLPTree(Object t) { if (t == null) return NULL; - if(t instanceof Boolean || t.getClass() == boolean.class){ + if (t instanceof Boolean || t.getClass() == boolean.class) { return ((Boolean) t) ? ONE : NULL; } if (t instanceof RLPElement) return (RLPElement) t; @@ -111,7 +111,8 @@ static RLPElement readRLPTree(Object t) { } // peek fields reflection List fields = RLPUtils.getRLPFields(t.getClass()); - if (fields.size() == 0) throw new RuntimeException(t.getClass() + " is not supported, no @RLP annotation found"); + if (fields.size() == 0) + throw new RuntimeException(t.getClass() + " is not supported, no @RLP annotation found"); return new RLPList(fields.stream().map(f -> { f.setAccessible(true); RLPEncoder fieldEncoder = RLPUtils.getAnnotatedRLPEncoder(f); @@ -122,6 +123,30 @@ static RLPElement readRLPTree(Object t) { throw new RuntimeException(e); } } + Comparator comparator = RLPUtils.getContentOrdering(f); + if (Collection.class.isAssignableFrom(f.getType()) && comparator != null) { + try { + return RLPCodec.encodeCollection((Collection) f.get(t), comparator); + } catch (Exception e) { + throw new RuntimeException("get field " + f + " failed " + e.getCause()); + } + } + comparator = RLPUtils.getKeyOrdering(f); + if(Map.class.isAssignableFrom(f.getType())){ + try{ + Map m = (Map) f.get(t); + RLPList list = RLPList.createEmpty(m.size() * 2); + Stream s = m.keySet().stream(); + if(comparator != null) s = s.sorted(comparator); + s.forEach(x -> { + list.add(readRLPTree(x)); + list.add(readRLPTree(m.get(x))); + }); + return list; + }catch (Exception e){ + throw new RuntimeException(e); + } + } try { return readRLPTree(f.get(t)); } catch (Exception e) { diff --git a/src/main/java/org/tdf/rlp/RLPEncoding.java b/src/main/java/org/tdf/rlp/RLPEncoding.java index ab9fcd1..3cbde93 100644 --- a/src/main/java/org/tdf/rlp/RLPEncoding.java +++ b/src/main/java/org/tdf/rlp/RLPEncoding.java @@ -4,9 +4,21 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.*; @Target({java.lang.annotation.ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RLPEncoding { Class value() default RLPEncoder.None.class; + + // content ordering of set + Class contentOrdering() default None.class; + Class keyOrdering() default None.class; + + class None implements Comparator{ + @Override + public int compare(Object o1, Object o2) { + return 0; + } + } } diff --git a/src/main/java/org/tdf/rlp/RLPUtils.java b/src/main/java/org/tdf/rlp/RLPUtils.java index 725a488..b45ea71 100644 --- a/src/main/java/org/tdf/rlp/RLPUtils.java +++ b/src/main/java/org/tdf/rlp/RLPUtils.java @@ -1,9 +1,6 @@ package org.tdf.rlp; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; +import java.lang.reflect.*; import java.util.Arrays; import java.util.Comparator; import java.util.List; @@ -51,6 +48,33 @@ static List getRLPFields(Class clazz) { return fields; } + static Comparator getContentOrdering(AnnotatedElement element){ + if (!element.isAnnotationPresent(RLPEncoding.class)) { + return null; + } + Class clazz = element.getAnnotation(RLPEncoding.class).contentOrdering(); + if(clazz == RLPEncoding.None.class) return null; + try{ + Constructor con = clazz.getDeclaredConstructor(); + con.setAccessible(true); + return con.newInstance(); + }catch (Exception e){ + throw new RuntimeException("new instance of " + clazz + " failed " + e.getMessage()); + } + } + + static Comparator getKeyOrdering(AnnotatedElement element){ + if (!element.isAnnotationPresent(RLPEncoding.class)) { + return null; + } + Class clazz = element.getAnnotation(RLPEncoding.class).keyOrdering(); + if(clazz == RLPEncoding.None.class) return null; + try{ + return clazz.newInstance(); + }catch (Exception e){ + throw new RuntimeException("new instance of " + clazz + " failed " + e.getCause()); + } + } static Resolved resolveFieldType(Field f) { if (f.getType() != List.class) { diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 030d9ac..5d90f1a 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -1325,42 +1325,87 @@ public void testLazyParse() throws Exception { } @Test(expected = RuntimeException.class) - public void testByteOverFlow(){ + public void testByteOverFlow() { RLPItem.fromLong(0xffL + 1).asByte(); } @Test(expected = RuntimeException.class) - public void testShortOverFlow(){ + public void testShortOverFlow() { RLPItem.fromLong(0xffffL + 1).asByte(); } @Test(expected = RuntimeException.class) - public void testIntOverFlow(){ + public void testIntOverFlow() { RLPItem.fromLong(0xffffffffL + 1).asByte(); } @Test(expected = RuntimeException.class) - public void testItemAsList1(){ + public void testItemAsList1() { NULL.get(0); } @Test(expected = RuntimeException.class) - public void testItemAsList2(){ + public void testItemAsList2() { NULL.add(NULL); } @Test(expected = RuntimeException.class) - public void testItemAsList3(){ + public void testItemAsList3() { NULL.set(0, NULL); } @Test(expected = RuntimeException.class) - public void testItemAsList4(){ + public void testItemAsList4() { NULL.size(); } @Test - public void testAsByteSuccess(){ + public void testAsByteSuccess() { assert RLPItem.fromLong(0xffL).asByte() == (byte) 0xff; } + + @Test + public void testInstanceOf() { + ArrayList li = new ArrayList<>(); + assert li instanceof Collection; + } + + private static class SetWrapper0 { + @RLP + private Set set = new HashSet<>(); + } + + private static class StringComparator implements Comparator { + @Override + public int compare(String o1, String o2) { + return o1.length() - o2.length(); + } + } + + private static class SetWrapper1 { + @RLP + @RLPEncoding(contentOrdering = StringComparator.class) + Set set = new HashSet<>(); + } + + + @Test + public void testEncodeSetSuccess() { + SetWrapper1 w1 = new SetWrapper1(); + List strings = Arrays.asList("1", "22", "333", "4444", "55555"); + w1.set.addAll(strings); + int i = 0; + boolean hasSorted = true; + for (String s : w1.set) { + if (!s.equals(strings.get(i))){ + hasSorted = false; + break; + } + } + assert !hasSorted; + RLPElement el = RLPElement.readRLPTree(w1); + for(int j = 0; j < strings.size(); j++){ + assert el.get(0).get(j).asString().equals(strings.get(j)); + } + } } From 1a3078799a7a658b61118d8dd0a0daf0e0763b9b Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Fri, 13 Dec 2019 21:31:21 +0800 Subject: [PATCH 19/25] add encoding helper --- src/main/java/org/tdf/rlp/RLPCodec.java | 26 ++++++++++++++----- src/main/java/org/tdf/rlp/RLPElement.java | 31 ++++++++++------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/tdf/rlp/RLPCodec.java b/src/main/java/org/tdf/rlp/RLPCodec.java index 9188c8d..18804ea 100644 --- a/src/main/java/org/tdf/rlp/RLPCodec.java +++ b/src/main/java/org/tdf/rlp/RLPCodec.java @@ -8,8 +8,10 @@ import java.math.BigInteger; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.tdf.rlp.RLPConstants.*; +import static org.tdf.rlp.RLPElement.readRLPTree; public final class RLPCodec { @@ -18,7 +20,7 @@ public static T decode(byte[] data, Class clazz) { return decode(element, clazz); } - static List decodeList(RLPElement element, Class elementType){ + static List decodeList(RLPElement element, Class elementType) { return decodeList(element.asRLPList(), 1, elementType); } @@ -51,7 +53,7 @@ public static T decode(RLPElement element, Class clazz) { if (clazz == RLPElement.class) return (T) element; if (clazz == RLPList.class) return (T) element.asRLPList(); if (clazz == RLPItem.class) return (T) element.asRLPItem(); - if(clazz == boolean.class || clazz == Boolean.class) return (T) Boolean.valueOf(element.asBoolean()); + if (clazz == boolean.class || clazz == Boolean.class) return (T) Boolean.valueOf(element.asBoolean()); RLPDecoder decoder = RLPUtils.getAnnotatedRLPDecoder(clazz); if (decoder != null) return (T) decoder.decode(element); // non null terminals @@ -184,8 +186,8 @@ public static String decodeString(byte[] encoded) { return RLPElement.fromEncoded(encoded).asString(); } - public static byte[] encode(Object o){ - return RLPElement.readRLPTree(o).getEncoded(); + public static byte[] encode(Object o) { + return readRLPTree(o).getEncoded(); } // rlp list encode @@ -276,7 +278,19 @@ public static byte[] encodeElements(@NonNull Collection elements) { return data; } - static RLPElement encodeCollection(Collection col, Comparator ordering){ - return RLPElement.readRLPTree(col.stream().sorted(ordering).collect(Collectors.toList())); + static RLPElement encodeCollection(Collection col, Comparator contentOrdering) { + Stream s = contentOrdering == null ? col.stream() : col.stream().sorted(contentOrdering); + return new RLPList(s.map(RLPElement::readRLPTree) + .collect(Collectors.toList())); + } + + static RLPElement encodeMap(Map m, Comparator keyOrdering) { + RLPList list = RLPList.createEmpty(m.size() * 2); + Stream keys = keyOrdering == null ? m.keySet().stream() : m.keySet().stream().sorted(keyOrdering); + keys.forEach(x -> { + list.add(readRLPTree(x)); + list.add(readRLPTree(m.get(x))); + }); + return list; } } diff --git a/src/main/java/org/tdf/rlp/RLPElement.java b/src/main/java/org/tdf/rlp/RLPElement.java index 28b2c9b..c38a542 100644 --- a/src/main/java/org/tdf/rlp/RLPElement.java +++ b/src/main/java/org/tdf/rlp/RLPElement.java @@ -95,6 +95,9 @@ static RLPElement readRLPTree(Object t) { if (t instanceof Long || t.getClass().equals(long.class)) { return RLPItem.fromLong((long) t); } + if (t instanceof Map) { + return RLPCodec.encodeMap((Map) t, null); + } if (t.getClass().isArray()) { RLPList list = RLPList.createEmpty(Array.getLength(t)); for (int i = 0; i < Array.getLength(t); i++) { @@ -103,11 +106,7 @@ static RLPElement readRLPTree(Object t) { return list; } if (t instanceof Collection) { - RLPList list = RLPList.createEmpty(((Collection) t).size()); - for (Object o : ((Collection) t)) { - list.add(readRLPTree(o)); - } - return list; + return RLPCodec.encodeCollection((Collection) t, null); } // peek fields reflection List fields = RLPUtils.getRLPFields(t.getClass()); @@ -115,6 +114,11 @@ static RLPElement readRLPTree(Object t) { throw new RuntimeException(t.getClass() + " is not supported, no @RLP annotation found"); return new RLPList(fields.stream().map(f -> { f.setAccessible(true); + try { + if (f.get(t) == null) return NULL; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } RLPEncoder fieldEncoder = RLPUtils.getAnnotatedRLPEncoder(f); if (fieldEncoder != null) { try { @@ -124,7 +128,7 @@ static RLPElement readRLPTree(Object t) { } } Comparator comparator = RLPUtils.getContentOrdering(f); - if (Collection.class.isAssignableFrom(f.getType()) && comparator != null) { + if (Collection.class.isAssignableFrom(f.getType())) { try { return RLPCodec.encodeCollection((Collection) f.get(t), comparator); } catch (Exception e) { @@ -132,18 +136,11 @@ static RLPElement readRLPTree(Object t) { } } comparator = RLPUtils.getKeyOrdering(f); - if(Map.class.isAssignableFrom(f.getType())){ - try{ + if (Map.class.isAssignableFrom(f.getType())) { + try { Map m = (Map) f.get(t); - RLPList list = RLPList.createEmpty(m.size() * 2); - Stream s = m.keySet().stream(); - if(comparator != null) s = s.sorted(comparator); - s.forEach(x -> { - list.add(readRLPTree(x)); - list.add(readRLPTree(m.get(x))); - }); - return list; - }catch (Exception e){ + return RLPCodec.encodeMap(m, comparator); + } catch (Exception e) { throw new RuntimeException(e); } } From b4846ab589a76583062416d25423d4d4fc98701b Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Fri, 13 Dec 2019 22:49:19 +0800 Subject: [PATCH 20/25] ready for decode container type --- src/main/java/org/tdf/rlp/RLPCodec.java | 62 ++++++++++++- src/main/java/org/tdf/rlp/RLPUtils.java | 115 +++++++++++++++++++++++- src/test/java/org/tdf/rlp/RLPTest.java | 14 +++ 3 files changed, 185 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/tdf/rlp/RLPCodec.java b/src/main/java/org/tdf/rlp/RLPCodec.java index 18804ea..1933d09 100644 --- a/src/main/java/org/tdf/rlp/RLPCodec.java +++ b/src/main/java/org/tdf/rlp/RLPCodec.java @@ -2,9 +2,7 @@ import lombok.NonNull; -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; +import java.lang.reflect.*; import java.math.BigInteger; import java.util.*; import java.util.stream.Collectors; @@ -293,4 +291,62 @@ static RLPElement encodeMap(Map m, Comparator keyOrdering) { }); return list; } + + static Object decodeContainer(RLPElement element, RLPUtils.Container container) { + switch (container.getContainerType()) { + case RAW: + return decode(element, container.asRawType()); + case COLLECTION: { + try { + RLPUtils.CollectionContainer collectionContainer = container.asCollectionContainer(); + Collection res = (Collection) getDefaultImpl(collectionContainer.collectionType).newInstance(); + for(int i = 0; i < element.size(); i++){ + res.add(decodeContainer(element.get(i), collectionContainer.contentType)); + } + return res; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + case MAP: { + try { + RLPUtils.MapContainer mapContainer = container.asMapContainer(); + Map res = (Map) getDefaultImpl(mapContainer.mapType).newInstance(); + for(int i = 0; i < element.size(); i+= 2){ + res.put( + decodeContainer(element.get(i), mapContainer.keyType), + decodeContainer(element.get(i+1), mapContainer.valueType) + ); + } + return res; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + throw new RuntimeException("unreachable"); + } + + private static Class getDefaultImpl(Class clazz) { + if (clazz == Collection.class + || clazz == List.class + ) { + return ArrayList.class; + } + if (clazz == Set.class) { + return HashSet.class; + } + if (clazz == Queue.class) { + return LinkedList.class; + } + if (clazz == Deque.class) { + return ArrayDeque.class; + } + if(clazz == Map.class){ + return HashMap.class; + } + if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) + throw new RuntimeException("cannot new instance of " + clazz); + return clazz; + } } diff --git a/src/main/java/org/tdf/rlp/RLPUtils.java b/src/main/java/org/tdf/rlp/RLPUtils.java index b45ea71..c25d828 100644 --- a/src/main/java/org/tdf/rlp/RLPUtils.java +++ b/src/main/java/org/tdf/rlp/RLPUtils.java @@ -1,9 +1,7 @@ package org.tdf.rlp; import java.lang.reflect.*; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; final class RLPUtils { @@ -112,4 +110,115 @@ public Resolved(int level, Class type) { this.type = type; } } + + enum ContainerType{ + RAW, + COLLECTION, + MAP + } + + interface Container{ + ContainerType getContainerType(); + Class asRawType(); + CollectionContainer asCollectionContainer(); + MapContainer asMapContainer(); + } + + static class Raw implements Container{ + Class rawType; + public ContainerType getContainerType(){ + return ContainerType.RAW; + } + + @Override + public Class asRawType() { + return rawType; + } + + @Override + public CollectionContainer asCollectionContainer() { + throw new RuntimeException("not a collection container"); + } + + @Override + public MapContainer asMapContainer() { + throw new RuntimeException("not a map container"); } + } + + static class CollectionContainer implements Container{ + Class collectionType; + + public ContainerType getContainerType(){ + return ContainerType.COLLECTION; + } + + Container contentType; + + @Override + public Class asRawType() { + throw new RuntimeException("not a raw type"); + } + + @Override + public CollectionContainer asCollectionContainer() { + return this; + } + + @Override + public MapContainer asMapContainer() { + throw new RuntimeException("not a map container"); + } + } + + static class MapContainer implements Container{ + Class mapType; + + public ContainerType getContainerType(){ + return ContainerType.MAP; + } + + Container keyType; + Container valueType; + + @Override + public Class asRawType() { + throw new RuntimeException("not a raw type"); + } + + @Override + public CollectionContainer asCollectionContainer() { + throw new RuntimeException("not a collection container"); + } + + @Override + public MapContainer asMapContainer() { + return this; + } + } + + static Container resolveContainer(Type type){ + if(!(type instanceof ParameterizedType)){ + Raw raw = new Raw(); + raw.rawType = (Class) type; + return raw; + } + ParameterizedType parameterizedType = (ParameterizedType) type; + Class clazz = (Class) parameterizedType.getRawType(); + if(Collection.class.isAssignableFrom(clazz)){ + CollectionContainer con = new CollectionContainer(); + con.contentType = resolveContainer(parameterizedType.getActualTypeArguments()[0]); + con.collectionType = clazz; + return con; + } + if(Map.class.isAssignableFrom(clazz)){ + MapContainer con = new MapContainer(); + con.keyType = resolveContainer(parameterizedType.getActualTypeArguments()[0]); + con.valueType = resolveContainer(parameterizedType.getActualTypeArguments()[1]); + con.mapType = clazz; + return con; + } + Raw raw = new Raw(); + raw.rawType = clazz; + return raw; + } } diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 5d90f1a..19d7297 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -1408,4 +1408,18 @@ public void testEncodeSetSuccess() { assert el.get(0).get(j).asString().equals(strings.get(j)); } } + + public static class Con{ + public List>>> sss; + public Optional ccc; + public String vvv; + } + + @Test + public void testContainer() throws Exception{ + RLPUtils.Container con = RLPUtils.resolveContainer(Con.class.getField("sss").getGenericType()); + RLPUtils.Container con2 = RLPUtils.resolveContainer(Con.class.getField("ccc").getGenericType()); + RLPUtils.Container con3 = RLPUtils.resolveContainer(Con.class.getField("vvv").getGenericType()); + + } } From 9e97b65981485e6d615b7b647dd3c5460b9397b0 Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Fri, 13 Dec 2019 23:28:04 +0800 Subject: [PATCH 21/25] support set, map and common collections --- src/main/java/org/tdf/rlp/RLPCodec.java | 50 +++++++--- src/main/java/org/tdf/rlp/RLPDecoding.java | 1 - src/main/java/org/tdf/rlp/RLPUtils.java | 107 +++++++++++++++------ src/test/java/org/tdf/rlp/RLPTest.java | 24 ++++- 4 files changed, 132 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/tdf/rlp/RLPCodec.java b/src/main/java/org/tdf/rlp/RLPCodec.java index 1933d09..624ad1f 100644 --- a/src/main/java/org/tdf/rlp/RLPCodec.java +++ b/src/main/java/org/tdf/rlp/RLPCodec.java @@ -88,9 +88,36 @@ public static T decode(RLPElement element, Class clazz) { return (T) res; } // cannot determine generic type at runtime - if (clazz == List.class) { + if (clazz == List.class || clazz == Collection.class) { return (T) element.asRLPList(); } + if (clazz == Set.class || clazz == HashSet.class) { + return (T) new HashSet(element.asRLPList()); + } + if (clazz == TreeSet.class) { + return (T) new TreeSet(element.asRLPList()); + } + if (clazz == Map.class || clazz == HashMap.class) { + HashMap m = new HashMap(element.size() / 2); + for (int i = 0; i < element.size(); i += 2) { + m.put(element.get(i), element.get(i + 1)); + } + return (T) m; + } + if (clazz == TreeMap.class) { + TreeMap m = new TreeMap(); + for (int i = 0; i < element.size(); i += 2) { + m.put(element.get(i), element.get(i + 1)); + } + return (T) m; + } + if(clazz == Queue.class || clazz == LinkedList.class){ + LinkedList list = new LinkedList(element.asRLPList()); + return (T) list; + } + if(clazz == Deque.class || clazz == ArrayDeque.class){ + return (T) new ArrayDeque<>(element.asRLPList()); + } T o; try { @@ -117,21 +144,12 @@ public static T decode(RLPElement element, Class clazz) { continue; } - if (!f.getType().equals(List.class)) { - try { - f.set(o, decode(el, f.getType())); - } catch (Exception e) { - throw new RuntimeException(e); - } - continue; - } - try { if (el.isNull()) { continue; } - RLPUtils.Resolved resolved = RLPUtils.resolveFieldType(f); - f.set(o, decodeList(el.asRLPList(), resolved.level, resolved.type)); + RLPUtils.Container container = RLPUtils.resolveField(f); + f.set(o, decodeContainer(el, container)); } catch (Exception e) { throw new RuntimeException(e); } @@ -300,7 +318,7 @@ static Object decodeContainer(RLPElement element, RLPUtils.Container container) try { RLPUtils.CollectionContainer collectionContainer = container.asCollectionContainer(); Collection res = (Collection) getDefaultImpl(collectionContainer.collectionType).newInstance(); - for(int i = 0; i < element.size(); i++){ + for (int i = 0; i < element.size(); i++) { res.add(decodeContainer(element.get(i), collectionContainer.contentType)); } return res; @@ -312,10 +330,10 @@ static Object decodeContainer(RLPElement element, RLPUtils.Container container) try { RLPUtils.MapContainer mapContainer = container.asMapContainer(); Map res = (Map) getDefaultImpl(mapContainer.mapType).newInstance(); - for(int i = 0; i < element.size(); i+= 2){ + for (int i = 0; i < element.size(); i += 2) { res.put( decodeContainer(element.get(i), mapContainer.keyType), - decodeContainer(element.get(i+1), mapContainer.valueType) + decodeContainer(element.get(i + 1), mapContainer.valueType) ); } return res; @@ -342,7 +360,7 @@ private static Class getDefaultImpl(Class clazz) { if (clazz == Deque.class) { return ArrayDeque.class; } - if(clazz == Map.class){ + if (clazz == Map.class) { return HashMap.class; } if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) diff --git a/src/main/java/org/tdf/rlp/RLPDecoding.java b/src/main/java/org/tdf/rlp/RLPDecoding.java index 5261c4a..19ce1f4 100644 --- a/src/main/java/org/tdf/rlp/RLPDecoding.java +++ b/src/main/java/org/tdf/rlp/RLPDecoding.java @@ -11,5 +11,4 @@ public @interface RLPDecoding { Class value() default RLPDecoder.None.class; Class as() default Void.class; - } diff --git a/src/main/java/org/tdf/rlp/RLPUtils.java b/src/main/java/org/tdf/rlp/RLPUtils.java index c25d828..6497c5b 100644 --- a/src/main/java/org/tdf/rlp/RLPUtils.java +++ b/src/main/java/org/tdf/rlp/RLPUtils.java @@ -46,30 +46,30 @@ static List getRLPFields(Class clazz) { return fields; } - static Comparator getContentOrdering(AnnotatedElement element){ + static Comparator getContentOrdering(AnnotatedElement element) { if (!element.isAnnotationPresent(RLPEncoding.class)) { return null; } Class clazz = element.getAnnotation(RLPEncoding.class).contentOrdering(); - if(clazz == RLPEncoding.None.class) return null; - try{ + if (clazz == RLPEncoding.None.class) return null; + try { Constructor con = clazz.getDeclaredConstructor(); con.setAccessible(true); return con.newInstance(); - }catch (Exception e){ + } catch (Exception e) { throw new RuntimeException("new instance of " + clazz + " failed " + e.getMessage()); } } - static Comparator getKeyOrdering(AnnotatedElement element){ + static Comparator getKeyOrdering(AnnotatedElement element) { if (!element.isAnnotationPresent(RLPEncoding.class)) { return null; } Class clazz = element.getAnnotation(RLPEncoding.class).keyOrdering(); - if(clazz == RLPEncoding.None.class) return null; - try{ + if (clazz == RLPEncoding.None.class) return null; + try { return clazz.newInstance(); - }catch (Exception e){ + } catch (Exception e) { throw new RuntimeException("new instance of " + clazz + " failed " + e.getCause()); } } @@ -111,22 +111,26 @@ public Resolved(int level, Class type) { } } - enum ContainerType{ + enum ContainerType { RAW, COLLECTION, MAP } - interface Container{ + interface Container { ContainerType getContainerType(); + Class asRawType(); + CollectionContainer asCollectionContainer(); + MapContainer asMapContainer(); } - static class Raw implements Container{ + static class Raw implements Container { Class rawType; - public ContainerType getContainerType(){ + + public ContainerType getContainerType() { return ContainerType.RAW; } @@ -142,13 +146,21 @@ public CollectionContainer asCollectionContainer() { @Override public MapContainer asMapContainer() { - throw new RuntimeException("not a map container"); } + throw new RuntimeException("not a map container"); + } + + public Raw() { + } + + Raw(Class rawType) { + this.rawType = rawType; + } } - static class CollectionContainer implements Container{ + static class CollectionContainer implements Container { Class collectionType; - public ContainerType getContainerType(){ + public ContainerType getContainerType() { return ContainerType.COLLECTION; } @@ -170,10 +182,10 @@ public MapContainer asMapContainer() { } } - static class MapContainer implements Container{ + static class MapContainer implements Container { Class mapType; - public ContainerType getContainerType(){ + public ContainerType getContainerType() { return ContainerType.MAP; } @@ -196,29 +208,64 @@ public MapContainer asMapContainer() { } } - static Container resolveContainer(Type type){ - if(!(type instanceof ParameterizedType)){ - Raw raw = new Raw(); - raw.rawType = (Class) type; - return raw; + static Container resolveField(Field field) { + Container container = resolveContainerofGeneric(field.getGenericType()); + Class clazz = null; + if (field.isAnnotationPresent(RLPDecoding.class)) { + clazz = field.getAnnotation(RLPDecoding.class).as(); } - ParameterizedType parameterizedType = (ParameterizedType) type; - Class clazz = (Class) parameterizedType.getRawType(); + + if(clazz == null || clazz == Void.class) return container; + if(container.getContainerType() == ContainerType.RAW) + throw new RuntimeException("@RLPDecoding.as is used on collection or map type"); + if(!field.getType().isAssignableFrom(clazz)) + throw new RuntimeException("cannot assign " + clazz + " as " + field.getType()); + if(container.getContainerType() == ContainerType.COLLECTION){ + container.asCollectionContainer().collectionType = clazz; + } + if(container.getContainerType() == ContainerType.MAP){ + container.asMapContainer().mapType = clazz; + } + return container; + } + + static Container resolveContainerOfNoGeneric(Class clazz){ if(Collection.class.isAssignableFrom(clazz)){ CollectionContainer con = new CollectionContainer(); - con.contentType = resolveContainer(parameterizedType.getActualTypeArguments()[0]); + con.contentType = new Raw(RLPElement.class); con.collectionType = clazz; return con; } if(Map.class.isAssignableFrom(clazz)){ MapContainer con = new MapContainer(); - con.keyType = resolveContainer(parameterizedType.getActualTypeArguments()[0]); - con.valueType = resolveContainer(parameterizedType.getActualTypeArguments()[1]); + con.keyType = new Raw(RLPElement.class); + con.valueType = new Raw(RLPElement.class); + con.mapType = clazz; + return con; + } + return new Raw(clazz); + } + + static Container resolveContainerofGeneric(Type type) { + if (!(type instanceof ParameterizedType)) { + Class clazz = (Class) type; + return resolveContainerOfNoGeneric(clazz); + } + ParameterizedType parameterizedType = (ParameterizedType) type; + Class clazz = (Class) parameterizedType.getRawType(); + if (Collection.class.isAssignableFrom(clazz)) { + CollectionContainer con = new CollectionContainer(); + con.contentType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[0]); + con.collectionType = clazz; + return con; + } + if (Map.class.isAssignableFrom(clazz)) { + MapContainer con = new MapContainer(); + con.keyType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[0]); + con.valueType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[1]); con.mapType = clazz; return con; } - Raw raw = new Raw(); - raw.rawType = clazz; - return raw; + return resolveContainerOfNoGeneric(clazz); } } diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 19d7297..a4a427e 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -1413,13 +1413,31 @@ public static class Con{ public List>>> sss; public Optional ccc; public String vvv; + public List li; } @Test public void testContainer() throws Exception{ - RLPUtils.Container con = RLPUtils.resolveContainer(Con.class.getField("sss").getGenericType()); - RLPUtils.Container con2 = RLPUtils.resolveContainer(Con.class.getField("ccc").getGenericType()); - RLPUtils.Container con3 = RLPUtils.resolveContainer(Con.class.getField("vvv").getGenericType()); + RLPUtils.Container con = RLPUtils.resolveContainerofGeneric(Con.class.getField("sss").getGenericType()); + RLPUtils.Container con2 = RLPUtils.resolveContainerofGeneric(Con.class.getField("ccc").getGenericType()); + RLPUtils.Container con3 = RLPUtils.resolveContainerofGeneric(Con.class.getField("vvv").getGenericType()); + RLPUtils.Container con4 = RLPUtils.resolveContainerofGeneric(Con.class.getField("li").getGenericType()); + } + public static class MapWrapper2{ + @RLP + @RLPDecoding(as = TreeMap.class) + public Map> map = new HashMap<>(); + } + + @Test + public void testMapWrapper2(){ + MapWrapper2 wrapper2 = new MapWrapper2(); + wrapper2.map.put("sss", new HashMap<>()); + wrapper2.map.get("sss").put("aaa", "bbb"); + byte[] encoded = RLPCodec.encode(wrapper2); + MapWrapper2 decoded = RLPCodec.decode(encoded, MapWrapper2.class); + assert decoded.map instanceof TreeMap; + assert decoded.map.get("sss").get("aaa").equals("bbb"); } } From cd276a8118467ff03fdcd412ac3a555bf438d45e Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Fri, 13 Dec 2019 23:28:23 +0800 Subject: [PATCH 22/25] Update build.gradle --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1bf9a1b..7a0a178 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'org.tdf' -version '1.1.5' +version '1.1.7' sourceCompatibility = 1.8 From 28eab21b461885e48777e74f0eabdb1df41b0953 Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Fri, 13 Dec 2019 23:44:13 +0800 Subject: [PATCH 23/25] refactors --- .../java/org/tdf/rlp/CollectionContainer.java | 28 +++ src/main/java/org/tdf/rlp/Container.java | 78 +++++++ src/main/java/org/tdf/rlp/ContainerType.java | 7 + src/main/java/org/tdf/rlp/MapContainer.java | 29 +++ src/main/java/org/tdf/rlp/RLPCodec.java | 39 +--- src/main/java/org/tdf/rlp/RLPUtils.java | 195 ------------------ src/main/java/org/tdf/rlp/Raw.java | 31 +++ src/test/java/org/tdf/rlp/RLPTest.java | 45 ++-- 8 files changed, 195 insertions(+), 257 deletions(-) create mode 100644 src/main/java/org/tdf/rlp/CollectionContainer.java create mode 100644 src/main/java/org/tdf/rlp/Container.java create mode 100644 src/main/java/org/tdf/rlp/ContainerType.java create mode 100644 src/main/java/org/tdf/rlp/MapContainer.java create mode 100644 src/main/java/org/tdf/rlp/Raw.java diff --git a/src/main/java/org/tdf/rlp/CollectionContainer.java b/src/main/java/org/tdf/rlp/CollectionContainer.java new file mode 100644 index 0000000..e5e0a9a --- /dev/null +++ b/src/main/java/org/tdf/rlp/CollectionContainer.java @@ -0,0 +1,28 @@ +package org.tdf.rlp; + +import java.util.Collection; + +class CollectionContainer implements Container { + Class collectionType; + + public ContainerType getContainerType() { + return ContainerType.COLLECTION; + } + + Container contentType; + + @Override + public Class asRawType() { + throw new RuntimeException("not a raw type"); + } + + @Override + public CollectionContainer asCollectionContainer() { + return this; + } + + @Override + public MapContainer asMapContainer() { + throw new RuntimeException("not a map container"); + } +} \ No newline at end of file diff --git a/src/main/java/org/tdf/rlp/Container.java b/src/main/java/org/tdf/rlp/Container.java new file mode 100644 index 0000000..b757fb5 --- /dev/null +++ b/src/main/java/org/tdf/rlp/Container.java @@ -0,0 +1,78 @@ +package org.tdf.rlp; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Map; + +interface Container { + ContainerType getContainerType(); + + Class asRawType(); + + CollectionContainer asCollectionContainer(); + + MapContainer asMapContainer(); + + static Container resolveField(Field field) { + Container container = resolveContainerofGeneric(field.getGenericType()); + Class clazz = null; + if (field.isAnnotationPresent(RLPDecoding.class)) { + clazz = field.getAnnotation(RLPDecoding.class).as(); + } + + if(clazz == null || clazz == Void.class) return container; + if(container.getContainerType() == ContainerType.RAW) + throw new RuntimeException("@RLPDecoding.as is used on collection or map type"); + if(!field.getType().isAssignableFrom(clazz)) + throw new RuntimeException("cannot assign " + clazz + " as " + field.getType()); + if(container.getContainerType() == ContainerType.COLLECTION){ + container.asCollectionContainer().collectionType = clazz; + } + if(container.getContainerType() == ContainerType.MAP){ + container.asMapContainer().mapType = clazz; + } + return container; + } + + static Container resolveContainerOfNoGeneric(Class clazz){ + if(Collection.class.isAssignableFrom(clazz)){ + CollectionContainer con = new CollectionContainer(); + con.contentType = new Raw(RLPElement.class); + con.collectionType = clazz; + return con; + } + if(Map.class.isAssignableFrom(clazz)){ + MapContainer con = new MapContainer(); + con.keyType = new Raw(RLPElement.class); + con.valueType = new Raw(RLPElement.class); + con.mapType = clazz; + return con; + } + return new Raw(clazz); + } + + static Container resolveContainerofGeneric(Type type) { + if (!(type instanceof ParameterizedType)) { + Class clazz = (Class) type; + return resolveContainerOfNoGeneric(clazz); + } + ParameterizedType parameterizedType = (ParameterizedType) type; + Class clazz = (Class) parameterizedType.getRawType(); + if (Collection.class.isAssignableFrom(clazz)) { + CollectionContainer con = new CollectionContainer(); + con.contentType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[0]); + con.collectionType = clazz; + return con; + } + if (Map.class.isAssignableFrom(clazz)) { + MapContainer con = new MapContainer(); + con.keyType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[0]); + con.valueType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[1]); + con.mapType = clazz; + return con; + } + return resolveContainerOfNoGeneric(clazz); + } +} diff --git a/src/main/java/org/tdf/rlp/ContainerType.java b/src/main/java/org/tdf/rlp/ContainerType.java new file mode 100644 index 0000000..eb7d380 --- /dev/null +++ b/src/main/java/org/tdf/rlp/ContainerType.java @@ -0,0 +1,7 @@ +package org.tdf.rlp; + +public enum ContainerType { + RAW, + COLLECTION, + MAP +} diff --git a/src/main/java/org/tdf/rlp/MapContainer.java b/src/main/java/org/tdf/rlp/MapContainer.java new file mode 100644 index 0000000..20d02f2 --- /dev/null +++ b/src/main/java/org/tdf/rlp/MapContainer.java @@ -0,0 +1,29 @@ +package org.tdf.rlp; + +import java.util.Map; + +public class MapContainer implements Container { + Class mapType; + + public ContainerType getContainerType() { + return ContainerType.MAP; + } + + Container keyType; + Container valueType; + + @Override + public Class asRawType() { + throw new RuntimeException("not a raw type"); + } + + @Override + public CollectionContainer asCollectionContainer() { + throw new RuntimeException("not a collection container"); + } + + @Override + public MapContainer asMapContainer() { + return this; + } +} diff --git a/src/main/java/org/tdf/rlp/RLPCodec.java b/src/main/java/org/tdf/rlp/RLPCodec.java index 624ad1f..e2c99c5 100644 --- a/src/main/java/org/tdf/rlp/RLPCodec.java +++ b/src/main/java/org/tdf/rlp/RLPCodec.java @@ -8,45 +8,16 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.tdf.rlp.Container.resolveField; import static org.tdf.rlp.RLPConstants.*; import static org.tdf.rlp.RLPElement.readRLPTree; public final class RLPCodec { - public static T decode(byte[] data, Class clazz) { RLPElement element = RLPElement.fromEncoded(data); return decode(element, clazz); } - static List decodeList(RLPElement element, Class elementType) { - return decodeList(element.asRLPList(), 1, elementType); - } - - static List decodeList(byte[] data, Class elementType) { - RLPElement element = RLPElement.fromEncoded(data); - return decodeList(element.asRLPList(), 1, elementType); - } - - private static List decodeList(RLPList list, int level, Class elementType) { - if (level == 0) throw new RuntimeException("level should be positive"); - if (level > 1) { - List res = new ArrayList(list.size()); - for (int i = 0; i < list.size(); i++) { - res.add(decodeList(list.get(i).asRLPList(), level - 1, elementType)); - } - return res; - } - if (elementType == RLPElement.class) return list; - if (elementType == RLPItem.class) { - return list.stream().map(x -> x.asRLPItem()).collect(Collectors.toList()); - } - List res = new ArrayList<>(list.size()); - for (int i = 0; i < list.size(); i++) { - res.add(decode(list.get(i), elementType)); - } - return res; - } - public static T decode(RLPElement element, Class clazz) { if (clazz == RLPElement.class) return (T) element; if (clazz == RLPList.class) return (T) element.asRLPList(); @@ -148,7 +119,7 @@ public static T decode(RLPElement element, Class clazz) { if (el.isNull()) { continue; } - RLPUtils.Container container = RLPUtils.resolveField(f); + Container container = resolveField(f); f.set(o, decodeContainer(el, container)); } catch (Exception e) { throw new RuntimeException(e); @@ -310,13 +281,13 @@ static RLPElement encodeMap(Map m, Comparator keyOrdering) { return list; } - static Object decodeContainer(RLPElement element, RLPUtils.Container container) { + static Object decodeContainer(RLPElement element, Container container) { switch (container.getContainerType()) { case RAW: return decode(element, container.asRawType()); case COLLECTION: { try { - RLPUtils.CollectionContainer collectionContainer = container.asCollectionContainer(); + CollectionContainer collectionContainer = container.asCollectionContainer(); Collection res = (Collection) getDefaultImpl(collectionContainer.collectionType).newInstance(); for (int i = 0; i < element.size(); i++) { res.add(decodeContainer(element.get(i), collectionContainer.contentType)); @@ -328,7 +299,7 @@ static Object decodeContainer(RLPElement element, RLPUtils.Container container) } case MAP: { try { - RLPUtils.MapContainer mapContainer = container.asMapContainer(); + MapContainer mapContainer = container.asMapContainer(); Map res = (Map) getDefaultImpl(mapContainer.mapType).newInstance(); for (int i = 0; i < element.size(); i += 2) { res.put( diff --git a/src/main/java/org/tdf/rlp/RLPUtils.java b/src/main/java/org/tdf/rlp/RLPUtils.java index 6497c5b..a5e9c45 100644 --- a/src/main/java/org/tdf/rlp/RLPUtils.java +++ b/src/main/java/org/tdf/rlp/RLPUtils.java @@ -73,199 +73,4 @@ static Comparator getKeyOrdering(AnnotatedElement element) { throw new RuntimeException("new instance of " + clazz + " failed " + e.getCause()); } } - - static Resolved resolveFieldType(Field f) { - if (f.getType() != List.class) { - return new Resolved(0, f.getType()); - } - Type generic = f.getGenericType(); - if (!(generic instanceof ParameterizedType)) { - return new Resolved(1, RLPElement.class); - } - return resolve((ParameterizedType) generic, new Resolved(1, null)); - } - - private static Resolved resolve(ParameterizedType type, Resolved resolved) { - Type[] types = type.getActualTypeArguments(); - Type t = types[0]; - if (t instanceof Class) { - resolved.type = (Class) t; - return resolved; - } - // type is nested; - ParameterizedType nested = (ParameterizedType) t; - resolved.level += 1; - return resolve(nested, resolved); - } - - static class Resolved { - int level; - Class type; - - public Resolved() { - } - - public Resolved(int level, Class type) { - this.level = level; - this.type = type; - } - } - - enum ContainerType { - RAW, - COLLECTION, - MAP - } - - interface Container { - ContainerType getContainerType(); - - Class asRawType(); - - CollectionContainer asCollectionContainer(); - - MapContainer asMapContainer(); - } - - static class Raw implements Container { - Class rawType; - - public ContainerType getContainerType() { - return ContainerType.RAW; - } - - @Override - public Class asRawType() { - return rawType; - } - - @Override - public CollectionContainer asCollectionContainer() { - throw new RuntimeException("not a collection container"); - } - - @Override - public MapContainer asMapContainer() { - throw new RuntimeException("not a map container"); - } - - public Raw() { - } - - Raw(Class rawType) { - this.rawType = rawType; - } - } - - static class CollectionContainer implements Container { - Class collectionType; - - public ContainerType getContainerType() { - return ContainerType.COLLECTION; - } - - Container contentType; - - @Override - public Class asRawType() { - throw new RuntimeException("not a raw type"); - } - - @Override - public CollectionContainer asCollectionContainer() { - return this; - } - - @Override - public MapContainer asMapContainer() { - throw new RuntimeException("not a map container"); - } - } - - static class MapContainer implements Container { - Class mapType; - - public ContainerType getContainerType() { - return ContainerType.MAP; - } - - Container keyType; - Container valueType; - - @Override - public Class asRawType() { - throw new RuntimeException("not a raw type"); - } - - @Override - public CollectionContainer asCollectionContainer() { - throw new RuntimeException("not a collection container"); - } - - @Override - public MapContainer asMapContainer() { - return this; - } - } - - static Container resolveField(Field field) { - Container container = resolveContainerofGeneric(field.getGenericType()); - Class clazz = null; - if (field.isAnnotationPresent(RLPDecoding.class)) { - clazz = field.getAnnotation(RLPDecoding.class).as(); - } - - if(clazz == null || clazz == Void.class) return container; - if(container.getContainerType() == ContainerType.RAW) - throw new RuntimeException("@RLPDecoding.as is used on collection or map type"); - if(!field.getType().isAssignableFrom(clazz)) - throw new RuntimeException("cannot assign " + clazz + " as " + field.getType()); - if(container.getContainerType() == ContainerType.COLLECTION){ - container.asCollectionContainer().collectionType = clazz; - } - if(container.getContainerType() == ContainerType.MAP){ - container.asMapContainer().mapType = clazz; - } - return container; - } - - static Container resolveContainerOfNoGeneric(Class clazz){ - if(Collection.class.isAssignableFrom(clazz)){ - CollectionContainer con = new CollectionContainer(); - con.contentType = new Raw(RLPElement.class); - con.collectionType = clazz; - return con; - } - if(Map.class.isAssignableFrom(clazz)){ - MapContainer con = new MapContainer(); - con.keyType = new Raw(RLPElement.class); - con.valueType = new Raw(RLPElement.class); - con.mapType = clazz; - return con; - } - return new Raw(clazz); - } - - static Container resolveContainerofGeneric(Type type) { - if (!(type instanceof ParameterizedType)) { - Class clazz = (Class) type; - return resolveContainerOfNoGeneric(clazz); - } - ParameterizedType parameterizedType = (ParameterizedType) type; - Class clazz = (Class) parameterizedType.getRawType(); - if (Collection.class.isAssignableFrom(clazz)) { - CollectionContainer con = new CollectionContainer(); - con.contentType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[0]); - con.collectionType = clazz; - return con; - } - if (Map.class.isAssignableFrom(clazz)) { - MapContainer con = new MapContainer(); - con.keyType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[0]); - con.valueType = resolveContainerofGeneric(parameterizedType.getActualTypeArguments()[1]); - con.mapType = clazz; - return con; - } - return resolveContainerOfNoGeneric(clazz); - } } diff --git a/src/main/java/org/tdf/rlp/Raw.java b/src/main/java/org/tdf/rlp/Raw.java new file mode 100644 index 0000000..1c3e4ce --- /dev/null +++ b/src/main/java/org/tdf/rlp/Raw.java @@ -0,0 +1,31 @@ +package org.tdf.rlp; + +class Raw implements Container { + Class rawType; + + public ContainerType getContainerType() { + return ContainerType.RAW; + } + + @Override + public Class asRawType() { + return rawType; + } + + @Override + public CollectionContainer asCollectionContainer() { + throw new RuntimeException("not a collection container"); + } + + @Override + public MapContainer asMapContainer() { + throw new RuntimeException("not a map container"); + } + + public Raw() { + } + + Raw(Class rawType) { + this.rawType = rawType; + } +} diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index a4a427e..3408c20 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.tdf.rlp.Container.resolveContainerofGeneric; import static org.tdf.rlp.RLPCodec.*; import static org.tdf.rlp.RLPItem.NULL; import static org.tdf.rlp.RLPItem.ONE; @@ -1213,19 +1214,6 @@ public Nested() { } } - @Test - public void testNested() throws Exception { - RLPUtils.Resolved resolved = RLPUtils.resolveFieldType(Nested.class.getDeclaredField("nested")); - assert resolved.level == 3; - assert resolved.type == String.class; - resolved = RLPUtils.resolveFieldType(Nested.class.getDeclaredField("hello")); - assert resolved.level == 0; - assert resolved.type == String.class; - resolved = RLPUtils.resolveFieldType(NoNested.class.getDeclaredField("nested")); - assert resolved.level == 1; - assert resolved.type == String.class; - } - private static class NoNested { @RLP private List nested; @@ -1277,9 +1265,10 @@ public void testBoolean() { assert RLPCodec.decode(RLPItem.fromBoolean(true), Boolean.class); List elements = Stream.of(1, 1, 1).map(RLPItem::fromInt).collect(Collectors.toList()); RLPList list = RLPList.fromElements(elements); - assert RLPCodec.decodeList( - list, Boolean.class - ).stream().allMatch(x -> x); + for (boolean b : RLPCodec.decode( + list, boolean[].class + )) + assert b; } @Test(expected = RuntimeException.class) @@ -1291,8 +1280,8 @@ public void testBooleanFailed() { public void testBooleanFailed2() { List elements = Stream.of(1, 2, 3).map(RLPItem::fromInt).collect(Collectors.toList()); RLPList list = RLPList.fromElements(elements); - RLPCodec.decodeList( - list, Boolean.class + RLPCodec.decode( + list, boolean[].class ); } @@ -1397,19 +1386,19 @@ public void testEncodeSetSuccess() { int i = 0; boolean hasSorted = true; for (String s : w1.set) { - if (!s.equals(strings.get(i))){ + if (!s.equals(strings.get(i))) { hasSorted = false; break; } } assert !hasSorted; RLPElement el = RLPElement.readRLPTree(w1); - for(int j = 0; j < strings.size(); j++){ + for (int j = 0; j < strings.size(); j++) { assert el.get(0).get(j).asString().equals(strings.get(j)); } } - public static class Con{ + public static class Con { public List>>> sss; public Optional ccc; public String vvv; @@ -1417,21 +1406,21 @@ public static class Con{ } @Test - public void testContainer() throws Exception{ - RLPUtils.Container con = RLPUtils.resolveContainerofGeneric(Con.class.getField("sss").getGenericType()); - RLPUtils.Container con2 = RLPUtils.resolveContainerofGeneric(Con.class.getField("ccc").getGenericType()); - RLPUtils.Container con3 = RLPUtils.resolveContainerofGeneric(Con.class.getField("vvv").getGenericType()); - RLPUtils.Container con4 = RLPUtils.resolveContainerofGeneric(Con.class.getField("li").getGenericType()); + public void testContainer() throws Exception { + Container con = resolveContainerofGeneric(Con.class.getField("sss").getGenericType()); + Container con2 = resolveContainerofGeneric(Con.class.getField("ccc").getGenericType()); + Container con3 = resolveContainerofGeneric(Con.class.getField("vvv").getGenericType()); + Container con4 = resolveContainerofGeneric(Con.class.getField("li").getGenericType()); } - public static class MapWrapper2{ + public static class MapWrapper2 { @RLP @RLPDecoding(as = TreeMap.class) public Map> map = new HashMap<>(); } @Test - public void testMapWrapper2(){ + public void testMapWrapper2() { MapWrapper2 wrapper2 = new MapWrapper2(); wrapper2.map.put("sss", new HashMap<>()); wrapper2.map.get("sss").put("aaa", "bbb"); From bdc1207b8cf4155cc5bd85231179e4827ee78c51 Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Sat, 14 Dec 2019 00:06:33 +0800 Subject: [PATCH 24/25] test set/map encoding/decoding --- src/main/java/org/tdf/rlp/RLPUtils.java | 4 +- src/test/java/org/tdf/rlp/ByteArrayMap.java | 189 ++++++++++++++++++ src/test/java/org/tdf/rlp/ByteArraySet.java | 133 ++++++++++++ .../java/org/tdf/rlp/ByteArrayWrapper.java | 46 +++++ .../java/org/tdf/rlp/FastByteComparisons.java | 89 +++++++++ src/test/java/org/tdf/rlp/RLPTest.java | 61 ++++++ src/test/java/org/tdf/rlp/SetAdapter.java | 92 +++++++++ 7 files changed, 613 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/tdf/rlp/ByteArrayMap.java create mode 100644 src/test/java/org/tdf/rlp/ByteArraySet.java create mode 100644 src/test/java/org/tdf/rlp/ByteArrayWrapper.java create mode 100644 src/test/java/org/tdf/rlp/FastByteComparisons.java create mode 100644 src/test/java/org/tdf/rlp/SetAdapter.java diff --git a/src/main/java/org/tdf/rlp/RLPUtils.java b/src/main/java/org/tdf/rlp/RLPUtils.java index a5e9c45..5420a03 100644 --- a/src/main/java/org/tdf/rlp/RLPUtils.java +++ b/src/main/java/org/tdf/rlp/RLPUtils.java @@ -68,7 +68,9 @@ static Comparator getKeyOrdering(AnnotatedElement element) { Class clazz = element.getAnnotation(RLPEncoding.class).keyOrdering(); if (clazz == RLPEncoding.None.class) return null; try { - return clazz.newInstance(); + Constructor con = clazz.getDeclaredConstructor(); + con.setAccessible(true); + return con.newInstance(); } catch (Exception e) { throw new RuntimeException("new instance of " + clazz + " failed " + e.getCause()); } diff --git a/src/test/java/org/tdf/rlp/ByteArrayMap.java b/src/test/java/org/tdf/rlp/ByteArrayMap.java new file mode 100644 index 0000000..c7fb5e2 --- /dev/null +++ b/src/test/java/org/tdf/rlp/ByteArrayMap.java @@ -0,0 +1,189 @@ +package org.tdf.rlp; + +import java.util.*; + + +/** + * wrap byte array wrapper hash map as byte array map + */ +public class ByteArrayMap implements Map { + private final Map delegate; + + public ByteArrayMap() { + this.delegate = new HashMap<>(); + } + + public ByteArrayMap(Map map) { + this(); + putAll(map); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(new ByteArrayWrapper((byte[]) key)); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public V get(Object key) { + return delegate.get(new ByteArrayWrapper((byte[]) key)); + } + + @Override + public V put(byte[] key, V value) { + return delegate.put(new ByteArrayWrapper(key), value); + } + + @Override + public V remove(Object key) { + return delegate.remove(new ByteArrayWrapper((byte[]) key)); + } + + @Override + public void putAll(Map m) { + for (Entry entry : m.entrySet()) { + delegate.put(new ByteArrayWrapper(entry.getKey()), entry.getValue()); + } + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public Set keySet() { + return new ByteArraySet(new SetAdapter<>(delegate)); + } + + @Override + public Collection values() { + return delegate.values(); + } + + @Override + public Set> entrySet() { + return new MapEntrySet(delegate.entrySet()); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public String toString() { + return delegate.toString(); + } + + private class MapEntrySet implements Set> { + private final Set> delegate; + + private MapEntrySet(Set> delegate) { + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + throw new RuntimeException("Not implemented"); + } + + @Override + public Iterator> iterator() { + final Iterator> it = delegate.iterator(); + return new Iterator>() { + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public Entry next() { + Entry next = it.next(); + return new AbstractMap.SimpleImmutableEntry(next.getKey().getData(), next.getValue()); + } + + @Override + public void remove() { + it.remove(); + } + }; + } + + @Override + public Object[] toArray() { + throw new RuntimeException("Not implemented"); + } + + @Override + public T[] toArray(T[] a) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean add(Entry vEntry) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean remove(Object o) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean containsAll(Collection c) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean addAll(Collection> c) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean retainAll(Collection c) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean removeAll(Collection c) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void clear() { + throw new RuntimeException("Not implemented"); + + } + } +} diff --git a/src/test/java/org/tdf/rlp/ByteArraySet.java b/src/test/java/org/tdf/rlp/ByteArraySet.java new file mode 100644 index 0000000..128c42a --- /dev/null +++ b/src/test/java/org/tdf/rlp/ByteArraySet.java @@ -0,0 +1,133 @@ +package org.tdf.rlp; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + + +/** + * wrap byte array hash set as set + */ +public class ByteArraySet implements Set { + Set delegate; + + public ByteArraySet() { + this(new HashSet()); + } + + public ByteArraySet(Collection all){ + this(); + addAll(all); + } + + ByteArraySet(Set delegate) { + this.delegate = delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.contains(new ByteArrayWrapper((byte[]) o)); + } + + @Override + public Iterator iterator() { + return new Iterator() { + + Iterator it = delegate.iterator(); + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public byte[] next() { + return it.next().getData(); + } + + @Override + public void remove() { + it.remove(); + } + }; + } + + @Override + public Object[] toArray() { + byte[][] ret = new byte[size()][]; + + ByteArrayWrapper[] arr = delegate.toArray(new ByteArrayWrapper[size()]); + for (int i = 0; i < arr.length; i++) { + ret[i] = arr[i].getData(); + } + return ret; + } + + @Override + public T[] toArray(T[] a) { + return (T[]) toArray(); + } + + @Override + public boolean add(byte[] bytes) { + return delegate.add(new ByteArrayWrapper(bytes)); + } + + @Override + public boolean remove(Object o) { + return delegate.remove(new ByteArrayWrapper((byte[]) o)); + } + + @Override + public boolean containsAll(Collection c) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean addAll(Collection c) { + boolean ret = false; + for (byte[] bytes : c) { + ret |= add(bytes); + } + return ret; + } + + @Override + public boolean retainAll(Collection c) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean removeAll(Collection c) { + boolean changed = false; + for (Object el : c) { + changed |= remove(el); + } + return changed; + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + public boolean equals(Object o) { + throw new RuntimeException("Not implemented"); + } + + @Override + public int hashCode() { + throw new RuntimeException("Not implemented"); + } +} diff --git a/src/test/java/org/tdf/rlp/ByteArrayWrapper.java b/src/test/java/org/tdf/rlp/ByteArrayWrapper.java new file mode 100644 index 0000000..3ddcbf2 --- /dev/null +++ b/src/test/java/org/tdf/rlp/ByteArrayWrapper.java @@ -0,0 +1,46 @@ +package org.tdf.rlp; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * wrap byte array as immutable + */ +public class ByteArrayWrapper implements Comparable, Serializable { + + private static final long serialVersionUID = 7120319357455987329L; + private final byte[] data; + private int hashCode = 0; + + public ByteArrayWrapper(byte[] data) { + if (data == null) + throw new NullPointerException("Data must not be null"); + this.data = data; + this.hashCode = Arrays.hashCode(data); + } + + public boolean equals(Object other) { + if (!(other instanceof ByteArrayWrapper)) + return false; + byte[] otherData = ((ByteArrayWrapper) other).getData(); + return FastByteComparisons.compareTo( + data, 0, data.length, + otherData, 0, otherData.length) == 0; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public int compareTo(ByteArrayWrapper o) { + return FastByteComparisons.compareTo( + data, 0, data.length, + o.getData(), 0, o.getData().length); + } + + public byte[] getData() { + return data; + } +} diff --git a/src/test/java/org/tdf/rlp/FastByteComparisons.java b/src/test/java/org/tdf/rlp/FastByteComparisons.java new file mode 100644 index 0000000..ef03a3a --- /dev/null +++ b/src/test/java/org/tdf/rlp/FastByteComparisons.java @@ -0,0 +1,89 @@ +package org.tdf.rlp; + +@SuppressWarnings("restriction") +public abstract class FastByteComparisons { + + public static boolean equal(byte[] b1, byte[] b2) { + return b1.length == b2.length && compareTo(b1, 0, b1.length, b2, 0, b2.length) == 0; + } + /** + * Lexicographically compare two byte arrays. + * + * @param b1 buffer1 + * @param s1 offset1 + * @param l1 length1 + * @param b2 buffer2 + * @param s2 offset2 + * @param l2 length2 + * @return int + */ + public static int compareTo(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { + return LexicographicalComparerHolder.BEST_COMPARER.compareTo( + b1, s1, l1, b2, s2, l2); + } + + private interface Comparer { + int compareTo(T buffer1, int offset1, int length1, + T buffer2, int offset2, int length2); + } + + private static Comparer lexicographicalComparerJavaImpl() { + return LexicographicalComparerHolder.PureJavaComparer.INSTANCE; + } + + + /** + * + *

Uses reflection to gracefully fall back to the Java implementation if + * {@code Unsafe} isn't available. + */ + private static class LexicographicalComparerHolder { + static final String UNSAFE_COMPARER_NAME = + LexicographicalComparerHolder.class.getName() + "$UnsafeComparer"; + + static final Comparer BEST_COMPARER = getBestComparer(); + + /** + * Returns the Unsafe-using Comparer, or falls back to the pure-Java + * implementation if unable to do so. + */ + static Comparer getBestComparer() { + try { + Class theClass = Class.forName(UNSAFE_COMPARER_NAME); + + // yes, UnsafeComparer does implement Comparer + @SuppressWarnings("unchecked") + Comparer comparer = + (Comparer) theClass.getEnumConstants()[0]; + return comparer; + } catch (Throwable t) { // ensure we really catch *everything* + return lexicographicalComparerJavaImpl(); + } + } + + private enum PureJavaComparer implements Comparer { + INSTANCE; + + @Override + public int compareTo(byte[] buffer1, int offset1, int length1, + byte[] buffer2, int offset2, int length2) { + // Short circuit equal case + if (buffer1 == buffer2 && offset1 == offset2 && length1 == length2) { + return 0; + } + + int end1 = offset1 + length1; + int end2 = offset2 + length2; + for (int i = offset1, j = offset2; i < end1 && j < end2; i++, j++) { + int a = (buffer1[i] & 0xff); + int b = (buffer2[j] & 0xff); + if (a != b) { + return a - b; + } + } + + return length1 - length2; + } + } + } +} diff --git a/src/test/java/org/tdf/rlp/RLPTest.java b/src/test/java/org/tdf/rlp/RLPTest.java index 3408c20..df6c004 100644 --- a/src/test/java/org/tdf/rlp/RLPTest.java +++ b/src/test/java/org/tdf/rlp/RLPTest.java @@ -1390,6 +1390,7 @@ public void testEncodeSetSuccess() { hasSorted = false; break; } + i++; } assert !hasSorted; RLPElement el = RLPElement.readRLPTree(w1); @@ -1416,17 +1417,77 @@ public void testContainer() throws Exception { public static class MapWrapper2 { @RLP @RLPDecoding(as = TreeMap.class) + @RLPEncoding(keyOrdering = StringComparator.class) public Map> map = new HashMap<>(); } @Test public void testMapWrapper2() { MapWrapper2 wrapper2 = new MapWrapper2(); + wrapper2.map.put("1", new HashMap<>()); + wrapper2.map.put("22", new HashMap<>()); wrapper2.map.put("sss", new HashMap<>()); wrapper2.map.get("sss").put("aaa", "bbb"); + boolean hasSorted = true; + int i = 1; + for (String k : wrapper2.map.keySet()) { + if (k.length() != i) { + hasSorted = false; + break; + } + i++; + } + assert !hasSorted; byte[] encoded = RLPCodec.encode(wrapper2); + RLPElement el = RLPElement.readRLPTree(wrapper2); + for (int j = 0; j < 3; j++) { + assert el.get(0).get(j * 2).asString().length() == j + 1; + } MapWrapper2 decoded = RLPCodec.decode(encoded, MapWrapper2.class); assert decoded.map instanceof TreeMap; assert decoded.map.get("sss").get("aaa").equals("bbb"); } + + private static class ByteArraySetWrapper { + @RLP + @RLPDecoding(as = ByteArraySet.class) + @RLPEncoding(contentOrdering = BytesComparator.class) + private Set bytesSet; + } + + private static class BytesComparator implements Comparator { + @Override + public int compare(byte[] o1, byte[] o2) { + return new BigInteger(1, o1).compareTo(new BigInteger(1, o2)); + } + } + + @Test + public void testByteArraySet() { + ByteArraySetWrapper wrapper = + RLPList.of(RLPList.of(RLPItem.fromLong(1), RLPItem.fromLong(2))).as(ByteArraySetWrapper.class); + assert wrapper.bytesSet instanceof ByteArraySet; + + wrapper = new ByteArraySetWrapper(); + wrapper.bytesSet = new HashSet<>(); + wrapper.bytesSet.add(new byte[]{1}); + wrapper.bytesSet.add(new byte[]{2}); + wrapper.bytesSet.add(new byte[]{3}); + wrapper.bytesSet.add(new byte[]{4}); + wrapper.bytesSet.add(new byte[]{5}); + boolean sorted = true; + int i = 0; + for (byte[] b : wrapper.bytesSet) { + if (new BigInteger(1, b).compareTo(BigInteger.valueOf(i + 1)) != 0) { + sorted = false; + break; + } + i++; + } + assert !sorted; + RLPElement el = RLPElement.readRLPTree(wrapper).get(0); + for (int j = 0; j < el.size(); j++) { + assert new BigInteger(1, el.get(j).asBytes()).compareTo(BigInteger.valueOf(j + 1)) == 0; + } + } } diff --git a/src/test/java/org/tdf/rlp/SetAdapter.java b/src/test/java/org/tdf/rlp/SetAdapter.java new file mode 100644 index 0000000..c95749f --- /dev/null +++ b/src/test/java/org/tdf/rlp/SetAdapter.java @@ -0,0 +1,92 @@ +package org.tdf.rlp; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Map wrapper + */ +public class SetAdapter implements Set { + private static final Object DummyValue = new Object(); + Map delegate; + + public SetAdapter(Map delegate) { + this.delegate = (Map) delegate; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return delegate.containsKey(o); + } + + @Override + public Iterator iterator() { + return delegate.keySet().iterator(); + } + + @Override + public Object[] toArray() { + return delegate.keySet().toArray(); + } + + @Override + public T[] toArray(T[] a) { + return delegate.keySet().toArray(a); + } + + @Override + public boolean add(E e) { + return delegate.put(e, DummyValue) == null; + } + + @Override + public boolean remove(Object o) { + return delegate.remove(o) != null; + } + + @Override + public boolean containsAll(Collection c) { + return delegate.keySet().containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + boolean ret = false; + for (E e : c) { + ret |= add(e); + } + return ret; + } + + @Override + public boolean retainAll(Collection c) { + throw new RuntimeException("Not implemented"); // TODO add later if required + } + + @Override + public boolean removeAll(Collection c) { + boolean ret = false; + for (Object e : c) { + ret |= remove(e); + } + return ret; + } + + @Override + public void clear() { + delegate.clear(); + } +} + From 8194d3ad3ea69e0822f6daa60a7cf7efa26ff179 Mon Sep 17 00:00:00 2001 From: zhuyingjie Date: Sat, 14 Dec 2019 00:12:44 +0800 Subject: [PATCH 25/25] add readme.md --- .vscode/settings.json | 3 ++ README.md | 87 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/README.md b/README.md index 4607707..21419a3 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,93 @@ public class Main{ } ``` +- encoding/decoding of Set/Map + +```java +public class Main{ + + private static class ByteArraySetWrapper { + @RLP + @RLPDecoding(as = TreeSet.class) + @RLPEncoding(contentOrdering = BytesComparator.class) + private Set bytesSet; + } + + private static class BytesComparator implements Comparator { + @Override + public int compare(byte[] o1, byte[] o2) { + return new BigInteger(1, o1).compareTo(new BigInteger(1, o2)); + } + } + + public static class MapWrapper2 { + @RLP + @RLPDecoding(as = TreeMap.class) + @RLPEncoding(keyOrdering = StringComparator.class) + public Map> map = new HashMap<>(); + } + + private static class StringComparator implements Comparator { + @Override + public int compare(String o1, String o2) { + return o1.length() - o2.length(); + } + } + + public static void main(String[] args){ + ByteArraySetWrapper wrapper = + RLPList.of(RLPList.of(RLPItem.fromLong(1), RLPItem.fromLong(2))).as(ByteArraySetWrapper.class); + assert wrapper.bytesSet instanceof TreeSet; + + wrapper = new ByteArraySetWrapper(); + wrapper.bytesSet = new HashSet<>(); + wrapper.bytesSet.add(new byte[]{1}); + wrapper.bytesSet.add(new byte[]{2}); + wrapper.bytesSet.add(new byte[]{3}); + wrapper.bytesSet.add(new byte[]{4}); + wrapper.bytesSet.add(new byte[]{5}); + boolean sorted = true; + int i = 0; + for (byte[] b : wrapper.bytesSet) { + if (new BigInteger(1, b).compareTo(BigInteger.valueOf(i + 1)) != 0) { + sorted = false; + break; + } + i++; + } + assert !sorted; + RLPElement el = RLPElement.readRLPTree(wrapper).get(0); + for (int j = 0; j < el.size(); j++) { + assert new BigInteger(1, el.get(j).asBytes()).compareTo(BigInteger.valueOf(j + 1)) == 0; + } + + MapWrapper2 wrapper2 = new MapWrapper2(); + wrapper2.map.put("1", new HashMap<>()); + wrapper2.map.put("22", new HashMap<>()); + wrapper2.map.put("sss", new HashMap<>()); + wrapper2.map.get("sss").put("aaa", "bbb"); + hasSorted = true; + i = 1; + for (String k : wrapper2.map.keySet()) { + if (k.length() != i) { + hasSorted = false; + break; + } + i++; + } + assert !hasSorted; + byte[] encoded = RLPCodec.encode(wrapper2); + el = RLPElement.readRLPTree(wrapper2); + for (int j = 0; j < 3; j++) { + assert el.get(0).get(j * 2).asString().length() == j + 1; + } + MapWrapper2 decoded = RLPCodec.decode(encoded, MapWrapper2.class); + assert decoded.map instanceof TreeMap; + assert decoded.map.get("sss").get("aaa").equals("bbb"); + } +} +``` + Benchmark compare to EthereumJ: decoding list 10000000 times: