From 89490c66f3e417d4d4ce25272f6e3e2490e9c446 Mon Sep 17 00:00:00 2001 From: mikera Date: Sat, 12 Oct 2024 14:55:17 +0100 Subject: [PATCH] Add CAD3 Extension values 0xE0 - 0xEF --- .../main/java/convex/core/data/ABlobLike.java | 3 +- .../src/main/java/convex/core/data/ACell.java | 4 +- .../src/main/java/convex/core/data/ACode.java | 27 ---- .../convex/core/data/AExtensionValue.java | 129 ++++++++++++++++++ .../main/java/convex/core/data/Address.java | 93 +------------ .../java/convex/core/data/ExtensionValue.java | 105 ++++++++++++++ .../main/java/convex/core/data/Format.java | 2 +- .../src/main/java/convex/core/data/Tag.java | 3 +- .../convex/core/data/util/BlobBuilder.java | 13 ++ .../test/java/convex/core/data/CAD3Test.java | 18 +++ 10 files changed, 276 insertions(+), 121 deletions(-) delete mode 100644 convex-core/src/main/java/convex/core/data/ACode.java create mode 100644 convex-core/src/main/java/convex/core/data/AExtensionValue.java create mode 100644 convex-core/src/main/java/convex/core/data/ExtensionValue.java create mode 100644 convex-core/src/test/java/convex/core/data/CAD3Test.java diff --git a/convex-core/src/main/java/convex/core/data/ABlobLike.java b/convex-core/src/main/java/convex/core/data/ABlobLike.java index 81cb62ebb..f526eca75 100644 --- a/convex-core/src/main/java/convex/core/data/ABlobLike.java +++ b/convex-core/src/main/java/convex/core/data/ABlobLike.java @@ -188,6 +188,5 @@ public Blob toFlatBlob() { */ public abstract boolean equalsBytes(ABlob b); - @Override - public abstract int compareTo(ABlobLike b); + } diff --git a/convex-core/src/main/java/convex/core/data/ACell.java b/convex-core/src/main/java/convex/core/data/ACell.java index eb730b2e2..a90f9a965 100644 --- a/convex-core/src/main/java/convex/core/data/ACell.java +++ b/convex-core/src/main/java/convex/core/data/ACell.java @@ -165,7 +165,7 @@ protected static boolean genericEquals(ACell a, ACell b) { } /** - * Writes this Cell's encoding to a byte array, including a tag byte which will be written first. + * Writes this Cell's CAD3 encoding to a byte array, including the tag byte which will be written first. * * Cell must be canonical, or else an error may occur. * @@ -177,7 +177,7 @@ protected static boolean genericEquals(ACell a, ACell b) { public abstract int encode(byte[] bs, int pos); /** - * Writes this Cell's encoding to a byte array, excluding the tag byte. + * Writes this Cell's CAD3 encoding to a byte array, excluding the tag byte. * * @param bs A byte array to which to write the encoding * @param pos The offset into the byte array diff --git a/convex-core/src/main/java/convex/core/data/ACode.java b/convex-core/src/main/java/convex/core/data/ACode.java deleted file mode 100644 index 4a067a8d2..000000000 --- a/convex-core/src/main/java/convex/core/data/ACode.java +++ /dev/null @@ -1,27 +0,0 @@ -package convex.core.data; - -import convex.core.data.impl.ALongBlob; -import convex.core.util.Utils; -import convex.core.exceptions.InvalidDataException; - -/** - * Abstract base class for CVM code data types - */ -public abstract class ACode extends ALongBlob { - - protected byte tag; - - protected ACode(long value) { - super(value); - } - - @Override - public void validateCell() throws InvalidDataException { - if ((byte)(this.tag&0xF0)!=Tag.CODE_BASE) { - throw new InvalidDataException("Invalide Code tag: 0x"+Utils.toHexString(tag),this); - } - if (this.value<0) { - throw new InvalidDataException("Negaitive code value",this); - } - } -} diff --git a/convex-core/src/main/java/convex/core/data/AExtensionValue.java b/convex-core/src/main/java/convex/core/data/AExtensionValue.java new file mode 100644 index 000000000..0453f2d7c --- /dev/null +++ b/convex-core/src/main/java/convex/core/data/AExtensionValue.java @@ -0,0 +1,129 @@ +package convex.core.data; + +import convex.core.data.impl.LongBlob; +import convex.core.data.prim.CVMLong; +import convex.core.util.ErrorMessages; +import convex.core.util.Utils; + +/** + * Abstract base class for 8-byte bloblike extension Values such as Address and CAD Extension Values + */ +public abstract class AExtensionValue extends ABlobLike { + + /** + * Length of an Address in bytes (when considered as a Blob) + */ + static final int BYTE_LENGTH = 8; + + @Override + public final long count() { + return BYTE_LENGTH; + } + + @Override + public int estimatedEncodingSize() { + // tag VLC bytes + return 1 + Format.MAX_VLQ_COUNT_LENGTH; + } + + @Override + public Blob slice(long start, long end) { + return toFlatBlob().slice(start,end); + } + + @Override + public ABlob toBlob() { + return LongBlob.create(longValue()); + } + + protected static void checkIndex(long i) { + if ((i < 0) || (i >= BYTE_LENGTH)) throw new IndexOutOfBoundsException(ErrorMessages.badIndex(i)); + } + + @Override + public Blob toFlatBlob() { + byte[] bs=new byte[BYTE_LENGTH]; + getBytes(bs,0); + return Blob.wrap(bs); + } + + @Override + protected ACell toCanonical() { + // Always canonical + return this; + } + + @Override + public long hexMatch(ABlobLike b, long start, long length) { + for (int i=0; i b) { + if (b.count()==BYTE_LENGTH) { + return compareTo(b.longValue()); + } else { + // safe because must be a different type + return -b.compareTo(this); + } + } + + @Override + public AExtensionValue empty() { + // There is no empty extension value + return null; + } + + @Override + protected final long calcMemorySize() { + // always embedded and no child Refs, so memory size == 0 + return 0; + } + + @Override + public boolean equals(ACell a) { + if (a instanceof AExtensionValue) return equals((AExtensionValue) a); + return false; + } + + protected boolean equals(AExtensionValue a) { + if (getTag()!=a.getTag()) return false; + return longValue()==a.longValue(); + } + + @Override + public CVMLong get(long i) { + checkIndex(i); + return CVMLong.create(Utils.longByteAt(longValue(),i)); + } + + @Override + public Ref getRef(int i) { + throw new IndexOutOfBoundsException(i); + } + + @Override + public ACell updateRefs(IRefFunction func) { + return this; + } + + @Override + public final int getRefCount() { + // No Refs + return 0; + } +} diff --git a/convex-core/src/main/java/convex/core/data/Address.java b/convex-core/src/main/java/convex/core/data/Address.java index 088e02806..20408e33b 100644 --- a/convex-core/src/main/java/convex/core/data/Address.java +++ b/convex-core/src/main/java/convex/core/data/Address.java @@ -1,6 +1,5 @@ package convex.core.data; -import convex.core.data.impl.LongBlob; import convex.core.data.prim.CVMLong; import convex.core.data.type.AType; import convex.core.data.type.Types; @@ -8,8 +7,6 @@ import convex.core.exceptions.BadFormatException; import convex.core.exceptions.InvalidDataException; import convex.core.lang.RT; -import convex.core.util.Bits; -import convex.core.util.ErrorMessages; import convex.core.util.Utils; /** @@ -19,7 +16,7 @@ * serves as an index into the vector of accounts for the current state. * */ -public final class Address extends ABlobLike { +public final class Address extends AExtensionValue { public static final int LENGTH=8; @@ -41,11 +38,6 @@ public final class Address extends ABlobLike { */ public static final Address MAX_VALUE = Address.create(Long.MAX_VALUE); - /** - * Length of an Address in bytes (when considered as a Blob) - */ - static final int BYTE_LENGTH = 8; - /** * 64-bit address value */ @@ -93,12 +85,7 @@ public static Address create(ABlobLike b) { public AType getType() { return Types.ADDRESS; } - - @Override - public int hashCode() { - return Bits.hash32(value); - } - + @Override public boolean equals(Object a) { if (a==this) return true; // Fast path, avoids cast @@ -222,22 +209,11 @@ public String toString() { return "#"+value; } - @Override - public int estimatedEncodingSize() { - // tag VLC bytes - return 1 + Format.MAX_VLQ_COUNT_LENGTH; - } - @Override public void validateCell() throws InvalidDataException { if (value<0) throw new InvalidDataException("Address must be positive",this); } - @Override - public Blob slice(long start, long end) { - return toFlatBlob().slice(start,end); - } - @Override public Blob toFlatBlob() { byte[] bs=new byte[BYTE_LENGTH]; @@ -277,25 +253,6 @@ public final byte byteAtUnchecked(long i) { return (byte) Utils.longByteAt(value,i); } - private static void checkIndex(long i) { - if ((i < 0) || (i >= LENGTH)) throw new IndexOutOfBoundsException(ErrorMessages.badIndex(i)); - } - - @Override - public long hexMatch(ABlobLike b, long start, long length) { - for (int i=0; i b) { - if (b.count()==LENGTH) { - return compareTo(b.longValue()); - } else { - // safe because must be a different type - return -b.compareTo(this); - } - } - protected int compareTo(long bvalue) { return Long.compareUnsigned(value, bvalue); } @Override - public long count() { - return LENGTH; + public boolean isCVMValue() { + return true; } @Override @@ -345,33 +288,7 @@ public CVMLong get(long i) { @Override public boolean isCanonical() { + // Always canonical, we assume valid by construction return true; } - - @Override - protected long calcMemorySize() { - // always embedded and no child Refs, so memory size == 0 - return 0; - } - - @Override - public boolean isCVMValue() { - return true; - } - - @Override - public Ref getRef(int i) { - throw new IndexOutOfBoundsException(i); - } - - @Override - public ACell updateRefs(IRefFunction func) { - return this; - } - - @Override - public int getRefCount() { - // No Refs - return 0; - } } diff --git a/convex-core/src/main/java/convex/core/data/ExtensionValue.java b/convex-core/src/main/java/convex/core/data/ExtensionValue.java new file mode 100644 index 000000000..31ac7f284 --- /dev/null +++ b/convex-core/src/main/java/convex/core/data/ExtensionValue.java @@ -0,0 +1,105 @@ +package convex.core.data; + +import convex.core.data.util.BlobBuilder; +import convex.core.exceptions.InvalidDataException; +import convex.core.lang.RT; +import convex.core.util.Bits; +import convex.core.util.Utils; + +public class ExtensionValue extends AExtensionValue { + + protected final byte tag; + protected final long value; + + protected ExtensionValue(byte tag,long value) { + this.tag=tag; + this.value=value; + } + + /** + * Create a CAD3 extension value + * @return Extension value, or null if not valid + */ + public static ExtensionValue create(byte tag, long value) { + if (value<0) return null; + if ((byte)(tag&0xF0)!=Tag.EXTENSION_VALUE_BASE) return null; // not an extension value + + return new ExtensionValue(tag,value); + } + + @Override + public void validateCell() throws InvalidDataException { + if ((byte)(this.tag&0xF0)!=Tag.EXTENSION_VALUE_BASE) { + throw new InvalidDataException("Invalide Code tag: 0x"+Utils.toHexString(tag),this); + } + if (this.value<0) { + throw new InvalidDataException("Negaitive code value",this); + } + } + + @Override + public int hashCode() { + return Bits.hash32(value); + } + + @Override + public final byte byteAt(long i) { + checkIndex(i); + return (byte) Utils.longByteAt(value,i); + } + + @Override + public final byte byteAtUnchecked(long i) { + return (byte) Utils.longByteAt(value,i); + } + + @Override + public final int getBytes(byte[] bs, int pos) { + pos=Utils.writeLong(bs, pos, value); + return pos; + } + + @Override + public long longValue() { + return value; + } + + @Override + public byte getTag() { + return tag; + } + + @Override + public boolean isCanonical() { + // Always canonical if valid + return value>=0; + } + + @Override + public int encode(byte[] bs, int pos) { + bs[pos++]=tag; + return encodeRaw(bs,pos); + } + + @Override + public int encodeRaw(byte[] bs, int pos) { + pos=Format.writeVLQCount(bs, pos, value); + return pos; + } + + + @Override + public boolean isCVMValue() { + return false; + } + + @Override + public boolean print(BlobBuilder sb, long limit) { + return RT.printCAD3(sb,limit,this); + } + + + + + +} diff --git a/convex-core/src/main/java/convex/core/data/Format.java b/convex-core/src/main/java/convex/core/data/Format.java index 4e3e30185..59a724aa7 100644 --- a/convex-core/src/main/java/convex/core/data/Format.java +++ b/convex-core/src/main/java/convex/core/data/Format.java @@ -514,7 +514,7 @@ private static ACell readCode(byte tag, Blob b, int pos) throws BadFormatExcepti private static ACell readExtension(byte tag, Blob blob, int offset) throws BadFormatException { if (tag == Tag.CORE_DEF) return Core.read(blob, offset); - throw new BadFormatException(badTagMessage(tag)); + return ExtensionValue.create(tag, readVLQCount(blob,offset+1)); } diff --git a/convex-core/src/main/java/convex/core/data/Tag.java b/convex-core/src/main/java/convex/core/data/Tag.java index dc730daca..bd9bc7af3 100644 --- a/convex-core/src/main/java/convex/core/data/Tag.java +++ b/convex-core/src/main/java/convex/core/data/Tag.java @@ -112,10 +112,11 @@ public class Tag { //========================================== // Extension values (0xEx) + public static final byte EXTENSION_VALUE_BASE = (byte) 0xE0; + // CVM Core definitions public static final byte CORE_DEF = (byte) 0xED; - //=========================================== // Illegal / reserved for special values (0xFx) public static final byte ILLEGAL = (byte) 0xFF; diff --git a/convex-core/src/main/java/convex/core/data/util/BlobBuilder.java b/convex-core/src/main/java/convex/core/data/util/BlobBuilder.java index 7d389e4d9..acafd7f70 100644 --- a/convex-core/src/main/java/convex/core/data/util/BlobBuilder.java +++ b/convex-core/src/main/java/convex/core/data/util/BlobBuilder.java @@ -5,6 +5,7 @@ import convex.core.data.ABlob; import convex.core.data.AString; import convex.core.data.Blob; +import convex.core.data.Format; import convex.core.data.Strings; import convex.core.data.prim.CVMChar; import convex.core.util.Utils; @@ -259,6 +260,18 @@ public void appendCAD3Hex(Blob encoding) { append(Utils.toHexChar((b & 0xF))); } } + + public void appendVLQCountHex(long value) { + if (value<0) throw new IllegalArgumentException("Negative VLQ Count?"); + int n=Format.getVLQCountLength(value); + for (int i=0; i>((n-i-1)*7))&0x7f); // 128 bits from each position + if (i<(n-1)) { + b|=(byte) 0x80; // continuation bit except at end + } + appendHexByte(b); + } + } /** * Append a CVM character to this Blob diff --git a/convex-core/src/test/java/convex/core/data/CAD3Test.java b/convex-core/src/test/java/convex/core/data/CAD3Test.java new file mode 100644 index 000000000..4ec56bb0a --- /dev/null +++ b/convex-core/src/test/java/convex/core/data/CAD3Test.java @@ -0,0 +1,18 @@ +package convex.core.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class CAD3Test { + + @Test public void testExtensionValues() { + ExtensionValue ev=ExtensionValue.create((byte) 0xe3,100); + assertEquals(100,ev.longValue()); + assertEquals(Tag.EXTENSION_VALUE_BASE+3,ev.getTag()); + assertEquals("#[e364]",ev.toString()); + + ObjectsTest.doAnyValueTests(ev); + } + +}