Skip to content

Commit 89c82a1

Browse files
committed
Plumbing for extensible encode reading
1 parent b7a6371 commit 89c82a1

File tree

5 files changed

+177
-16
lines changed

5 files changed

+177
-16
lines changed

convex-core/src/main/java/convex/core/data/AEncoder.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ public abstract class AEncoder<T> {
1919
*/
2020
public abstract Blob encode(T a);
2121

22-
public abstract T decode(Blob encoding) throws BadFormatException;
22+
public T decode(Blob encoding) throws BadFormatException {
23+
return read(encoding,0);
24+
}
25+
26+
public abstract T read(Blob encoding, int offset) throws BadFormatException;
2327

2428
/**
2529
* Reads a value from a Blob of data

convex-core/src/main/java/convex/core/data/CAD3Encoder.java

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,159 @@
11
package convex.core.data;
22

3+
import convex.core.data.prim.AByteFlag;
4+
import convex.core.data.prim.ANumeric;
5+
import convex.core.data.prim.CVMBigInteger;
6+
import convex.core.data.prim.CVMChar;
7+
import convex.core.data.prim.CVMDouble;
8+
import convex.core.data.prim.CVMLong;
39
import convex.core.exceptions.BadFormatException;
10+
import convex.core.util.ErrorMessages;
411

512
/**
6-
* Base Encoder for CAD3 data / stores
13+
* Base Encoder for CAD3 data / stores.
14+
*
15+
* Does NOT directly decode custom CVM value types. Use the derived CVMEncoder if you need that behaviour.
716
*/
817
public class CAD3Encoder extends AEncoder<ACell> {
918

1019
public Blob encode(ACell a) {
1120
return Cells.encode(a);
1221
}
1322

23+
@Override
1424
public ACell decode(Blob encoding) throws BadFormatException {
15-
return Format.read(encoding);
25+
if (encoding.count()<1) throw new BadFormatException("Empty encoding");
26+
return read(encoding,0);
27+
}
28+
29+
@Override
30+
public ACell read(Blob encoding, int offset) throws BadFormatException {
31+
byte tag = encoding.byteAt(offset);
32+
ACell result= read(tag,encoding,offset);
33+
return result;
34+
}
35+
36+
protected ACell read(byte tag, Blob encoding, int offset) throws BadFormatException {
37+
switch (tag>>4) {
38+
case 0: // 0x00-0x0F : Only null is valid
39+
if (tag==Tag.NULL) return null;
40+
break;
41+
42+
case 1: // 0x10-0x1F : Numeric values
43+
return readNumeric(tag,encoding,offset);
44+
45+
case 2: // 0x20-0x2F : Addresses and references
46+
if (tag == Tag.ADDRESS) return Address.read(encoding,offset);
47+
// Note: 0x20 reference is invalid as a top level encoding
48+
break;
49+
50+
case 3: // 0x30-0x3F : BAsic string / blob-like objects
51+
return readBasicObject(tag, encoding, offset);
52+
53+
case 4: case 5: case 6: case 7: // 0x40-0x7F currently reserved
54+
break;
55+
56+
case 8: // 0x80-0x8F : BAsic string / blob-like objects
57+
return readDataStructure(tag, encoding, offset);
58+
59+
case 9: // 0x90-0x9F : Crypto / signature objects
60+
return readSignedData(tag,encoding, offset);
61+
62+
case 10: // 0xA0-0xAF : Sparse records. A is for Airy.
63+
return readSparseRecord(tag,encoding,offset);
64+
65+
case 11: // 0xB0-0xBF : Byte flags including booleans
66+
return AByteFlag.read(tag);
67+
68+
case 12: // 0xC0-0xCF : Coded data objects
69+
return readCodedData(tag,encoding, offset);
70+
71+
case 13: // 0xD0-0xDF : Dense records
72+
return readDenseRecord(tag,encoding, offset);
73+
74+
case 14: // 0xE0-0xEF : Extension values
75+
return readExtension(tag,encoding, offset);
76+
77+
case 15: // 0xF0-0xFF : Reserved / failure Values
78+
break;
79+
}
80+
81+
return Format.read(tag,encoding,offset);
82+
// throw new BadFormatException(ErrorMessages.badTagMessage(tag));
83+
}
84+
85+
86+
protected ANumeric readNumeric(byte tag, Blob blob, int offset) throws BadFormatException {
87+
if (tag<0x19) return CVMLong.read(tag,blob,offset);
88+
if (tag == 0x19) return CVMBigInteger.read(blob,offset);
89+
if (tag == Tag.DOUBLE) return CVMDouble.read(tag,blob,offset);
90+
91+
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
92+
}
93+
94+
protected ACell readBasicObject(byte tag, Blob blob, int offset) throws BadFormatException{
95+
switch (tag) {
96+
case Tag.SYMBOL: return Symbol.read(blob,offset);
97+
case Tag.KEYWORD: return Keyword.read(blob,offset);
98+
case Tag.BLOB: return Blobs.read(blob,offset);
99+
case Tag.STRING: return Strings.read(blob,offset);
100+
}
101+
102+
if ((tag&Tag.CHAR_MASK)==Tag.CHAR_BASE) {
103+
int len=CVMChar.byteCountFromTag(tag);
104+
if (len>4) throw new BadFormatException("Can't read char type with length: " + len);
105+
return CVMChar.read(len, blob,offset); // skip tag byte
106+
}
107+
108+
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
109+
}
110+
111+
protected ACell readDataStructure(byte tag, Blob b, int pos) throws BadFormatException {
112+
if (tag == Tag.VECTOR) return Vectors.read(b,pos);
113+
114+
if (tag == Tag.MAP) return Maps.read(b,pos);
115+
116+
if (tag == Tag.SYNTAX) return Syntax.read(b,pos);
117+
118+
if (tag == Tag.SET) return Sets.read(b,pos);
119+
120+
if (tag == Tag.LIST) return List.read(b,pos);
121+
122+
if (tag == Tag.INDEX) return Index.read(b,pos);
123+
124+
throw new BadFormatException("Can't read data structure with tag byte: " + tag);
125+
}
126+
127+
protected SignedData<?> readSignedData(byte tag,Blob blob, int offset) throws BadFormatException {
128+
if (tag==Tag.SIGNED_DATA) return SignedData.read(blob,offset,true);
129+
if (tag==Tag.SIGNED_DATA_SHORT) return SignedData.read(blob,offset,false);
130+
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
131+
}
132+
133+
protected ARecord readSparseRecord(byte tag, Blob encoding, int offset) throws BadFormatException {
134+
// TODO spare records
135+
throw new BadFormatException(ErrorMessages.TODO);
16136
}
17137

138+
protected ACell readCodedData(byte tag, Blob encoding, int offset) throws BadFormatException {
139+
// TODO Change delegation to proper read
140+
return Format.read(tag,encoding,offset);
141+
}
142+
143+
protected ACell readDenseRecord(byte tag, Blob encoding, int offset) throws BadFormatException {
144+
// TODO Change delegation to proper read
145+
return Format.read(tag,encoding,offset);
146+
}
147+
148+
protected ACell readExtension(byte tag, Blob blob, int offset) throws BadFormatException {
149+
// We expect a VLQ Count following the tag
150+
long code=Format.readVLQCount(blob,offset+1);
151+
return ExtensionValue.create(tag, code);
152+
}
153+
154+
155+
156+
18157
/**
19158
* Reads a cell value from a Blob of data, allowing for non-embedded branches following the first cell
20159
* @param data Data to decode
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
package convex.core.data;
22

3+
import convex.core.exceptions.BadFormatException;
4+
import convex.core.lang.Core;
5+
6+
/**
7+
* Encoder for CVM values and data structures
8+
*/
39
public class CVMEncoder extends CAD3Encoder {
410

511
public static final CVMEncoder INSTANCE = new CVMEncoder();
612

13+
@Override
14+
public ACell read(Blob encoding,int offset) throws BadFormatException {
15+
return super.read(encoding,offset);
16+
}
717

8-
18+
protected ACell readExtension(byte tag, Blob blob, int offset) throws BadFormatException {
19+
// We expect a VLQ Count following the tag
20+
long code=Format.readVLQCount(blob,offset+1);
21+
if (tag == Tag.CORE_DEF) return Core.fromCode(code);
22+
23+
return ExtensionValue.create(tag, code);
24+
}
925
}

convex-core/src/main/java/convex/core/data/Format.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import convex.core.store.AStore;
4040
import convex.core.store.Stores;
4141
import convex.core.util.Bits;
42+
import convex.core.util.ErrorMessages;
4243
import convex.core.util.Trees;
4344
import convex.core.util.Utils;
4445

@@ -580,7 +581,7 @@ public static <T extends ACell> T read(String hexString) throws BadFormatExcepti
580581
* @throws BadFormatException If encoding is invalid for the given tag
581582
*/
582583
@SuppressWarnings("unchecked")
583-
private static <T extends ACell> T read(byte tag, Blob blob, int offset) throws BadFormatException {
584+
static <T extends ACell> T read(byte tag, Blob blob, int offset) throws BadFormatException {
584585

585586
// Fast paths for common one-byte instances. TODO: might switch have better performance if compiled correctly into a table?
586587
if (tag==Tag.NULL) return null;
@@ -616,27 +617,22 @@ private static <T extends ACell> T read(byte tag, Blob blob, int offset) throws
616617
} catch (Exception e) {
617618
throw new BadFormatException("Unexpected Exception when decoding ("+tag+"): "+e.getMessage(), e);
618619
}
619-
throw new BadFormatException(badTagMessage(tag));
620+
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
620621
}
621622

622623

623624
private static <T extends ACell> SignedData<T> readSignedData(byte tag,Blob blob, int offset) throws BadFormatException {
624625
if (tag==Tag.SIGNED_DATA) return SignedData.read(blob,offset,true);
625626
if (tag==Tag.SIGNED_DATA_SHORT) return SignedData.read(blob,offset,false);
626-
throw new BadFormatException(badTagMessage(tag));
627-
}
628-
629-
private static String badTagMessage(byte tag) {
630-
return "Unrecognised tag byte 0x"+Utils.toHexString(tag);
627+
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
631628
}
632629

633630
private static ANumeric readNumeric(byte tag, Blob blob, int offset) throws BadFormatException {
634-
// TODO Auto-generated method stub
635631
if (tag<0x19) return CVMLong.read(tag,blob,offset);
636632
if (tag == 0x19) return CVMBigInteger.read(blob,offset);
637633
if (tag == Tag.DOUBLE) return CVMDouble.read(tag,blob,offset);
638634

639-
throw new BadFormatException(badTagMessage(tag));
635+
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
640636
}
641637

642638
private static ACell readBasicObject(byte tag, Blob blob, int offset) throws BadFormatException{
@@ -653,7 +649,7 @@ private static ACell readBasicObject(byte tag, Blob blob, int offset) throws Ba
653649
return CVMChar.read(len, blob,offset); // skip tag byte
654650
}
655651

656-
throw new BadFormatException(badTagMessage(tag));
652+
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
657653
}
658654

659655

@@ -691,7 +687,7 @@ private static <T extends ARecord> T readRecord(byte tag, Blob b, int pos) throw
691687
if (tag == Tag.PEER_STATUS) return (T) PeerStatus.read(b,pos);
692688
if (tag == Tag.ACCOUNT_STATUS) return (T) AccountStatus.read(b,pos);
693689

694-
throw new BadFormatException(badTagMessage(tag));
690+
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
695691
}
696692

697693
@SuppressWarnings("unchecked")
@@ -712,7 +708,7 @@ private static <T extends ACell> T readTransaction(byte tag, Blob b, int pos) th
712708

713709
// Might be a generic Dense Record
714710
DenseRecord dr=DenseRecord.read(tag,b,pos);
715-
if (dr==null) throw new BadFormatException(badTagMessage(tag));
711+
if (dr==null) throw new BadFormatException(ErrorMessages.badTagMessage(tag));
716712
return (T) dr;
717713
}
718714

convex-core/src/main/java/convex/core/util/ErrorMessages.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public class ErrorMessages {
1919

2020

2121

22+
public static final String TODO = "Not yet implemented.";
23+
2224
public static String immutable(Object a) {
2325
return "Object is immutable: "+a.getClass();
2426
}
@@ -61,4 +63,8 @@ public static ErrorValue nobody(Address address) {
6163

6264
public static ErrorValue INVALID_NUMERIC = ErrorValue.create(ErrorCodes.ARGUMENT,"Invalid numeric result");
6365

66+
public static String badTagMessage(byte tag) {
67+
return "Unrecognised tag byte 0x"+Utils.toHexString(tag);
68+
}
69+
6470
}

0 commit comments

Comments
 (0)