@@ -117,9 +117,7 @@ private static int labelsHashCode(String... labelValues) {
117117 }
118118
119119 private static int hash (int hashCode , int length ) {
120- // Multiply by -127, and left-shift to use least bit as part of hash
121- // Based on the equivalent function in java.util.IdentityHashMap
122- return ((hashCode << 1 ) - (hashCode << 8 )) & (length - 1 );
120+ return hashCode & (length - 1 ); // length is a power of 2
123121 }
124122
125123 /**
@@ -165,6 +163,9 @@ public Child labels() {
165163 validateCount (0 );
166164 return noLabelsChild ;
167165 }
166+
167+ // Logic for the fixed-arg overloads is identical apart from number of strings
168+ // used for hashcode generation and key comparison
168169
169170 public Child labels (String v1 ) {
170171 validateCount (1 );
@@ -232,49 +233,64 @@ public Child labels(String v1, String v2, String v3, String v4) {
232233 }
233234
234235 private Child labelsMiss (int hashCode , ChildEntry <Child >[] arr , int pos , String ... values ) {
235- return updateChild (values , hashCode , arr , pos ,
236+ return updateChild (values , hashCode , true , arr , pos ,
236237 new ChildEntry <Child >(values , hashCode , newChild ()));
237238 }
238239
239240 /**
240241 * Multi-purpose used for set, remove and get after not found (cache miss)
241242 *
242- * @param pos -1 for set and remove, otherwise insert (null) position in arr
243+ * @param getIfPresent if true, entry won't be updated if it already exists
244+ * @param pos insertion position for new entry if known (get case), otherwise -1
243245 * @param newEntry null for remove, otherwise new entry to add
244246 * @return new/existing Child in case of get, prior Child in case of set/remove
245247 */
246248 @ SuppressWarnings ("unchecked" )
247- private Child updateChild (String [] values , int hashCode ,
249+ private Child updateChild (String [] values , int hashCode , boolean getIfPresent ,
248250 ChildEntry <Child >[] arr , int pos , ChildEntry <Child > newEntry ) {
249- final boolean set = pos == -1 ;
250- int arrLen = arr .length ;
251+ // This loop is just for retries after CAS failures of the children field
251252 while (true ) {
253+ final int arrLen = arr .length ;
252254 final ChildEntry <Child >[] newArr ;
255+ // This gets set to the entry we're replacing, if/when applicable
253256 ChildEntry <Child > replacing = null ;
257+ // If pos >= 0, we already know where the new entry should go
254258 if (pos == -1 ) {
259+ // Scan the table looking for the a matching entry starting at
260+ // the labels' hash position
255261 pos = hash (hashCode , arrLen );
256262 while (true ) {
257263 ChildEntry <Child > ce = arr [pos ];
258264 if (ce == null ) {
265+ // If we reach a null entry then the labels aren't present, and
266+ // our current pos is the target location for inserting the new entry
259267 if (newEntry == null ) {
268+ // Nothing to do in removal case
260269 return null ;
261270 }
262271 break ;
263272 }
264273 if (ce .hashCode == hashCode && Arrays .equals (ce .labels , values )) {
265- if ( set ) {
266- replacing = ce ;
267- break ;
274+ // We found an entry matching the target labels
275+ if ( getIfPresent ) {
276+ return ce . child ;
268277 }
269- return ce .child ;
278+ replacing = ce ;
279+ break ;
270280 }
271- pos = nextIdx (pos , arrLen );
281+ pos = nextIdx (pos , arrLen ); // wrap around
272282 }
273283 }
274284 boolean resizeNeeded ;
285+ // If we reached here we'll be updating the table by either inserting
286+ // replacing, or removing an entry
275287 if (newEntry == null | replacing != null ) {
288+ // No need to increase the table capacity if removing or replacing
276289 resizeNeeded = false ;
277290 } else {
291+ // We are inserting a new entry so count the existing entries to determine
292+ // how full the table is - we resize (double) it if greater than half full.
293+ // This avoids having to separately track the count
278294 int count = 1 ;
279295 for (ChildEntry <Child > ce : arr ) {
280296 if (ce != null ) {
@@ -284,10 +300,14 @@ private Child updateChild(String[] values, int hashCode,
284300 resizeNeeded = count > arrLen / 2 ;
285301 }
286302 if (resizeNeeded ) {
287- newArr = new ChildEntry [arrLen * 2 ];
288- int newArrLen = newArr .length ;
303+ // Double the size of the table
304+ final int newArrLen = arrLen * 2 ;
305+ newArr = new ChildEntry [newArrLen ];
306+ // Insert our new entry first
289307 newArr [hash (hashCode , newArrLen )] = newEntry ;
308+ // Then re-hash/insert the existing entries
290309 for (ChildEntry <Child > ce : arr ) {
310+ // Exclude the entry we're replacing (if applicable - replacing will be non-null)
291311 if (ce != null & ce != replacing ) {
292312 for (int j = hash (ce .hashCode , newArrLen );; j = nextIdx (j , newArrLen )) {
293313 if (newArr [j ] == null ) {
@@ -298,17 +318,20 @@ private Child updateChild(String[] values, int hashCode,
298318 }
299319 }
300320 } else {
321+ // If we're not resizing the table, just make a straight copy
322+ // and insert our new entry at the target position
301323 newArr = Arrays .copyOf (arr , arrLen , ChildEntry [].class );
302324 newArr [pos ] = newEntry ;
303325 }
326+ // Attempt to update the shared field atomically
304327 if (UPDATER .compareAndSet (this , arr , newArr )) {
305- if ( set ) {
306- return replacing != null ? replacing . child : null ;
307- }
308- return newEntry .child ;
328+ // Successful table modification
329+ // Return the new entry in the get case, old entry in the remove/set cases
330+ return getIfPresent ? newEntry . child
331+ : ( replacing != null ? replacing .child : null ) ;
309332 }
333+ // Someone else changed the table, read it and start again
310334 arr = children ;
311- arrLen = arr .length ;
312335 pos = -1 ;
313336 }
314337 }
@@ -319,7 +342,7 @@ private Child updateChild(String[] values, int hashCode,
319342 * Any references to the Child are invalidated.
320343 */
321344 public void remove (String ... labelValues ) {
322- updateChild (labelValues , labelsHashCode (labelValues ), children , -1 , null );
345+ updateChild (labelValues , labelsHashCode (labelValues ), false , children , -1 , null );
323346 initializeNoLabelsChild ();
324347 }
325348
@@ -369,7 +392,7 @@ protected void initializeNoLabelsChild() {
369392 public <T extends Collector > T setChild (Child child , String ... labelValues ) {
370393 validateCount (labelValues .length );
371394 int hashCode = labelsHashCode (labelValues );
372- updateChild (labelValues , hashCode , children , -1 ,
395+ updateChild (labelValues , hashCode , false , children , -1 ,
373396 new ChildEntry <Child >(labelValues , hashCode , child ));
374397 return (T )this ;
375398 }
0 commit comments