Skip to content

Commit

Permalink
Propagate nullness knowledge
Browse files Browse the repository at this point in the history
Keys and values are known to be non-null. Conditions are known to be
nullable.

Signed-off-by: Robert Varga <[email protected]>
  • Loading branch information
rovarga committed Jan 25, 2025
1 parent 0076327 commit 31b47da
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 53 deletions.
35 changes: 19 additions & 16 deletions triemap/src/main/java/tech/pantheon/triemap/CNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static tech.pantheon.triemap.PresencePredicate.PRESENT;

import java.util.concurrent.ThreadLocalRandom;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;

final class CNode<K, V> extends MainNode<K, V> {
Expand Down Expand Up @@ -54,8 +55,8 @@ private CNode(final Gen gen, final int bitmap, final Branch<K, V>... array) {
this(gen, 0, (Branch<K, V>[]) EMPTY_ARRAY);
}

static <K, V> MainNode<K, V> dual(final SNode<K, V> first, final K key, final V value, final int hc,
final int initLev, final Gen gen) {
static <K, V> MainNode<K, V> dual(final SNode<K, V> first, final @NonNull K key, final @NonNull V value,
final int hc, final int initLev, final Gen gen) {
final var second = new SNode<>(key, value, hc);
final var fhc = first.hc();

Expand Down Expand Up @@ -89,7 +90,7 @@ static <K, V> MainNode<K, V> dual(final SNode<K, V> first, final K key, final V
}
}

Object lookup(final TrieMap<K, V> ct, final Gen startGen, final int hc, final K key, final int lev,
Object lookup(final TrieMap<K, V> ct, final Gen startGen, final int hc, final @NonNull K key, final int lev,
final INode<K, V> parent) {
// 1) a multinode
final int idx = hc >>> lev & 0x1f;
Expand All @@ -115,8 +116,8 @@ Object lookup(final TrieMap<K, V> ct, final Gen startGen, final int hc, final K
}
}

boolean insert(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key, final V val,
final int lev, final INode<K, V> parent) {
boolean insert(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final @NonNull K key,
final @NonNull V val, final int lev, final INode<K, V> parent) {
// 1) a multiway node
final int idx = hc >>> lev & 0x1f;
final int flag = 1 << idx;
Expand All @@ -141,7 +142,7 @@ boolean insert(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc,
}

boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int pos, final SNode<K, V> sn,
final K key, final V val, final int hc, final int lev) {
final @NonNull K key, final @NonNull V val, final int hc, final int lev) {
final CNode<K, V> next;
if (!sn.matches(hc, key)) {
final var rn = gen == in.gen ? this : renewed(ct, gen);
Expand All @@ -152,15 +153,15 @@ boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int po
return in.gcasWrite(ct, next);
}

boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int pos, final int flag, final K key,
final V val, final int hc) {
boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int pos, final int flag,
final @NonNull K key, final @NonNull V val, final int hc) {
final var ngen = in.gen;
final var rn = gen == ngen ? this : renewed(ct, ngen);
return in.gcasWrite(ct, rn.toInsertedAt(this, ngen, pos, flag, key, val, hc));
}

@Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key,
final V val, final Object cond, final int lev, final INode<K, V> parent) {
@Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final @NonNull K key,
final @NonNull V val, final @Nullable Object cond, final int lev, final INode<K, V> parent) {
// 1) a multiway node
final int idx = hc >>> lev & 0x1f;
final int flag = 1 << idx;
Expand Down Expand Up @@ -190,7 +191,8 @@ boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int po
}

private @Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int pos,
final SNode<K, V> sn, final K key, final V val, final int hc, final Object cond, final int lev) {
final SNode<K, V> sn, final @NonNull K key, final @NonNull V val, final int hc, final @Nullable Object cond,
final int lev) {
if (!sn.matches(hc, key)) {
if (cond == null || cond == ABSENT) {
final var ngen = in.gen;
Expand All @@ -208,8 +210,8 @@ boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int po
return Result.empty();
}

@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key,
final Object cond, final int lev, final INode<K, V> parent) {
@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final @NonNull K key,
final @Nullable Object cond, final int lev, final INode<K, V> parent) {
final int idx = hc >>> lev & 0x1f;
final int flag = 1 << idx;
if ((bitmap & flag) == 0) {
Expand Down Expand Up @@ -345,12 +347,13 @@ private CNode<K, V> updatedAt(final int pos, final Branch<K, V> nn, final Gen ng
return toUpdatedAt(this, pos, nn, ngen);
}

private CNode<K, V> updatedAt(final int pos, final K key, final V val, final int hc, final Gen ngen) {
private CNode<K, V> updatedAt(final int pos, final @NonNull K key, final @NonNull V val, final int hc,
final Gen ngen) {
return updatedAt(pos, new SNode<>(key, val, hc), ngen);
}

private CNode<K, V> toInsertedAt(final CNode<K, V> prev, final Gen ngen, final int pos, final int flag, final K key,
final V value, final int hc) {
private CNode<K, V> toInsertedAt(final CNode<K, V> prev, final Gen ngen, final int pos, final int flag,
final @NonNull K key, final @NonNull V value, final int hc) {
final int len = array.length;
final var narr = newArray(len + 1);
System.arraycopy(array, 0, narr, 0, pos);
Expand Down
5 changes: 3 additions & 2 deletions triemap/src/main/java/tech/pantheon/triemap/DefaultEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package tech.pantheon.triemap;

import java.util.Map.Entry;
import org.eclipse.jdt.annotation.NonNull;

/**
* Our {@link Entry} implementations are immutable by default.
Expand All @@ -27,15 +28,15 @@
*/
sealed interface DefaultEntry<K, V> extends Entry<K, V> permits AbstractEntry, EntryNode {

K key();
@NonNull K key();

@Override
@Deprecated
default K getKey() {
return key();
}

V value();
@NonNull V value();

@Override
@Deprecated
Expand Down
17 changes: 9 additions & 8 deletions triemap/src/main/java/tech/pantheon/triemap/INode.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ private TryGcas(final Gcas<K, V> prev) {
this.gen = gen;
}

INode(final INode<K, V> prev, final SNode<K, V> sn, final K key, final V val, final int hc, final int lev) {
INode(final INode<K, V> prev, final SNode<K, V> sn, final @NonNull K key, final @NonNull V val, final int hc,
final int lev) {
this(prev.gen, CNode.dual(sn, key, val, hc, lev + LEVEL_BITS, prev.gen));
}

Expand Down Expand Up @@ -229,7 +230,7 @@ public int elementSize(final ImmutableTrieMap<K, V> ct) {
* @return null if no value has been found, RESTART if the operation was not successful, or any other value
* otherwise
*/
Object lookup(final TrieMap<K, V> ct, final Gen startGen, final int hc, final K key, final int lev,
Object lookup(final TrieMap<K, V> ct, final Gen startGen, final int hc, final @NonNull K key, final int lev,
final INode<K, V> parent) {
final var m = gcasRead(ct);

Expand Down Expand Up @@ -257,8 +258,8 @@ Object lookup(final TrieMap<K, V> ct, final Gen startGen, final int hc, final K
*
* @return true if successful, false otherwise
*/
boolean insert(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key, final V val,
final int lev, final INode<K, V> parent) {
boolean insert(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final @NonNull K key,
final @NonNull V val, final int lev, final INode<K, V> parent) {
final var m = gcasRead(ct);
if (m instanceof CNode<K, V> cn) {
return cn.insert(ct, startGen, hc, key, val, lev, this);
Expand All @@ -282,8 +283,8 @@ boolean insert(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc,
* other value `val` - key must be bound to `val`
* @return null if unsuccessful, Result(V) otherwise (indicating previous value bound to the key)
*/
@Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key,
final V val, final Object cond, final int lev, final INode<K, V> parent) {
@Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final @NonNull K key,
final @NonNull V val, final Object cond, final int lev, final INode<K, V> parent) {
final var m = gcasRead(ct);
if (m instanceof CNode<K, V> cn) {
return cn.insertIf(ct, startGen, hc, key, val, cond, lev, this);
Expand All @@ -308,8 +309,8 @@ boolean insert(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc,
* @return null if not successful, an Result indicating the previous
* value otherwise
*/
@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key,
final Object cond, final int lev, final INode<K, V> parent) {
@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final @NonNull K key,
final @Nullable Object cond, final int lev, final INode<K, V> parent) {
final var m = gcasRead(ct);
if (m instanceof CNode<K, V> cn) {
final var res = cn.remove(ct, startGen, hc, key, cond, lev, this);
Expand Down
34 changes: 18 additions & 16 deletions triemap/src/main/java/tech/pantheon/triemap/LNodeEntries.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static tech.pantheon.triemap.PresencePredicate.ABSENT;
import static tech.pantheon.triemap.PresencePredicate.PRESENT;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;

/**
Expand All @@ -32,7 +33,7 @@
abstract sealed class LNodeEntries<K, V> extends LNodeEntry<K, V> {
// Visible for testing
static final class Single<K, V> extends LNodeEntries<K, V> {
Single(final K key, final V value) {
Single(final @NonNull K key, final @NonNull V value) {
super(key, value);
}

Expand All @@ -51,7 +52,7 @@ private static final class Multiple<K, V> extends LNodeEntries<K, V> {
this(entry.key(), entry.value(), null);
}

Multiple(final K key, final V value, final LNodeEntries<K, V> next) {
Multiple(final @NonNull K key, final @NonNull V value, final LNodeEntries<K, V> next) {
super(key, value);
this.next = next;
}
Expand All @@ -62,11 +63,12 @@ LNodeEntries<K, V> next() {
}
}

LNodeEntries(final K key, final V value) {
LNodeEntries(final @NonNull K key, final @NonNull V value) {
super(key, value);
}

static <K,V> LNodeEntries<K, V> of(final K k1, final V v1, final K k2, final V v2) {
static <K,V> LNodeEntries<K, V> of(final @NonNull K k1, final @NonNull V v1,
final @NonNull K k2, final @NonNull V v2) {
return new Multiple<>(k1, v1, new Single<>(k2, v2));
}

Expand All @@ -78,19 +80,19 @@ static <K,V> LNodeEntries<K, V> of(final K k1, final V v1, final K k2, final V v
*/
abstract LNodeEntries<K, V> next();

final @Nullable V lookup(final K key) {
final @Nullable V lookup(final @NonNull K key) {
final var entry = findEntry(key);
return entry != null ? entry.value() : null;
}

final boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final LNode<K, V> ln, final K key,
final V val) {
final boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final LNode<K, V> ln,
final @NonNull K key, final @NonNull V val) {
final var entry = findEntry(key);
return in.gcasWrite(ct, entry == null ? toInserted(ln, key, val) : toReplaced(ln, entry, val));
}

@Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final INode<K, V> in, final LNode<K, V> ln, final K key,
final V val, final Object cond) {
@Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final INode<K, V> in, final LNode<K, V> ln,
final @NonNull K key, final @NonNull V val, final Object cond) {
final var entry = findEntry(key);
if (entry == null) {
return cond != null && cond != ABSENT || in.gcasWrite(ct, toInserted(ln, key, val)) ? Result.empty() : null;
Expand All @@ -103,8 +105,8 @@ final boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final
return Result.empty();
}

@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final INode<K, V> in, final LNode<K, V> ln, final K key,
final Object cond, final int hc) {
@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final INode<K, V> in, final LNode<K, V> ln,
final @NonNull K key, final @Nullable Object cond, final int hc) {
final var entry = findEntry(key);
if (entry == null) {
// Key was not found, hence no modification is needed
Expand All @@ -127,22 +129,22 @@ final boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final
return in.gcasWrite(ct, next) ? entry.toResult() : null;
}

private LNode<K, V> toInserted(final LNode<K, V> ln, final K key, final V val) {
private LNode<K, V> toInserted(final LNode<K, V> ln, final @NonNull K key, final @NonNull V val) {
return new LNode<>(ln, insertEntry(key, val), ln.size + 1);
}

private LNode<K, V> toReplaced(final LNode<K, V> ln, final LNodeEntry<K, V> entry, final V val) {
private LNode<K, V> toReplaced(final LNode<K, V> ln, final LNodeEntry<K, V> entry, final @NonNull V val) {
return new LNode<>(ln, replace(entry, val), ln.size);
}

// Visible for testing
final LNodeEntries<K, V> replace(final LNodeEntry<K, V> entry, final V value) {
final LNodeEntries<K, V> replace(final LNodeEntry<K, V> entry, final @NonNull V value) {
final var removed = removeEntry(entry);
return removed == null ? new Single<>(entry.key(), value) : new Multiple<>(entry.key(), value, removed);
}

// Visible for testing
final @Nullable LNodeEntry<K, V> findEntry(final K key) {
final @Nullable LNodeEntry<K, V> findEntry(final @NonNull K key) {
// We do not perform recursion on purpose here, so we do not run out of stack if the key hashing fails.
var entry = this;
do {
Expand All @@ -157,7 +159,7 @@ final LNodeEntries<K, V> replace(final LNodeEntry<K, V> entry, final V value) {
}

// Visible for testing
final LNodeEntries<K, V> insertEntry(final K key, final V value) {
final LNodeEntries<K, V> insertEntry(final @NonNull K key, final @NonNull V value) {
return new Multiple<>(key, value, this);
}

Expand Down
6 changes: 3 additions & 3 deletions triemap/src/main/java/tech/pantheon/triemap/LNodeEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
* @param <V> the type of value
*/
abstract sealed class LNodeEntry<K, V> extends AbstractEntry<K, V> permits LNodeEntries {
private final K key;
private final V value;
private final @NonNull K key;
private final @NonNull V value;

LNodeEntry(final K key, final V value) {
LNodeEntry(final @NonNull K key, final @NonNull V value) {
this.key = key;
this.value = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ public K key() {
*/
@Override
public V value() {
return newValue != null ? newValue : delegate.value();
final var val = newValue;
return val != null ? val : delegate.value();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;

/**
* A mutable TrieMap.
Expand Down Expand Up @@ -106,7 +107,7 @@ public V replace(final K key, final V value) {
return insertIf(k, requireNonNull(value), PRESENT).orNull();
}

private @NonNull Result<V> insertIf(final K key, final V value, final Object cond) {
private @NonNull Result<V> insertIf(final @NonNull K key, final @NonNull V value, final @Nullable Object cond) {
final int hc = computeHash(key);

Result<V> res;
Expand All @@ -119,7 +120,7 @@ public V replace(final K key, final V value) {
return res;
}

private @NonNull Result<V> removeIf(final K key, final Object cond) {
private @NonNull Result<V> removeIf(final @NonNull K key, final @Nullable Object cond) {
final int hc = computeHash(key);

Result<V> res;
Expand Down
2 changes: 1 addition & 1 deletion triemap/src/main/java/tech/pantheon/triemap/SNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;

record SNode<K, V>(K key, V value, int hc) implements Branch<K, V>, EntryNode<K, V> {
record SNode<K, V>(@NonNull K key, @NonNull V value, int hc) implements Branch<K, V>, EntryNode<K, V> {
SNode(final TNode<K, V> tn) {
this(tn.key, tn.value, tn.hc);
}
Expand Down
10 changes: 6 additions & 4 deletions triemap/src/main/java/tech/pantheon/triemap/TNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
*/
package tech.pantheon.triemap;

import org.eclipse.jdt.annotation.NonNull;

final class TNode<K, V> extends MainNode<K, V> implements EntryNode<K, V> {
final K key;
final V value;
final @NonNull K key;
final @NonNull V value;
final int hc;

// Visible for testing
TNode(final CNode<K, V> prev, final K key, final V value, final int hc) {
TNode(final CNode<K, V> prev, final @NonNull K key, final @NonNull V value, final int hc) {
super(prev);
this.key = key;
this.value = value;
Expand All @@ -32,7 +34,7 @@ final class TNode<K, V> extends MainNode<K, V> implements EntryNode<K, V> {
this(prev, sn.key(), sn.value(), sn.hc());
}

TNode(final LNode<K, V> prev, final K key, final V value, final int hc) {
TNode(final LNode<K, V> prev, final @NonNull K key, final @NonNull V value, final int hc) {
super(prev);
this.key = key;
this.value = value;
Expand Down

0 comments on commit 31b47da

Please sign in to comment.