convex.core.data.Address Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of convex-core Show documentation
Show all versions of convex-core Show documentation
Convex core libraries and common utilities
The newest version!
package convex.core.data;
import convex.core.data.prim.CVMLong;
import convex.core.data.type.AType;
import convex.core.data.type.Types;
import convex.core.data.util.BlobBuilder;
import convex.core.exceptions.BadFormatException;
import convex.core.exceptions.InvalidDataException;
import convex.core.lang.RT;
import convex.core.util.Bits;
import convex.core.util.Errors;
import convex.core.util.Utils;
/**
* Immutable class representing an Address, generally used to uniquely identify an Account.
*
* An Address is a specialised 8-byte long blob instance that wraps a non-negative long Account number. This number
* serves as an index into the vector of accounts for the current state.
*
*/
public final class Address extends ABlobLike {
public static final int LENGTH=8;
private static final int CACHE_SIZE=256;
private static Address[] CACHE=new Address[CACHE_SIZE];
static {
for (int i=0; i b) {
if (b.count()>BYTE_LENGTH) return null;
return create(b.longValue());
}
@Override
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
if (!(a instanceof Address)) return false; // Handles null
return equals((Address)a);
}
@Override
public boolean equals(ACell o) {
if (o==this) return true;
if (!(o instanceof Address)) return false;
return value==((Address) o).value;
}
public final boolean equals(Address o) {
return value==o.value;
}
/**
* Obtains an Address from a hex string
*
* @param hexString String to read Address from
* @return An Address constructed from the hex string, or null if not a valid
* hex string
*/
public static Address fromHex(String hexString) {
// catch nulls just in case
if (hexString==null) return null;
// catch odd length
if ((hexString.length()&1)!=0) return null;
if (hexString.length()>16) return null;
Blob b=Blob.fromHex(hexString);
if (b==null) return null;
return create(b.longValue());
}
/**
* Obtains an Address from an arbitrary String, attempting to parse possible formats '123' '0xc3' or '#123'
* @param s String to parse
* @return Address parsed, or null if not valid
*/
public static Address parse(String s) {
if (s==null) return null;
s=s.trim();
if (s.startsWith("#")) {
s=s.substring(1);
}
if (s.startsWith("0x")) {
s=s.substring(2);
return fromHex(s);
}
try {
long l=Long.parseLong(s);
return Address.create(l);
} catch (NumberFormatException e) {
// fall through
return null;
}
}
/**
* Attempts to parse an address from an arbitrary object. Accepts strings and numbers on a best efforts basis.
* @param o Object to parse as an Address
* @return Address parsed, or null if not valid
*/
public static Address parse(Object o) {
if (o==null) return null;
if (o instanceof ACell) {
Address add=RT.castAddress((ACell)o);
if (add!=null) return add;
o=RT.jvm((ACell)o); // convert to JVM type
}
if (o instanceof String) return parse((String)o);
if (o instanceof Number) {
Number n=(Number)o;
long l=n.longValue();
if (l==n.doubleValue()) return Address.create(l);
}
return null;
}
public static Address read(Blob b, int pos) throws BadFormatException {
long value=Format.readVLCCount(b,pos+1); // skip tag, we assume correct
Address a= Address.create(value);
if (a==null) throw new BadFormatException("Invalid Address: "+value);
int epos=pos+1+Format.getVLCCountLength(value);
a.attachEncoding(b.slice(pos, epos));
return a;
}
@Override
public int encode(byte[] bs, int pos) {
bs[pos++]=Tag.ADDRESS;
return encodeRaw(bs,pos);
}
@Override
public int encodeRaw(byte[] bs, int pos) {
return Format.writeVLCCount(bs, pos, value);
}
@Override
public boolean print(BlobBuilder sb, long limit) {
sb.append("#");
sb.append(Long.toString(value));
return sb.check(limit);
}
@Override
public AString toCVMString(long limit) {
if (limit<2) return null;
return Strings.create(toString());
}
@Override
public String toString() {
return "#"+value;
}
@Override
public int estimatedEncodingSize() {
// tag VLC bytes
return 1 + Format.MAX_VLC_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];
Utils.writeLong(bs, 0, value);
return Blob.wrap(bs);
}
public static final int MAX_ENCODING_LENGTH = 1+Format.MAX_VLC_COUNT_LENGTH;
@Override
public byte getTag() {
return Tag.ADDRESS;
}
@Override
public Address toCanonical() {
return this;
}
/**
* Creates a new Address at an offset to this Address
* @param offset Offset to add to this Address (may be negative)
* @return New Address, or null if would be invalid
*/
public Address offset(long offset) {
return create(value+offset);
}
@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);
}
private static void checkIndex(long i) {
if ((i < 0) || (i >= LENGTH)) throw new IndexOutOfBoundsException(Errors.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;
}
@Override
public CVMLong get(long i) {
checkIndex(i);
return CVMLong.create(Utils.longByteAt(value,i));
}
@Override
public boolean isCanonical() {
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;
}
}