Skip to content

Commit

Permalink
Add ACell.validateStructure
Browse files Browse the repository at this point in the history
  • Loading branch information
mikera committed Dec 9, 2024
1 parent 5168647 commit 4cdbbcf
Show file tree
Hide file tree
Showing 24 changed files with 141 additions and 115 deletions.
4 changes: 2 additions & 2 deletions convex-core/src/main/java/convex/core/cvm/ARecordGeneric.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ public AVector<ACell> values() {
protected abstract ARecordGeneric withValues(AVector<ACell> newValues);

@Override
public void validateCell() throws InvalidDataException {
values.validateCell();
protected void validateCell() throws InvalidDataException {
Cells.validateCell(values);
}

}
6 changes: 3 additions & 3 deletions convex-core/src/main/java/convex/core/cvm/Syntax.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,12 @@ public boolean print(BlobBuilder bb, long limit) {
public void validateCell() throws InvalidDataException {
if (datumRef == null) throw new InvalidDataException("null datum ref", this);
if (meta == null) throw new InvalidDataException("null metadata", this);
meta.validateCell();
Cells.validateCell(meta);;
}

@Override
public void validate() throws InvalidDataException {
super.validate();
public void validateStructure() throws InvalidDataException {
super.validateStructure();
ACell datum=datumRef.getValue();
if (datum!=null) {
if (datum instanceof Syntax) {
Expand Down
3 changes: 2 additions & 1 deletion convex-core/src/main/java/convex/core/cvm/ops/AMultiOp.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import convex.core.cvm.AOp;
import convex.core.data.ACell;
import convex.core.data.AVector;
import convex.core.data.Cells;
import convex.core.data.Format;
import convex.core.data.IRefFunction;
import convex.core.data.Ref;
Expand Down Expand Up @@ -62,6 +63,6 @@ public <R extends ACell> Ref<R> getRef(int i) {

@Override
public void validateCell() throws InvalidDataException {
ops.validateCell();
Cells.validateCell(ops);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ public Context apply(Context ctx) {

@Override
public void validateCell() throws InvalidDataException {
super.validateCell();
if (!Coin.isValidAmount(offer)) throw new InvalidDataException("Invalid offer",this);
target.validateCell();
}

@Override
Expand Down
12 changes: 0 additions & 12 deletions convex-core/src/main/java/convex/core/cvm/transactions/Invoke.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@
import convex.core.data.ACell;
import convex.core.data.AVector;
import convex.core.data.Blob;
import convex.core.data.Cells;
import convex.core.data.Format;
import convex.core.data.Keyword;
import convex.core.data.Vectors;
import convex.core.data.prim.CVMLong;
import convex.core.exceptions.BadFormatException;
import convex.core.exceptions.InvalidDataException;
import convex.core.lang.Reader;
import convex.core.util.ErrorMessages;

Expand Down Expand Up @@ -112,16 +110,6 @@ public int estimatedEncodingSize() {
return 1 + 12 + Format.MAX_EMBEDDED_LENGTH + Format.MAX_VLQ_LONG_LENGTH;
}

@Override
public void validateCell() throws InvalidDataException {
if (command instanceof AOp) {
// OK?
((AOp<?>) command).validateCell();
} else {
if (!Cells.isCanonical(command)) throw new InvalidDataException("Non-canonical object as command?", this);
}
}

@Override
public Invoke withSequence(long newSequence) {
if (newSequence==this.sequence) return this;
Expand Down
5 changes: 5 additions & 0 deletions convex-core/src/main/java/convex/core/data/AArrayBlob.java
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,11 @@ public void validateCell() throws InvalidDataException {
"End out of range: " + (offset + count) + " with array size=" + store.length, this);
}
}

@Override
protected void validateStructure() {
// nothing to do by default
}

@Override
public long longValue() {
Expand Down
2 changes: 1 addition & 1 deletion convex-core/src/main/java/convex/core/data/ABlob.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ public void validate() throws InvalidDataException {
}

@Override
public void validateCell() throws InvalidDataException {
protected void validateCell() throws InvalidDataException {
if (count() < 0) throw new InvalidDataException("Negative blob length", this);
}

Expand Down
6 changes: 5 additions & 1 deletion convex-core/src/main/java/convex/core/data/ACAD3Record.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public void validateCell() throws InvalidDataException {
default: throw new InvalidDataException("Bad tag for CAD3 Record: 0x"+Utils.toHexString(tag),this);
}
}


@Override
protected void validateStructure() throws InvalidDataException {
// Nothing to do, any child refs are valid
}

@Override
public boolean equals(ACell a) {
Expand Down
16 changes: 14 additions & 2 deletions convex-core/src/main/java/convex/core/data/ACell.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,27 @@ public void validate() throws InvalidDataException {
}

/**
* Validates the local structure and invariants of this cell. Called by validate() super implementation.
* Validates the local structure and invariants of this cell.
*
* Should validate directly contained data, but should not validate all other structure of this cell.
*
* In particular, should not traverse potentially missing child Refs.
*
* @throws InvalidDataException If the Cell is invalid
*/
public abstract void validateCell() throws InvalidDataException;
protected abstract void validateCell() throws InvalidDataException;

/**
* Validates the structure and invariants of this cell.
*
* May visit child refs. Should complete in O(1) time.
*
* @throws InvalidDataException If the Cell is invalid
*/
protected void validateStructure() throws InvalidDataException {
// nothing by default
}


/**
* Hash of data Encoding of this cell, equivalent to the Value ID. Calling this method
Expand Down
6 changes: 6 additions & 0 deletions convex-core/src/main/java/convex/core/data/ASymbolic.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ public final int hashCode() {
@Override
public abstract void validateCell() throws InvalidDataException;

@Override
public void validateStructure() throws InvalidDataException {
// nothing to do by default, covered by validateCell in general
}


@Override
public byte byteAt(long i) {
// TODO Auto-generated method stub
Expand Down
16 changes: 7 additions & 9 deletions convex-core/src/main/java/convex/core/data/BlobTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -432,21 +432,24 @@ public Blob getChunk(long chunkIndex) {
int child = Utils.checkedInt(chunkIndex >> shift);
return getChild(child).getChunk(chunkIndex - child * childSize);
}

@Override
public void validate() throws InvalidDataException {
super.validate();
public void validateCell() throws InvalidDataException {
int n = children.length;
if ((n < 2) | (n > FANOUT)) throw new InvalidDataException("Illegal number of BlobTree children: " + n, this);
}

@Override
public void validateStructure() throws InvalidDataException {
long clen = childLength();
long total = 0;
int n = children.length;

// We need to validate and check the lengths of all child notes. Note that only the last child can
// be shorted than the defined childLength() for this shift level.
for (int i = 0; i < n; i++) {
ABlob child;
child = getChild(i);
child.validate();

long cl = child.count();
total += cl;
Expand Down Expand Up @@ -477,11 +480,6 @@ public boolean appendHex(BlobBuilder bb, long length) {
return true;
}

@Override
public void validateCell() throws InvalidDataException {
int n = children.length;
if ((n < 2) | (n > FANOUT)) throw new InvalidDataException("Illegal number of BlobTree children: " + n, this);
}

@Override
public long hexMatch(ABlobLike<?> b) {
Expand Down
6 changes: 6 additions & 0 deletions convex-core/src/main/java/convex/core/data/Cells.java
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ public static void validate(ACell cell) throws InvalidDataException {
Ref<?> ref=cell.getRef();
if (ref.isValidated()) return;
cell.validateCell();
cell.validateStructure();
}

/**
Expand Down Expand Up @@ -384,6 +385,11 @@ public static int getEncodingLength(ACell value) {
return value.getEncodingLength();
}

public static void validateCell(ACell a) throws InvalidDataException {
if (a==null) return;
a.validateCell();
}



}
7 changes: 7 additions & 0 deletions convex-core/src/main/java/convex/core/data/CodedValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public int estimatedEncodingSize() {
public void validateCell() throws InvalidDataException {
// Nothing to do
}

@Override
protected void validateStructure() throws InvalidDataException {
// Nothing to do, any child refs are valid
}

@Override
public byte getTag() {
Expand Down Expand Up @@ -116,4 +121,6 @@ public static CodedValue read(byte tag, Blob b, int pos) throws BadFormatExcepti
}
return result;
}


}
7 changes: 6 additions & 1 deletion convex-core/src/main/java/convex/core/data/Format.java
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,8 @@ public static void decodeCells(HashMap<Hash,ACell> acc, Blob data) throws BadFor
* @return Blob encoding
*/
public static Blob encodeMultiCell(ACell a, boolean everything) {
if (a==null) return Blob.NULL_ENCODING;

Blob topCellEncoding=Cells.encode(a);
if (a.getRefCount()==0) return topCellEncoding;

Expand All @@ -974,7 +976,10 @@ public static Blob encodeMultiCell(ACell a, boolean everything) {
int cellLength=lengthFieldSize+encLength;

int newLength=ml[0]+cellLength;
if (newLength>CPoSConstants.MAX_MESSAGE_LENGTH) return;
if (newLength>CPoSConstants.MAX_MESSAGE_LENGTH) {
System.err.println("Exceeded max message length when encoding");
return;
}
ml[0]=newLength;
refs.add(cr);
if (everything) Cells.visitBranchRefs(c, addToStackFunc);
Expand Down
96 changes: 48 additions & 48 deletions convex-core/src/main/java/convex/core/data/Index.java
Original file line number Diff line number Diff line change
Expand Up @@ -630,54 +630,6 @@ protected MapEntry<K, V> getEntryByHash(Hash hash) {
throw new UnsupportedOperationException();
}

@SuppressWarnings("unchecked")
@Override
public void validate() throws InvalidDataException {
super.validate();

if ((depth<0)||(depth>MAX_DEPTH)) throw new InvalidDataException("Invalid index depth",this);

if (entry!=null) {
ABlobLike<K> k=RT.ensureBlobLike(entry.getKey());
if (k==null) throw new InvalidDataException("Invalid entry key type: "+Utils.getClassName(entry.getKey()),this);
if (depth!=effectiveLength(k)) throw new InvalidDataException("Entry at inconsistent depth",this);
}

ABlobLike<?> prefix=getPrefix();
if (depth>effectiveLength(prefix)) throw new InvalidDataException("depth longer than common prefix",this);

long ecount = (entry == null) ? 0 : 1;
int n = children.length;
for (int i = 0; i < n; i++) {
ACell o = children[i].getValue();
if (!(o instanceof Index))
throw new InvalidDataException("Illegal Index child type: " + Utils.getClass(o), this);
Index<K, V> c = (Index<K, V>) o;

long ccount=c.count();
if (ccount==0) {
throw new InvalidDataException("Child "+i+" should not be empty! At depth "+depth,this);
}

if (c.getDepth() <= getDepth()) {
throw new InvalidDataException("Child must have greater depth than parent", this);
}

ABlobLike<?> childPrefix=c.getPrefix();
long ml=prefix.hexMatch(childPrefix, 0, depth);
if (ml<depth) throw new InvalidDataException("Child does not have matching common prefix", this);

c.validate();

// check child has correct digit for mask position
int digit=childPrefix.getHexDigit(depth);
if (i!=Bits.indexForDigit(digit, mask)) throw new InvalidDataException("Child does not have correct digit", this);

ecount += ccount;
}

if (count != ecount) throw new InvalidDataException("Bad entry count: " + ecount + " expected: " + count, this);
}

private static long effectiveLength(ABlobLike<?> prefix) {
return Math.min(MAX_DEPTH, prefix.hexLength());
Expand Down Expand Up @@ -728,6 +680,54 @@ public void validateCell() throws InvalidDataException {
"Index with no entry and count=" + count + " must have two or more children", this);
}
}

@SuppressWarnings("unchecked")
@Override
public void validate() throws InvalidDataException {
super.validate();

if ((depth<0)||(depth>MAX_DEPTH)) throw new InvalidDataException("Invalid index depth",this);

if (entry!=null) {
ABlobLike<K> k=RT.ensureBlobLike(entry.getKey());
if (k==null) throw new InvalidDataException("Invalid entry key type: "+Utils.getClassName(entry.getKey()),this);
if (depth!=effectiveLength(k)) throw new InvalidDataException("Entry at inconsistent depth",this);
}

ABlobLike<?> prefix=getPrefix();
if (depth>effectiveLength(prefix)) throw new InvalidDataException("depth longer than common prefix",this);

long ecount = (entry == null) ? 0 : 1;
int n = children.length;
for (int i = 0; i < n; i++) {
ACell o = children[i].getValue();
if (!(o instanceof Index))
throw new InvalidDataException("Illegal Index child type: " + Utils.getClass(o), this);
Index<K, V> c = (Index<K, V>) o;

long ccount=c.count();
if (ccount==0) {
throw new InvalidDataException("Child "+i+" should not be empty! At depth "+depth,this);
}

if (c.getDepth() <= getDepth()) {
throw new InvalidDataException("Child must have greater depth than parent", this);
}

ABlobLike<?> childPrefix=c.getPrefix();
long ml=prefix.hexMatch(childPrefix, 0, depth);
if (ml<depth) throw new InvalidDataException("Child does not have matching common prefix", this);

// check child has correct digit for mask position
int digit=childPrefix.getHexDigit(depth);
if (i!=Bits.indexForDigit(digit, mask)) throw new InvalidDataException("Child does not have correct digit", this);

ecount += ccount;
}

if (count != ecount) throw new InvalidDataException("Bad entry count: " + ecount + " expected: " + count, this);
}


@SuppressWarnings("unchecked")
@Override
Expand Down
Loading

0 comments on commit 4cdbbcf

Please sign in to comment.