convex.core.data.RefSoft 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 java.lang.ref.SoftReference;
import convex.core.exceptions.InvalidDataException;
import convex.core.exceptions.MissingDataException;
import convex.core.store.AStore;
import convex.core.store.Stores;
/**
* Reference class implemented via a soft reference and store lookup.
*
* Ref makes use of a soft reference to values, allowing memory to be reclaimed
* by the garbage collector when not required. A MissingDataException will occur
* with any attempt to deference this Ref when the value is not present
* and not stored in the current store.
*
* Instances of this class should usually be be STORED, otherwise data loss
* may occur due to garbage collection. However UNKNOWN RefSoft may exist temporarily
* (e.g. reading Refs from external messages)
*
* RefSoft must always have a non-null hash, to ensure lookup capability in
* store.
*
* RefSoft must always store a canonical value, if any
*
* @param Type of referenced Cell
*/
public class RefSoft extends Ref {
/**
* SoftReference to value. Might get updated to a fresh instance.
*/
protected SoftReference softRef;
/**
* SoftReference to value. Might get updated to a fresh instance.
*/
protected final AStore store;
protected RefSoft(AStore store, SoftReference ref, Hash hash, int flags) {
super(hash, flags);
this.softRef = ref;
this.store=store;
}
protected RefSoft(AStore store, T value, Hash hash, int flags) {
this(store,createSoftReference(value), hash, flags);
}
protected RefSoft(AStore store, Hash hash) {
// We don't know anything about this Ref.
this(store,new SoftReference(null), hash, UNKNOWN);
}
@SuppressWarnings("unchecked")
private static SoftReference createSoftReference(T value) {
if (!value.isCanonical()) {
value=(T) value.toCanonical();
}
return new SoftReference(value);
}
@Override
public RefSoft withFlags(int newFlags) {
if (flags==newFlags) return this;
return new RefSoft(store,softRef,hash,newFlags);
}
public static RefSoft create(AStore store,T value, int flags) {
Hash hash=Hash.get(value);
return new RefSoft(store,value, hash, flags);
}
/**
* Create a RefSoft with a Hash reference.
*
* Attempts to get the value will trigger a store lookup, which may in turn
* cause a MissingDataException if not found.
*
* @param Type of value
* @param hash Hash ID of value.
* @return New RefSoft instance
*/
public static RefSoft createForHash(Hash hash) {
return new RefSoft(Stores.current(),hash);
}
@Override
public T getValue() {
T result = softRef.get();
if (result == null) {
Ref storeRef = store.refForHash(hash);
if (storeRef == null) {
throw new MissingDataException(store,hash);
}
this.flags=Ref.mergeFlags(this.flags, storeRef.flags);
result = storeRef.getValue();
if (storeRef instanceof RefSoft) {
// Update soft reference to the fresh version. No point keeping old one....
this.softRef = ((RefSoft) storeRef).softRef;
} else {
// Create a new soft reference
this.softRef = new SoftReference(result);
}
}
return result;
}
@Override
public boolean isMissing() {
T result = softRef.get();
if (result != null) return false; // still in memory, so not missing
// check store
Ref storeRef = store.refForHash(hash);
if (storeRef == null) return true; // must be missing, couldn't find in store
// We know we have in store.
// Update soft reference to the fresh version. No point keeping old one....
if (storeRef instanceof RefSoft) {
this.softRef = ((RefSoft) storeRef).softRef;
} else {
this.softRef = new SoftReference(storeRef.getValue());
}
this.flags=Ref.mergeFlags(this.flags, storeRef.flags);
return false;
}
@Override
public boolean equals(Ref a) {
if (a.hash!=null) {
// prefer hash comparison, this avoid potential store lookups
return hash.equals(a.hash);
}
// compare by value
return Cells.equals(getValue(),a.getValue());
}
@Override
public boolean isDirect() {
return false;
}
@Override
public RefDirect toDirect() {
return RefDirect.create(getValue(), hash, flags);
}
@Override
public RefSoft toSoft(AStore store) {
return withStore(store);
}
@Override
public Hash getHash() {
return hash;
}
@Override
public void validate() throws InvalidDataException {
super.validate();
if (hash == null) throw new InvalidDataException("Hash should never be null in soft ref", this);
ACell val = softRef.get();
boolean embedded=isEmbedded();
if (embedded!=Format.isEmbedded(val)) {
throw new InvalidDataException("Embedded flag ["+embedded+"] inconsistent with value", this);
}
}
@Override
public Ref withValue(T newValue) {
if (softRef.get()!=newValue) return new RefSoft(store,newValue,hash,flags);
return this;
}
public RefSoft withStore(AStore store) {
if (this.store==store) return this;
return new RefSoft(store,softRef,hash,flags);
}
@Override
public int estimatedEncodingSize() {
return isEmbedded()?Format.MAX_EMBEDDED_LENGTH: INDIRECT_ENCODING_LENGTH;
}
@Override
public Ref ensureCanonical() {
return this;
}
public AStore getStore() {
return store;
}
/**
* Checks if this Ref still has a local reference
* @return True if an in memory reference exists
*/
public boolean hasReference() {
return softRef.get()!=null;
}
}