Skip to content

Commit 31e130d

Browse files
committed
Split out {C,I}Node.insert() and .insertIf()
The two methods are cooperating, which is obvious in MutableTrieMap.recInsert() progress. Split that method up again, leading to simpler code at each level. It also allows us to reshuffle method placement and visibility a bit. Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
1 parent 4a17baf commit 31e130d

File tree

11 files changed

+240
-251
lines changed

11 files changed

+240
-251
lines changed

triemap/src/main/java/tech/pantheon/triemap/CNode.java

Lines changed: 155 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ final class CNode<K, V> extends MainNode<K, V> {
2929

3030
final int bitmap;
3131
final Branch<K, V>[] array;
32-
final Gen gen;
32+
private final Gen gen;
3333

3434
// Since concurrent computation should lead to same results we can update this field without any synchronization.
3535
private volatile int csize = NO_SIZE;
@@ -54,6 +54,41 @@ private CNode(final Gen gen, final int bitmap, final Branch<K, V>... array) {
5454
this(gen, 0, (Branch<K, V>[]) EMPTY_ARRAY);
5555
}
5656

57+
static <K, V> MainNode<K, V> dual(final SNode<K, V> first, final K key, final V value, final int hc,
58+
final int initLev, final Gen gen) {
59+
final var second = new SNode<>(key, value, hc);
60+
final var fhc = first.hc();
61+
62+
// recursion control
63+
final var bmps = new int[MAX_DEPTH];
64+
int len = 0;
65+
int lev = initLev;
66+
67+
while (true) {
68+
MainNode<K, V> deepest;
69+
if (lev < HASH_BITS) {
70+
final int xidx = fhc >>> lev & 0x1f;
71+
final int yidx = hc >>> lev & 0x1f;
72+
final int bmp = 1 << xidx | 1 << yidx;
73+
if (xidx == yidx) {
74+
// enter recursion: save bitmap and increment lev
75+
bmps[len++] = bmp;
76+
lev += LEVEL_BITS;
77+
continue;
78+
}
79+
deepest = xidx < yidx ? new CNode<>(gen, bmp, first, second) : new CNode<>(gen, bmp, second, first);
80+
} else {
81+
deepest = new LNode<>(first, second);
82+
}
83+
84+
while (len > 0) {
85+
// exit recursion: load bitmap and wrap deepest with a CNode
86+
deepest = new CNode<>(gen, bmps[--len], new INode<>(gen, deepest));
87+
}
88+
return deepest;
89+
}
90+
}
91+
5792
Object lookup(final TrieMap<K, V> ct, final Gen startGen, final int hc, final K key, final int lev,
5893
final INode<K, V> parent) {
5994
// 1) a multinode
@@ -76,49 +111,33 @@ Object lookup(final TrieMap<K, V> ct, final Gen startGen, final int hc, final K
76111
// 2) singleton node
77112
return sn.lookup(hc, key);
78113
} else {
79-
throw TrieMap.invalidElement(sub);
114+
throw invalidElement(sub);
80115
}
81116
}
82117

83-
@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key,
84-
final Object cond, final int lev, final INode<K, V> parent) {
118+
boolean insert(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key, final V val,
119+
final int lev, final INode<K, V> parent) {
120+
// 1) a multiway node
85121
final int idx = hc >>> lev & 0x1f;
86122
final int flag = 1 << idx;
123+
final int mask = flag - 1;
124+
final int pos = Integer.bitCount(bitmap & mask);
125+
87126
if ((bitmap & flag) == 0) {
88-
return Result.empty();
127+
return insert(ct, parent, pos, flag, key, val, hc);
89128
}
90129

91-
final int pos = Integer.bitCount(bitmap & flag - 1);
92-
final var sub = array[pos];
93-
if (sub instanceof INode<K, V> in) {
94-
// renew if needed
95-
return startGen != in.gen && !renew(ct, parent, startGen)
96-
? null : in.remove(ct, startGen, hc, key, cond, lev + LEVEL_BITS, parent);
97-
} else if (sub instanceof SNode<K, V> sn) {
98-
return remove(ct, parent, flag, pos, sn, key, hc, cond, lev);
130+
// 1a) insert below
131+
final var cnAtPos = array[pos];
132+
if (cnAtPos instanceof INode<K, V> in) {
133+
// try to renew if needed and enter next level
134+
return (startGen == in.gen || renew(ct, parent, startGen))
135+
&& in.insert(ct, startGen, hc, key, val, lev + LEVEL_BITS, parent);
136+
} else if (cnAtPos instanceof SNode<K, V> sn) {
137+
return insert(ct, parent, pos, sn, key, val, hc, lev);
99138
} else {
100-
throw TrieMap.invalidElement(sub);
101-
}
102-
}
103-
104-
@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int flag, final int pos,
105-
final SNode<K, V> sn, final K key, final int hc, final Object cond, final int lev) {
106-
if (!sn.matches(hc, key) || cond != null && !cond.equals(sn.value())) {
107-
return Result.empty();
139+
throw invalidElement(cnAtPos);
108140
}
109-
110-
final var arr = array;
111-
final int len = arr.length;
112-
final var narr = newArray(len - 1);
113-
System.arraycopy(arr, 0, narr, 0, pos);
114-
System.arraycopy(arr, pos + 1, narr, pos, len - pos - 1);
115-
final var onlySN = onlySNode(narr, lev);
116-
return in.gcasWrite(onlySN != null ? onlySN.copyTombed(this) : new CNode<>(this, gen, bitmap ^ flag, narr), ct)
117-
? sn.toResult() : null;
118-
}
119-
120-
boolean renew(final TrieMap<K, V> ct, final INode<K, V> in, final Gen ngen) {
121-
return in.gcasWrite(renewed(ngen, ct), ct);
122141
}
123142

124143
boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int pos, final SNode<K, V> sn,
@@ -140,7 +159,37 @@ boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int po
140159
return in.gcasWrite(rn.toInsertedAt(this, ngen, pos, flag, key, val, hc), ct);
141160
}
142161

