Skip to content

Commit

Permalink
full supports of set/map encoding/decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
Salpadding committed Dec 14, 2019
1 parent 9b25f99 commit f2d239f
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 222 deletions.
182 changes: 35 additions & 147 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,144 +74,60 @@ public class Node{
```


- supports Collection, Map and POJO while set and and map is no ordered, generic list could be nested to any deepth
- supports Collection, Map and POJO while set and and map is no ordered, generic could be nested to any deepth

```java
public class Nested{
public static class Tree{
@RLP
private List<List<List<String>>> nested;

public Nested() {
}
@RLPDecoding(as = ConcurrentHashMap.class /* the deserialized tree will be ConcurrentHashMap instead of defualt implementation HashMap*/ )
// map are serialized as RLPList [key, value, key, value, ...]
// use @RLPEncoding.keyOrdering() to specify the ordering of key-value pair
// use @RLPEncoding.contentOrdering() to speficy the ordering of set or anther collection type
public Map<ByteArrayMap<Set<String>>, byte[]> tree;
}
```

```java
public class Main{
public static void main(String[] args){
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"));
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");
Tree tree = new Tree();
tree.tree = new HashMap<>();
tree.tree.put(new ByteArrayMap<>(), "1".getBytes());
tree.tree.keySet().stream()
.findFirst().get()
.put("1".getBytes(), new HashSet<>(Arrays.asList("1", "2", "3")));
byte[] encoded = RLPCodec.encode(tree);
RLPElement el = RLPElement.fromEncoded(encoded, false);
Tree tree1 = RLPCodec.decode(encoded, Tree.class);
assert tree1.tree instanceof ConcurrentHashMap;
ByteArrayMap<Set<String>> tree2 = tree1.tree.keySet().stream()
.findFirst().get();
assert Arrays.equals(tree1.tree.get(tree2), "1".getBytes());
assert tree2.get("1".getBytes()).containsAll(Arrays.asList("1", "2", "3"));
}
}
```

- encoding/decoding of Set/Map
- set and map encoding/decoding without wrapper by Container

```java
public class Main{

private static class ByteArraySetWrapper {
@RLP
@RLPDecoding(as = TreeSet.class)
@RLPEncoding(contentOrdering = BytesComparator.class)
private Set<byte[]> bytesSet;
}

private static class BytesComparator implements Comparator<byte[]> {
@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<String, Map<String, String>> map = new HashMap<>();
}

private static class StringComparator implements Comparator<String> {
@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");
// store generic info in a dummy field
private abstract class Dummy2 {
private List<ByteArrayMap<String>> dummy;
}
}
```

- set and map encoding/decoding without wrapper
public static void main(String[] args) throws Exception{
List<ByteArrayMap<String>> list = new ArrayList<>();
list.add(new ByteArrayMap<>());
list.get(0).put("1".getBytes(), "1");

```java
public class Main{
List<Map<byte[], String>> decoded = (List<Map<byte[], String>>) RLPCodec.decodeContainer(
RLPCodec.encode(list),
Container.fromField(Dummy2.class.getDeclaredField("dummy"))
);

public static void main(String[] args){
Map<String, String> map = new HashMap<>();
map.put("key", "value");
byte[] encoded = RLPCodec.encode(map);
TreeMap<String, String> m2 = RLPCodec.decodeMap(encoded,
TreeMap.class, String.class, String.class);
assert m2.get("key").equals("value");


Set<byte[]> set = new HashSet<>();
set.add("1".getBytes());
set.add("2".getBytes());
encoded = RLPCodec.encode(set);
set = RLPCodec.decodeCollection(encoded, ByteArraySet.class, byte[].class);
ByteArraySet s2 = RLPCodec.decodeCollection(encoded, ByteArraySet.class, byte[].class);
assert set instanceof ByteArraySet;
assert set.contains("1".getBytes());
assert set.contains("2".getBytes());
assert decoded.get(0).get("1".getBytes()).equals("1");
}
}
```
Expand Down Expand Up @@ -276,35 +192,7 @@ public class Main{
}

```

- decode use Container

```java

public class Main{

public static void main(String[] args){
List<Set<byte[]>> list = new ArrayList<>();
list.add(new ByteArraySet());
list.get(0).add("1".getBytes());
Container container = CollectionContainer.builder()
.collectionType(ArrayList.class)
.contentType(
CollectionContainer.builder()
.collectionType(ByteArraySet.class)
.contentType(new Raw(byte[].class))
.build()
)
.build();
List<Set<byte[]>> decoded = (List<Set<byte[]>>) RLPCodec.decodeContainer(
RLPCodec.encode(list),
container
);
assert decoded.get(0).contains("1".getBytes());
}
}

```


Benchmark compare to EthereumJ:

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group 'org.tdf'
version '1.1.9'
version '1.1.10'

sourceCompatibility = 1.8

Expand Down
69 changes: 42 additions & 27 deletions src/main/java/org/tdf/rlp/Container.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,58 +14,73 @@ static Container<?> fromField(Field field) {
clazz = field.getAnnotation(RLPDecoding.class).as();
}

if(clazz == null || clazz == Void.class) return container;
if(container.getType() == ContainerType.RAW)
if (clazz == null || clazz == Void.class) return container;
if (container.getType() == ContainerType.RAW)
throw new RuntimeException("@RLPDecoding.as is used on collection or map type");
if(!field.getType().isAssignableFrom(clazz))
if (!field.getType().isAssignableFrom(clazz))
throw new RuntimeException("cannot assign " + clazz + " as " + field.getType());
if(container.getType() == ContainerType.COLLECTION){
if (container.getType() == ContainerType.COLLECTION) {
container.asCollection().collectionType = clazz;
}
if(container.getType() == ContainerType.MAP){
if (container.getType() == ContainerType.MAP) {
container.asMap().mapType = clazz;
}
return container;
}

static Container fromClass(Class clazz){
if(Collection.class.isAssignableFrom(clazz)){
static Container fromNoGeneric(Class clazz) {
if (Collection.class.isAssignableFrom(clazz)) {
CollectionContainer con = new CollectionContainer();
con.contentType = new Raw(RLPElement.class);
Class contentType = RLPUtils.getGenericTypeRecursively(clazz, 0);
con.contentType = contentType == null ? null : new Raw(contentType);
con.collectionType = clazz;
return con;
}
if(Map.class.isAssignableFrom(clazz)){
if (Map.class.isAssignableFrom(clazz)) {
MapContainer con = new MapContainer();
con.keyType = new Raw(RLPElement.class);
con.valueType = new Raw(RLPElement.class);
Class keyType = RLPUtils.getGenericTypeRecursively(clazz, 0);
con.keyType = keyType == null ? null : new Raw(keyType);
Class valueType = RLPUtils.getGenericTypeRecursively(clazz, 1);
con.valueType = valueType == null ? null : new Raw(valueType);
con.mapType = clazz;
return con;
}
return new Raw(clazz);
}

static Container fromGeneric(Type type) {
if (!(type instanceof ParameterizedType)) {
Class clazz = (Class<?>) type;
return fromClass(clazz);
if (type instanceof Class) {
return fromNoGeneric((Class) type);
}
if (!(type instanceof ParameterizedType)) throw new RuntimeException(type + " is not allowed in rlp decoding");
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] types = parameterizedType.getActualTypeArguments();
Class clazz = (Class) parameterizedType.getRawType();
if (Collection.class.isAssignableFrom(clazz)) {
CollectionContainer con = new CollectionContainer();
con.contentType = fromGeneric(parameterizedType.getActualTypeArguments()[0]);
con.collectionType = clazz;
return con;
}
if (Map.class.isAssignableFrom(clazz)) {
MapContainer con = new MapContainer();
con.keyType = fromGeneric(parameterizedType.getActualTypeArguments()[0]);
con.valueType = fromGeneric(parameterizedType.getActualTypeArguments()[1]);
con.mapType = clazz;
return con;
Container container = fromNoGeneric(clazz);
switch (container.getType()) {
case RAW:
return container;
case MAP: {
MapContainer con = container.asMap();
int i = 0;
if (con.keyType == null) {
con.keyType = i < types.length ? fromGeneric(types[i++]) : null;
}
if(con.valueType == null){
con.valueType = i < types.length ? fromGeneric(types[i++]) : null;
}
return con;
}
case COLLECTION: {
CollectionContainer con = container.asCollection();
if (con.contentType == null) {
con.contentType = 0 < types.length ? fromGeneric(types[0]) : null;
}
return con;
}
default:
throw new RuntimeException("this is unreachable");
}
return fromClass(clazz);
}

ContainerType getType();
Expand Down
Loading

0 comments on commit f2d239f

Please sign in to comment.