Skip to content

Commit

Permalink
Merge pull request #24 from bruno-roustant/indexremove
Browse files Browse the repository at this point in the history
Support indexRemove in KTypeVTypeHashMap,KTypeHashSet,KTypeWormSet.
  • Loading branch information
dweiss committed Jun 5, 2021
2 parents 104d57f + 4f9afa4 commit 0e18c7d
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 54 deletions.
2 changes: 2 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

** New features and API changes

GH-24: Support indexRemove in KTypeVTypeHashMap,KTypeHashSet,KTypeWormSet (Bruno Roustant).

GH-20: KeysContainer of WormMap is not public (Haoyu Zhai, Bruno Roustant).

GH-13: Add automatic module name to the generated JAR's manifest ("com.carrotsearch.hppc")
Expand Down
7 changes: 5 additions & 2 deletions hppc/src/main/java/com/carrotsearch/hppc/WormUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ static int findPreviousInChain(int entryIndex, byte[] next) {
*
* @param index The index of an entry in the chain.
* @param nextOffset next[index].
* @return The index of the last entry in the chain.
* @param returnPrevious Whether to return the entry before the last.
* @return The index of the last entry in the chain, or the entry before, depending on {@code
* returnPrevious}. Returns {@code -1} if the entry at index is the last entry of the chain
* and {@code returnPrevious} is true.
*/
static int findLastOfChain(int index, int nextOffset, boolean returnPrevious, byte[] next) {
assert checkIndex(index, next.length);
Expand All @@ -153,7 +156,7 @@ static int findLastOfChain(int index, int nextOffset, boolean returnPrevious, by
if (nextOffset < 0) {
nextOffset = -nextOffset;
}
int previousIndex = Integer.MAX_VALUE;
int previousIndex = -1;
while (nextOffset != END_OF_CHAIN) {
previousIndex = index;
index = addOffset(index, nextOffset, capacity); // Jump forward.
Expand Down
21 changes: 21 additions & 0 deletions hppc/src/main/templates/com/carrotsearch/hppc/KTypeHashSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,27 @@ public void indexInsert(int index, KType key) {
}
}

/**
* Removes a key at an index previously acquired from {@link #indexOf}.
*
* @see #indexOf
*
* @param index The index of the key to remove, as returned from {@link #indexOf}.
* @throws AssertionError If assertions are enabled and the index does
* not correspond to an existing key.
*/
public void indexRemove(int index) {
assert index >= 0 : "The index must point at an existing key.";
assert index <= mask ||
(index == mask + 1 && hasEmptyKey);

if (index > mask) {
hasEmptyKey = false;
} else {
shiftConflictingKeys(index);
}
}

@Override
public String visualizeKeyDistribution(int characters) {
return KTypeBufferVisualizer.visualizeKeyDistribution(keys, mask, characters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,25 @@ public void indexInsert(int index, KType key, VType value) {
}
}

/**
* {@inheritDoc}
*/
@Override
public VType indexRemove(int index) {
assert index >= 0 : "The index must point at an existing key.";
assert index <= mask ||
(index == mask + 1 && hasEmptyKey);

VType previousValue = Intrinsics.<VType> cast(values[index]);
if (index > mask) {
hasEmptyKey = false;
values[index] = Intrinsics.<VType> empty();
} else {
shiftConflictingKeys(index);
}
return previousValue;
}

/**
* {@inheritDoc}
*/
Expand Down
14 changes: 14 additions & 0 deletions hppc/src/main/templates/com/carrotsearch/hppc/KTypeVTypeMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,20 @@ public interface KTypeVTypeMap<KType, VType> extends KTypeVTypeAssociativeContai
*/
public void indexInsert(int index, KType key, VType value);

/**
* Removes a key-value pair at an index previously acquired from {@link #indexOf}.
*
* @see #indexOf
*
* @param index
* The index of the key to remove, as returned from {@link #indexOf}.
* @return Returns the previous value associated with the key.
* @throws AssertionError
* If assertions are enabled and the index does not correspond to an
* existing key.
*/
public VType indexRemove(int index);

/**
* Clear all keys and values in the container.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public VType remove(KType key) {
}
int entryToRemoveIndex = previousEntryIndex == Integer.MAX_VALUE ?
hashIndex : addOffset(previousEntryIndex, Math.abs(next[previousEntryIndex]), next.length);
return remove(hashIndex, previousEntryIndex, entryToRemoveIndex);
return remove(entryToRemoveIndex, previousEntryIndex);
}

/** {@inheritDoc} */
Expand Down Expand Up @@ -505,13 +505,15 @@ public boolean indexExists(int index) {
@Override
public VType indexGet(int index) {
assert checkIndex(index, next.length);
assert next[index] != 0;
return Intrinsics.<VType>cast(values[index]);
}

/** {@inheritDoc} */
@Override
public VType indexReplace(int index, VType newValue) {
assert checkIndex(index, next.length);
assert next[index] != 0;
VType previousValue = Intrinsics.<VType>cast(values[index]);
values[index] = newValue;
return previousValue;
Expand All @@ -532,6 +534,14 @@ public void indexInsert(int index, KType key, VType value) {
}
}

/** {@inheritDoc} */
@Override
public VType indexRemove(int index) {
assert checkIndex(index, next.length);
assert next[index] != 0;
return remove(index, Integer.MAX_VALUE);
}

/** {@inheritDoc} */
@Override
public String toString() {
Expand Down Expand Up @@ -705,40 +715,42 @@ private void enlargeAndPutNewEntry(KType key, VType value) {
* Removes the entry at the specified removal index.
* Decrements {@link #size}.
*
* @param headIndex The head-of-chain index.
* @param previousEntryIndex The index of the entry in the chain preceding the entry to remove; or
* {@link Integer#MAX_VALUE} if the entry to remove is the head-of-chain.
* @param entryToRemoveIndex The index of the entry to remove.
* @param previousEntryIndex The index of the entry in the chain preceding the entry to remove; or
* {@link Integer#MAX_VALUE} if unknown or if the entry to remove is the head-of-chain.
* @return The value of the removed entry.
*/
private VType remove(int headIndex, int previousEntryIndex, int entryToRemoveIndex) {
assert checkIndex(headIndex, next.length);
assert next[headIndex] > 0;
assert previousEntryIndex == Integer.MAX_VALUE || checkIndex(previousEntryIndex, next.length);
private VType remove(int entryToRemoveIndex, int previousEntryIndex) {
assert checkIndex(entryToRemoveIndex, next.length);
assert previousEntryIndex == Integer.MAX_VALUE || checkIndex(previousEntryIndex, next.length);

final byte[] next = this.next;
VType previousValue = Intrinsics.<VType>cast(values[entryToRemoveIndex]);

// Find the last entry of the chain.
int beforeLastIndex = findLastOfChain(entryToRemoveIndex, next[entryToRemoveIndex], true, next);
// Replace the removed entry by the last entry of the chain.
int nextOffset = next[entryToRemoveIndex];
int beforeLastIndex = findLastOfChain(entryToRemoveIndex, nextOffset, true, next);
int lastIndex;
if (beforeLastIndex == Integer.MAX_VALUE) {
beforeLastIndex = previousEntryIndex;
if (beforeLastIndex == -1) {
// The entry to remove is the last of the chain.
lastIndex = entryToRemoveIndex;
if (nextOffset < 0) {
// Removing the last entry in a chain of at least two entries.
beforeLastIndex = previousEntryIndex == Integer.MAX_VALUE ?
findPreviousInChain(entryToRemoveIndex, next) : previousEntryIndex;
// Unlink the last entry which replaces the removed entry.
next[beforeLastIndex] = (byte) (next[beforeLastIndex] > 0 ? END_OF_CHAIN : -END_OF_CHAIN);
}
} else {
lastIndex = addOffset(beforeLastIndex, Math.abs(next[beforeLastIndex]), next.length);
}

// Replace the removed entry by the last entry of the chain.
if (entryToRemoveIndex != lastIndex) {
// Removing an entry before the last of the chain. Replace it by the last one.
int beforeLastNextOffset = next[beforeLastIndex];
lastIndex = addOffset(beforeLastIndex, Math.abs(beforeLastNextOffset), next.length);
assert entryToRemoveIndex != lastIndex;
// The entry to remove is before the last of the chain. Replace it by the last one.
keys[entryToRemoveIndex] = keys[lastIndex];
values[entryToRemoveIndex] = values[lastIndex];
}
if (lastIndex != headIndex) {
// Removing an entry in a chain of at least two entries. Unlink the last entry which replaces the removed entry.
next[beforeLastIndex] = (byte) (beforeLastIndex == headIndex ? END_OF_CHAIN : -END_OF_CHAIN);
// Unlink the last entry which replaces the removed entry.
next[beforeLastIndex] = (byte) (beforeLastNextOffset > 0 ? END_OF_CHAIN : -END_OF_CHAIN);
}
// Free the last entry of the chain.
keys[lastIndex] = Intrinsics.<KType>empty();
Expand Down
61 changes: 40 additions & 21 deletions hppc/src/main/templates/com/carrotsearch/hppc/KTypeWormSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public boolean remove(KType key) {
}
int entryToRemoveIndex = previousEntryIndex == Integer.MAX_VALUE ?
hashIndex : addOffset(previousEntryIndex, Math.abs(next[previousEntryIndex]), next.length);
remove(hashIndex, previousEntryIndex, entryToRemoveIndex);
remove(entryToRemoveIndex, previousEntryIndex);
return true;
}

Expand Down Expand Up @@ -457,6 +457,7 @@ public boolean indexExists(int index) {
*/
public KType indexGet(int index) {
assert checkIndex(index, next.length);
assert next[index] != 0;
return Intrinsics.<KType>cast(keys[index]);
}

Expand All @@ -475,6 +476,7 @@ public KType indexGet(int index) {
*/
public KType indexReplace(int index, KType equivalentKey) {
assert checkIndex(index, next.length);
assert next[index] != 0;
assert Intrinsics.equals(this, keys[index], equivalentKey);
KType previousKey = Intrinsics.<KType>cast(keys[index]);
keys[index] = equivalentKey;
Expand Down Expand Up @@ -504,6 +506,21 @@ public void indexInsert(int index, KType key) {
}
}

/**
* Removes a key at an index previously acquired from {@link #indexOf}.
*
* @see #indexOf
*
* @param index The index of the key to remove, as returned from {@link #indexOf}.
* @throws AssertionError If assertions are enabled and the index does
* not correspond to an existing key.
*/
public void indexRemove(int index) {
assert checkIndex(index, next.length);
assert next[index] != 0;
remove(index, Integer.MAX_VALUE);
}

/** {@inheritDoc} */
@Override
public String toString() {
Expand Down Expand Up @@ -665,37 +682,39 @@ private void enlargeAndPutNewEntry(KType key) {
* Removes the entry at the specified removal index.
* Decrements {@link #size}.
*
* @param headIndex The head-of-chain index.
* @param previousEntryIndex The index of the entry in the chain preceding the entry to remove; or
* {@link Integer#MAX_VALUE} if the entry to remove is the head-of-chain.
* @param entryToRemoveIndex The index of the entry to remove.
* @param previousEntryIndex The index of the entry in the chain preceding the entry to remove; or
* {@link Integer#MAX_VALUE} if unknown or if the entry to remove is the head-of-chain.
*/
private void remove(int headIndex, int previousEntryIndex, int entryToRemoveIndex) {
assert checkIndex(headIndex, next.length);
assert next[headIndex] > 0;
assert previousEntryIndex == Integer.MAX_VALUE || checkIndex(previousEntryIndex, next.length);
private void remove(int entryToRemoveIndex, int previousEntryIndex) {
assert checkIndex(entryToRemoveIndex, next.length);
assert previousEntryIndex == Integer.MAX_VALUE || checkIndex(previousEntryIndex, next.length);

final byte[] next = this.next;

// Find the last entry of the chain.
int beforeLastIndex = findLastOfChain(entryToRemoveIndex, next[entryToRemoveIndex], true, next);
// Replace the removed entry by the last entry of the chain.
int nextOffset = next[entryToRemoveIndex];
int beforeLastIndex = findLastOfChain(entryToRemoveIndex, nextOffset, true, next);
int lastIndex;
if (beforeLastIndex == Integer.MAX_VALUE) {
beforeLastIndex = previousEntryIndex;
if (beforeLastIndex == -1) {
// The entry to remove is the last of the chain.
lastIndex = entryToRemoveIndex;
if (nextOffset < 0) {
// Removing the last entry in a chain of at least two entries.
beforeLastIndex = previousEntryIndex == Integer.MAX_VALUE ?
findPreviousInChain(entryToRemoveIndex, next) : previousEntryIndex;
// Unlink the last entry which replaces the removed entry.
next[beforeLastIndex] = (byte) (next[beforeLastIndex] > 0 ? END_OF_CHAIN : -END_OF_CHAIN);
}
} else {
lastIndex = addOffset(beforeLastIndex, Math.abs(next[beforeLastIndex]), next.length);
}

// Replace the removed entry by the last entry of the chain.
if (entryToRemoveIndex != lastIndex) {
// Removing an entry before the last of the chain. Replace it by the last one.
int beforeLastNextOffset = next[beforeLastIndex];
lastIndex = addOffset(beforeLastIndex, Math.abs(beforeLastNextOffset), next.length);
assert entryToRemoveIndex != lastIndex;
// The entry to remove is before the last of the chain. Replace it by the last one.
keys[entryToRemoveIndex] = keys[lastIndex];
}
if (lastIndex != headIndex) {
// Removing an entry in a chain of at least two entries. Unlink the last entry which replaces the removed entry.
next[beforeLastIndex] = (byte) (beforeLastIndex == headIndex ? END_OF_CHAIN : -END_OF_CHAIN);
// Unlink the last entry which replaces the removed entry.
next[beforeLastIndex] = (byte) (beforeLastNextOffset > 0 ? END_OF_CHAIN : -END_OF_CHAIN);
}
// Free the last entry of the chain.
keys[lastIndex] = Intrinsics.<KType>empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ public void testIndexMethods()
set.indexInsert(set.indexOf(key2), key2);
Assertions.assertThat(set.indexGet(set.indexOf(key2))).isEqualTo(key2);
Assertions.assertThat(set.size()).isEqualTo(3);

set.indexRemove(set.indexOf(keyE));
Assertions.assertThat(set.size()).isEqualTo(2);
set.indexRemove(set.indexOf(key2));
Assertions.assertThat(set.size()).isEqualTo(1);
Assertions.assertThat(set.indexOf(keyE)).isNegative();
Assertions.assertThat(set.indexOf(key1)).isNotNegative();
Assertions.assertThat(set.indexOf(key2)).isNegative();
}

@Test
Expand Down Expand Up @@ -399,14 +407,32 @@ public void testAgainstHashSet()

if (rnd.nextBoolean())
{
if (rnd.nextBoolean()) {
int index = set.indexOf(cast(key));
if (set.indexExists(index)) {
set.indexReplace(index, cast(key));
} else {
set.indexInsert(index, cast(key));
}
} else {
set.add(cast(key));
}
other.add(cast(key));
set.add(cast(key));

assertTrue(set.contains(cast(key)));
assertTrue(set.indexExists(set.indexOf(cast(key))));
}
else
{
assertEquals(other.remove(key), set.remove(cast(key)));
assertEquals(other.contains(key), set.contains(cast(key)));
boolean removed;
if (set.contains(cast(key)) && rnd.nextBoolean()) {
set.indexRemove(set.indexOf(cast(key)));
removed = true;
} else {
removed = set.remove(cast(key));
}
assertEquals(other.remove(key), removed);
}

assertEquals(other.size(), set.size());
Expand Down
Loading

0 comments on commit 0e18c7d

Please sign in to comment.