143-
@Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int pos,
162+
@Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key,
163+
final V val, final Object cond, final int lev, final INode<K, V> parent) {
164+
// 1) a multiway node
165+
final int idx = hc >>> lev & 0x1f;
166+
final int flag = 1 << idx;
167+
final int bmp = bitmap;
168+
final int mask = flag - 1;
169+
final int pos = Integer.bitCount(bmp & mask);
170+
171+
if ((bmp & flag) == 0) {
172+
// not found
173+
return cond != null && cond != ABSENT || insert(ct, parent, pos, flag, key, val, hc)
174+
? Result.empty() : null;
175+
}
176+
177+
// 1a) insert below
178+
final var cnAtPos = array[pos];
179+
if (cnAtPos instanceof INode<K, V> in) {
180+
// enter next level
181+
if (startGen != in.gen && !renew(ct, parent, startGen)) {
182+
return null;
183+
}
184+
return in.insertIf(ct, startGen, hc, key, val, cond, lev + LEVEL_BITS, parent);
185+
} else if (cnAtPos instanceof SNode<K, V> sn) {
186+
return insertIf(ct, parent, pos, sn, key, val, hc, cond, lev);
187+
} else {
188+
throw invalidElement(cnAtPos);
189+
}
190+
}
191+
192+
private @Nullable Result<V> insertIf(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int pos,
144193
final SNode<K, V> sn, final K key, final V val, final int hc, final Object cond, final int lev) {
145194
if (!sn.matches(hc, key)) {
146195
if (cond == null || cond == ABSENT) {
@@ -159,44 +208,74 @@ boolean insert(final MutableTrieMap<K, V> ct, final INode<K, V> in, final int po
159208
return Result.empty();
160209
}
161210

162-
MainNode<K, V> toContracted(final CNode<K, V> prev, final int lev) {
163-
final var sn = onlySNode(array, lev);
164-
return sn == null ? new CNode<>(prev, gen, bitmap, array) : sn.copyTombed(prev);
165-
}
211+
@Nullable Result<V> remove(final MutableTrieMap<K, V> ct, final Gen startGen, final int hc, final K key,
212+
final Object cond, final int lev, final INode<K, V> parent) {
213+
final int idx = hc >>> lev & 0x1f;
214+
final int flag = 1 << idx;
215+
if ((bitmap & flag) == 0) {
216+
return Result.empty();
217+
}
166218

167-
static <K, V> MainNode<K, V> dual(final SNode<K, V> first, final K key, final V value, final int hc,
168-
final int initLev, final Gen gen) {
169-
final var second = new SNode<>(key, value, hc);
170-
final var fhc = first.hc();
219+
final int pos = Integer.bitCount(bitmap & flag - 1);
220+
final var sub = array[pos];
221+
if (sub instanceof INode<K, V> in) {
222+
// renew if needed
223+
return startGen != in.gen && !renew(ct, parent, startGen)
224+
? null : in.remove(ct, startGen, hc, key, cond, lev + LEVEL_BITS, parent);
225+
} else if (sub instanceof SNode<K, V> sn) {
226+
if (!sn.matches(hc, key) || cond != null && !cond.equals(sn.value())) {
227+
return Result.empty();
228+
}
229+
return parent.gcasWrite(toRemoved(ct, flag, pos, lev), ct) ? sn.toResult() : null;
230+
} else {
231+
throw invalidElement(sub);
232+
}
233+
}
171234

172-
// recursion control
173-
final var bmps = new int[MAX_DEPTH];
174-
int len = 0;
175-
int lev = initLev;
235+
private MainNode<K, V> toRemoved(final MutableTrieMap<K, V> ct, final int flag, final int pos, final int lev) {
236+
final var arr = array;
237+
final int len = arr.length;
238+
final var narr = newArray(len - 1);
239+
System.arraycopy(arr, 0, narr, 0, pos);
240+
System.arraycopy(arr, pos + 1, narr, pos, len - pos - 1);
176241

177-
while (true) {
178-
MainNode<K, V> deepest;
179-
if (lev < HASH_BITS) {
180-
final int xidx = fhc >>> lev & 0x1f;
181-
final int yidx = hc >>> lev & 0x1f;
182-
final int bmp = 1 << xidx | 1 << yidx;
183-
if (xidx == yidx) {
184-
// enter recursion: save bitmap and increment lev
185-
bmps[len++] = bmp;
186-
lev += LEVEL_BITS;
187-
continue;
188-
}
189-
deepest = xidx < yidx ? new CNode<>(gen, bmp, first, second) : new CNode<>(gen, bmp, second, first);
190-
} else {
191-
deepest = new LNode<>(first, second);
192-
}
242+
return toUpdated(gen, lev, narr, bitmap ^ flag);
243+
}
193244

194-
while (len > 0) {
195-
// exit recursion: load bitmap and wrap deepest with a CNode
196-
deepest = new CNode<>(gen, bmps[--len], new INode<>(gen, deepest));
197-
}
198-
return deepest;
245+
// - if the branching factor is 1 for this CNode, and the child is a tombed SNode, returns its tombed version
246+
// - otherwise, if there is at least one non-null node below, returns the version of this node with at least some
247+
// null-inodes removed (those existing when the op began)
248+
// - if there are only null-i-nodes below, returns null
249+
MainNode<K, V> toCompressed(final TrieMap<K, V> ct, final Gen ngen, final int lev) {
250+
final var arr = array;
251+
final int len = arr.length;
252+
final var narr = newArray(len);
253+
for (int i = 0; i < len; i++) {
254+
final var tmp = arr[i];
255+
narr[i] = tmp instanceof INode<K, V> in ? resurrect(in, ct) : tmp;
199256
}
257+
258+
return toUpdated(ngen, lev, narr, bitmap);
259+
}
260+
261+
MainNode<K, V> toContracted(final Gen ngen, final int pos, final TNode<K, V> tn, final int lev) {
262+
final int len = array.length;
263+
final var narr = newArray(len);
264+
System.arraycopy(array, 0, narr, 0, len);
265+
narr[pos] = new SNode<>(tn);
266+
267+
return toUpdated(ngen, lev, narr, bitmap);
268+
}
269+
270+
private MainNode<K, V> toUpdated(final Gen ngen, final int lev, final Branch<K, V>[] arr, final int bmp) {
271+
// Note: special-case for root, so we always have a ct.root.main is always a CNode
272+
return lev > 0 && arr.length == 1 && arr[0] instanceof SNode<K, V> sn
273+
? new TNode<>(this, sn) : new CNode<>(this, ngen, bmp, arr);
274+
}
275+
276+
// tries to gcasWrite() a copy of this CNode renewed to ngen
277+
private boolean renew(final TrieMap<K, V> ct, final INode<K, V> in, final Gen ngen) {
278+
return in.gcasWrite(renewed(ngen, ct), ct);
200279
}
201280

202281
@Override
@@ -238,13 +317,13 @@ private static int elementSize(final Branch<?, ?> elem, final ImmutableTrieMap<?
238317
if (elem instanceof SNode) {
239318
return 1;
240319
} else if (elem instanceof INode<?, ?> inode) {
241-
return inode.size(ct);
320+
return inode.readSize(ct);
242321
} else {
243-
throw TrieMap.invalidElement(elem);
322+
throw invalidElement(elem);
244323
}
245324
}
246325

247-
CNode<K, V> updatedAt(final int pos, final Branch<K, V> nn, final Gen ngen) {
326+
private CNode<K, V> updatedAt(final int pos, final Branch<K, V> nn, final Gen ngen) {
248327
return toUpdatedAt(this, pos, nn, ngen);
249328
}
250329

@@ -285,22 +364,6 @@ private CNode<K, V> renewed(final Gen ngen, final TrieMap<K, V> ct) {
285364
return new CNode<>(this, ngen, bitmap, narr);
286365
}
287366

288-
// - if the branching factor is 1 for this CNode, and the child is a tombed SNode, returns its tombed version
289-
// - otherwise, if there is at least one non-null node below, returns the version of this node with at least some
290-
// null-inodes removed (those existing when the op began)
291-
// - if there are only null-i-nodes below, returns null
292-
MainNode<K, V> toCompressed(final TrieMap<K, V> ct, final int lev, final Gen ngen) {
293-
final var arr = array;
294-
final int len = arr.length;
295-
final var narr = newArray(len);
296-
for (int i = 0; i < len; i++) {
297-
final var tmp = arr[i];
298-
narr[i] = tmp instanceof INode<K, V> in ? resurrect(in, ct) : tmp;
299-
}
300-
final var sn = onlySNode(narr, lev);
301-
return sn != null ? sn.copyTombed(this) : new CNode<>(this, ngen, bitmap, narr);
302-
}
303-
304367
@Override
305368
public String toString() {
306369
return "CNode";
@@ -311,11 +374,12 @@ private Branch<K, V>[] newArray(final int size) {
311374
return new Branch[size];
312375
}
313376

314-
private @Nullable SNode<K, V> onlySNode(final Branch<K, V>[] arr, final int lev) {
315-
return arr.length == 1 && lev > 0 && arr[0] instanceof SNode<K, V> sn ? sn : null;
377+
private static <K, V> Branch<K, V> resurrect(final INode<K, V> in, final TrieMap<K, V> ct) {
378+
return in.gcasReadNonNull(ct) instanceof TNode<K, V> tn ? new SNode<>(tn) : in;
316379
}
317380

318-
private static <K, V> Branch<K, V> resurrect(final INode<K, V> in, final TrieMap<K, V> ct) {
319-
return in.gcasReadNonNull(ct) instanceof TNode<K, V> tnode ? tnode.copyUntombed() : in;
381+
// Visible for testing
382+
static VerifyException invalidElement(final Branch<?, ?> elem) {
383+
throw new VerifyException("A CNode can contain only INodes and SNodes, not " + elem);
320384
}
321385
}

0 commit comments

Comments
 (0)