Skip to content

Commit

Permalink
Plumbing for extensible encode reading
Browse files Browse the repository at this point in the history
  • Loading branch information
mikera committed Nov 28, 2024
1 parent b7a6371 commit 89c82a1
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 16 deletions.
6 changes: 5 additions & 1 deletion convex-core/src/main/java/convex/core/data/AEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ public abstract class AEncoder<T> {
*/
public abstract Blob encode(T a);

public abstract T decode(Blob encoding) throws BadFormatException;
public T decode(Blob encoding) throws BadFormatException {
return read(encoding,0);
}

public abstract T read(Blob encoding, int offset) throws BadFormatException;

/**
* Reads a value from a Blob of data
Expand Down
143 changes: 141 additions & 2 deletions convex-core/src/main/java/convex/core/data/CAD3Encoder.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,159 @@
package convex.core.data;

import convex.core.data.prim.AByteFlag;
import convex.core.data.prim.ANumeric;
import convex.core.data.prim.CVMBigInteger;
import convex.core.data.prim.CVMChar;
import convex.core.data.prim.CVMDouble;
import convex.core.data.prim.CVMLong;
import convex.core.exceptions.BadFormatException;
import convex.core.util.ErrorMessages;

/**
* Base Encoder for CAD3 data / stores
* Base Encoder for CAD3 data / stores.
*
* Does NOT directly decode custom CVM value types. Use the derived CVMEncoder if you need that behaviour.
*/
public class CAD3Encoder extends AEncoder<ACell> {

public Blob encode(ACell a) {
return Cells.encode(a);
}

@Override
public ACell decode(Blob encoding) throws BadFormatException {
return Format.read(encoding);
if (encoding.count()<1) throw new BadFormatException("Empty encoding");
return read(encoding,0);
}

@Override
public ACell read(Blob encoding, int offset) throws BadFormatException {
byte tag = encoding.byteAt(offset);
ACell result= read(tag,encoding,offset);
return result;
}

protected ACell read(byte tag, Blob encoding, int offset) throws BadFormatException {
switch (tag>>4) {
case 0: // 0x00-0x0F : Only null is valid
if (tag==Tag.NULL) return null;
break;

case 1: // 0x10-0x1F : Numeric values
return readNumeric(tag,encoding,offset);

case 2: // 0x20-0x2F : Addresses and references
if (tag == Tag.ADDRESS) return Address.read(encoding,offset);
// Note: 0x20 reference is invalid as a top level encoding
break;

case 3: // 0x30-0x3F : BAsic string / blob-like objects
return readBasicObject(tag, encoding, offset);

case 4: case 5: case 6: case 7: // 0x40-0x7F currently reserved
break;

case 8: // 0x80-0x8F : BAsic string / blob-like objects
return readDataStructure(tag, encoding, offset);

case 9: // 0x90-0x9F : Crypto / signature objects
return readSignedData(tag,encoding, offset);

case 10: // 0xA0-0xAF : Sparse records. A is for Airy.
return readSparseRecord(tag,encoding,offset);

case 11: // 0xB0-0xBF : Byte flags including booleans
return AByteFlag.read(tag);

case 12: // 0xC0-0xCF : Coded data objects
return readCodedData(tag,encoding, offset);

case 13: // 0xD0-0xDF : Dense records
return readDenseRecord(tag,encoding, offset);

case 14: // 0xE0-0xEF : Extension values
return readExtension(tag,encoding, offset);

case 15: // 0xF0-0xFF : Reserved / failure Values
break;
}

return Format.read(tag,encoding,offset);
// throw new BadFormatException(ErrorMessages.badTagMessage(tag));
}


protected ANumeric readNumeric(byte tag, Blob blob, int offset) throws BadFormatException {
if (tag<0x19) return CVMLong.read(tag,blob,offset);
if (tag == 0x19) return CVMBigInteger.read(blob,offset);
if (tag == Tag.DOUBLE) return CVMDouble.read(tag,blob,offset);

throw new BadFormatException(ErrorMessages.badTagMessage(tag));
}

protected ACell readBasicObject(byte tag, Blob blob, int offset) throws BadFormatException{
switch (tag) {
case Tag.SYMBOL: return Symbol.read(blob,offset);
case Tag.KEYWORD: return Keyword.read(blob,offset);
case Tag.BLOB: return Blobs.read(blob,offset);
case Tag.STRING: return Strings.read(blob,offset);
}

if ((tag&Tag.CHAR_MASK)==Tag.CHAR_BASE) {
int len=CVMChar.byteCountFromTag(tag);
if (len>4) throw new BadFormatException("Can't read char type with length: " + len);
return CVMChar.read(len, blob,offset); // skip tag byte
}

throw new BadFormatException(ErrorMessages.badTagMessage(tag));
}

protected ACell readDataStructure(byte tag, Blob b, int pos) throws BadFormatException {
if (tag == Tag.VECTOR) return Vectors.read(b,pos);

if (tag == Tag.MAP) return Maps.read(b,pos);

if (tag == Tag.SYNTAX) return Syntax.read(b,pos);

if (tag == Tag.SET) return Sets.read(b,pos);

if (tag == Tag.LIST) return List.read(b,pos);

if (tag == Tag.INDEX) return Index.read(b,pos);

throw new BadFormatException("Can't read data structure with tag byte: " + tag);
}

protected SignedData<?> readSignedData(byte tag,Blob blob, int offset) throws BadFormatException {
if (tag==Tag.SIGNED_DATA) return SignedData.read(blob,offset,true);
if (tag==Tag.SIGNED_DATA_SHORT) return SignedData.read(blob,offset,false);
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
}

protected ARecord readSparseRecord(byte tag, Blob encoding, int offset) throws BadFormatException {
// TODO spare records
throw new BadFormatException(ErrorMessages.TODO);
}

protected ACell readCodedData(byte tag, Blob encoding, int offset) throws BadFormatException {
// TODO Change delegation to proper read
return Format.read(tag,encoding,offset);
}

protected ACell readDenseRecord(byte tag, Blob encoding, int offset) throws BadFormatException {
// TODO Change delegation to proper read
return Format.read(tag,encoding,offset);
}

protected ACell readExtension(byte tag, Blob blob, int offset) throws BadFormatException {
// We expect a VLQ Count following the tag
long code=Format.readVLQCount(blob,offset+1);
return ExtensionValue.create(tag, code);
}




/**
* Reads a cell value from a Blob of data, allowing for non-embedded branches following the first cell
* @param data Data to decode
Expand Down
18 changes: 17 additions & 1 deletion convex-core/src/main/java/convex/core/data/CVMEncoder.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
package convex.core.data;

import convex.core.exceptions.BadFormatException;
import convex.core.lang.Core;

/**
* Encoder for CVM values and data structures
*/
public class CVMEncoder extends CAD3Encoder {

public static final CVMEncoder INSTANCE = new CVMEncoder();

@Override
public ACell read(Blob encoding,int offset) throws BadFormatException {
return super.read(encoding,offset);
}


protected ACell readExtension(byte tag, Blob blob, int offset) throws BadFormatException {
// We expect a VLQ Count following the tag
long code=Format.readVLQCount(blob,offset+1);
if (tag == Tag.CORE_DEF) return Core.fromCode(code);

return ExtensionValue.create(tag, code);
}
}
20 changes: 8 additions & 12 deletions convex-core/src/main/java/convex/core/data/Format.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import convex.core.store.AStore;
import convex.core.store.Stores;
import convex.core.util.Bits;
import convex.core.util.ErrorMessages;
import convex.core.util.Trees;
import convex.core.util.Utils;

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

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


private static <T extends ACell> SignedData<T> readSignedData(byte tag,Blob blob, int offset) throws BadFormatException {
if (tag==Tag.SIGNED_DATA) return SignedData.read(blob,offset,true);
if (tag==Tag.SIGNED_DATA_SHORT) return SignedData.read(blob,offset,false);
throw new BadFormatException(badTagMessage(tag));
}

private static String badTagMessage(byte tag) {
return "Unrecognised tag byte 0x"+Utils.toHexString(tag);
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
}

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

throw new BadFormatException(badTagMessage(tag));
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
}

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

throw new BadFormatException(badTagMessage(tag));
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
}


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

throw new BadFormatException(badTagMessage(tag));
throw new BadFormatException(ErrorMessages.badTagMessage(tag));
}

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

// Might be a generic Dense Record
DenseRecord dr=DenseRecord.read(tag,b,pos);
if (dr==null) throw new BadFormatException(badTagMessage(tag));
if (dr==null) throw new BadFormatException(ErrorMessages.badTagMessage(tag));
return (T) dr;
}

Expand Down
6 changes: 6 additions & 0 deletions convex-core/src/main/java/convex/core/util/ErrorMessages.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class ErrorMessages {



public static final String TODO = "Not yet implemented.";

public static String immutable(Object a) {
return "Object is immutable: "+a.getClass();
}
Expand Down Expand Up @@ -61,4 +63,8 @@ public static ErrorValue nobody(Address address) {

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

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

}

0 comments on commit 89c82a1

Please sign in to comment.