All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jhotdraw8.icollection.impl.champmap.Node Maven / Gradle / Ivy

/*
 * @(#)Node.java
 * Copyright © 2022 The authors and contributors of JHotDraw. MIT License.
 */

package org.jhotdraw8.icollection.impl.champmap;

import org.jhotdraw8.icollection.impl.IdentityObject;
import org.jspecify.annotations.Nullable;

import java.util.Objects;
import java.util.function.ToIntFunction;

/**
 * Represents a node in a CHAMP trie.
 * 

* A node can store entries which have a key, a value (optionally) and a * sequence number (optionally). * * @param the key type * @param the value type */ public abstract class Node { static final int HASH_CODE_LENGTH = 32; /** * Bit partition size in the range [1,5]. *

* The bit-mask must fit into the 32 bits of an int field ({@code 32 = 1<<5}). * (You can use a size of 6, if you replace the bit-mask fields with longs). */ static final int BIT_PARTITION_SIZE = 5; static final int BIT_PARTITION_MASK = (1 << BIT_PARTITION_SIZE) - 1; /** * Represents no value. * We can not use {@code null}, because we allow storing null-keys and * null-values in the trie. */ public static final Object NO_DATA = new IdentityObject(); static final int MAX_DEPTH = (HASH_CODE_LENGTH + BIT_PARTITION_SIZE - 1) / BIT_PARTITION_SIZE + 1; static final int ENTRY_LENGTH = 2; Node() { } /** * Given a masked keyHash, returns its bit-position * in the bit-map. *

* For example, if the bit partition is 5 bits, then * we 2^5 == 32 distinct bit-positions. * If the masked keyHash is 3 then the bit-position is * the bit with index 3. That is, 1<<3 = 0b0100. * * @param mask masked key hash * @return bit position */ static int bitpos(final int mask) { return 1 << mask; } /** * Given a bitmap and a bit-position, returns the index * in the array. *

* For example, if the bitmap is 0b1101 and * bit-position is 0b0100, then the index is 1. * * @param bitmap a bit-map * @param bitpos a bit-position * @return the array index */ static int index(final int bitmap, final int bitpos) { return Integer.bitCount(bitmap & (bitpos - 1)); } static int mask(final int keyHash, final int shift) { return (keyHash >>> shift) & BIT_PARTITION_MASK; } Node mergeTwoDataEntriesIntoNode(IdentityObject mutator, final K k0, final V v0, final int keyHash0, final K k1, final V v1, final int keyHash1, final int shift) { assert !Objects.equals(k0, k1); if (shift >= HASH_CODE_LENGTH) { Object[] entries = new Object[ENTRY_LENGTH * 2]; entries[0] = k0; entries[ENTRY_LENGTH] = k1; if (ENTRY_LENGTH > 1) { entries[1] = v0; entries[ENTRY_LENGTH + 1] = v1; } return ChampTrie.newHashCollisionNode(mutator, keyHash0, entries, ENTRY_LENGTH); } final int mask0 = mask(keyHash0, shift); final int mask1 = mask(keyHash1, shift); if (mask0 != mask1) { // both nodes fit on same level final int dataMap = bitpos(mask0) | bitpos(mask1); Object[] entries = new Object[ENTRY_LENGTH * 2]; if (mask0 < mask1) { entries[0] = k0; entries[ENTRY_LENGTH] = k1; if (ENTRY_LENGTH > 1) { entries[1] = v0; entries[ENTRY_LENGTH + 1] = v1; } } else { entries[0] = k1; entries[ENTRY_LENGTH] = k0; if (ENTRY_LENGTH > 1) { entries[1] = v1; entries[ENTRY_LENGTH + 1] = v0; } } return ChampTrie.newBitmapIndexedNode(mutator, (0), dataMap, entries); } else { final Node node = mergeTwoDataEntriesIntoNode(mutator, k0, v0, keyHash0, k1, v1, keyHash1, shift + BIT_PARTITION_SIZE ); // values fit on next level final int nodeMap = bitpos(mask0); return ChampTrie.newBitmapIndexedNode(mutator, nodeMap, (0), new Object[]{node}); } } abstract int dataArity(); abstract boolean hasDataArityOne(); /** * Checks if this trie is equivalent to the specified other trie. * * @param other the other trie * @return true if equivalent */ abstract boolean equivalent(final Object other); /** * Finds a value by a key. * * @param key a key * @param keyHash the hash code of the key * @param shift the shift for this node * @return the value, returns {@link #NO_DATA} if the value is not present. */ abstract Object findByKey(final K key, final int keyHash, final int shift); abstract Object[] getDataEntry(final int index); public abstract K getKey(final int index); abstract EditableMapEntry getMapEntry(final int index); @Nullable IdentityObject getMutator() { return null; } abstract Node getNode(final int index); public abstract V getValue(final int index); abstract boolean hasData(); abstract boolean hasNodes(); boolean isAllowedToEdit(@Nullable IdentityObject y) { IdentityObject x = getMutator(); return x != null && x == y; } abstract int nodeArity(); abstract Node remove(final @Nullable IdentityObject mutator, final K key, final int keyHash, final int shift, final ChangeEvent details); abstract Node put(final @Nullable IdentityObject mutator, final K key, final V val, final int keyHash, final int shift, final ChangeEvent details, ToIntFunction hashFunction); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy