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

com.hazelcast.query.impl.bitmap.SparseBitSet Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.query.impl.bitmap;

import static com.hazelcast.query.impl.bitmap.BitmapUtils.capacityDeltaInt;
import static com.hazelcast.query.impl.bitmap.BitmapUtils.capacityDeltaShort;
import static com.hazelcast.query.impl.bitmap.BitmapUtils.toUnsignedInt;
import static com.hazelcast.query.impl.bitmap.BitmapUtils.toUnsignedLong;
import static com.hazelcast.query.impl.bitmap.BitmapUtils.unsignedBinarySearch;
import static java.lang.Long.numberOfTrailingZeros;
import static java.lang.System.arraycopy;
import static java.util.Arrays.copyOf;

/**
 * Stores a set of bits indexable by non-negative {@code long} indexes.
 * 

* Internally, uses a {@link SparseIntArray} indexed by the high 32 bits (32-bit * prefix) to resolve a storage ({@link Storage32 Storage32}) for the low 32 * bits (32-bit postfix). *

* {@link Storage32 Storage32} goes in two flavors: *

    *
  • {@link ArrayStorage32 ArrayStorage32} which manages sorted int array of * 32-bit postfixes. *
  • {@link PrefixStorage32 PrefixStorage32} which splits 32-bit postfixes * into 16-bit prefixes and 16-bit postfixes. The high 16 bits are stored in * sorted array and used to lookup a storage ({@link Storage16 Storage16}) for * the low 16 bits. *
*

* {@link Storage16 Storage16} also goes in two flavors: *

    *
  • {@link ArrayStorage16 ArrayStorage16} which manages sorted short array of * 16-bit postfixes. *
  • {@link BitSetStorage16 BitSetStorage16} which manages directly indexable * long array of bits. *
*

* The implementation (which was inspired by Roaring Bitmap) switches between * various storage flavors once certain thresholds on storage size are reached. *

* Empty storages are never stored by the implementation. */ final class SparseBitSet { /** * The size at which ArrayStorage32 is converted to PrefixStorage32. * Inferred empirically: at this size we are not penalized too much for * doing binary searches. */ public static final int ARRAY_STORAGE_32_MAX_SIZE = 513; /** * The size at which ArrayStorage16 is converted to BitSetStorage16. At * this size the memory cost of having sorted short array is equal to the * cost of having directly indexable long array of bits. */ public static final int ARRAY_STORAGE_16_MAX_SIZE = 4096; private static final long INT_PREFIX_MASK = 0xFFFFFFFF00000000L; private static final long INT_POSTFIX_MASK = 0x00000000FFFFFFFFL; private static final long SHORT_PREFIX_MASK = 0x00000000FFFF0000L; private static final long INT_PREFIX_SHORT_POSTFIX_MASK = 0xFFFFFFFF0000FFFFL; private static final long INT_PREFIX_SHORT_PREFIX_MASK = 0xFFFFFFFFFFFF0000L; private static final long SHORT_POSTFIX_MASK = 0x000000000000FFFFL; private final SparseIntArray storages = new SparseIntArray(); // used for caching of the last resolved 32-bit storage private int lastPrefix = -1; private Storage32 lastStorage; /** * Adds the given member to this bit set. * * @param member the member to add. */ public void add(long member) { assert member >= 0; int prefix = (int) (member >>> Integer.SIZE); if (prefix == lastPrefix) { Storage32 newStorage = lastStorage.add((int) member); if (newStorage != lastStorage) { // storage was upgraded lastStorage = newStorage; storages.set(prefix, newStorage); } } else { lastPrefix = prefix; Storage32 storage = storages.get(prefix); if (storage == null) { Storage32 createdStorage = new ArrayStorage32((int) member); lastStorage = createdStorage; storages.set(prefix, createdStorage); } else { Storage32 newStorage = storage.add((int) member); if (newStorage == storage) { lastStorage = storage; } else { // storage was upgraded lastStorage = newStorage; storages.set(prefix, newStorage); } } } } /** * Removes the given member from this bit set. * * @param member the member to remove. * @return {@code true} if this storage became empty as a result of the * member removal, {@code false} otherwise. */ public boolean remove(long member) { assert member >= 0; int prefix = (int) (member >>> Integer.SIZE); if (prefix == lastPrefix) { if (lastStorage.remove((int) member)) { lastPrefix = -1; lastStorage = null; return storages.clear(prefix); } else { return false; } } else { Storage32 storage = storages.get(prefix); if (storage == null) { return false; } if (storage.remove((int) member)) { lastPrefix = -1; lastStorage = null; return storages.clear(prefix); } else { lastPrefix = prefix; lastStorage = storage; return false; } } } /** * @return an iterator that iterates over all the indexes of bits set in * this sparse bit set. */ public AscendingLongIterator iterator() { return new IteratorImpl(storages); } /** * Defines internal contract of storages responsible for storing of 32-bit * postfixes. */ private interface Storage32 { /** * Adds the given member to this storage. * * @param member the member to add. * @return a new storage instance if this storage was converted to * another storage flavor; this storage otherwise. */ Storage32 add(int member); /** * Removes the given member from this storage. * * @param member the member to remove. * @return {@code true} if this storage became empty as a result of the * member removal, {@code false} otherwise. */ boolean remove(int member); /** * Starts iteration on this storage using the given iterator. *

* Always succeeds since we never keep empty storages. * * @param iterator the iterator to iterate with. */ void iterate(IteratorImpl iterator); /** * Advances the given iterator on this storage. * * @param iterator the iterator to advance. * @return {@code true} if the iterator is advanced to the next member, * {@code false} if no members to iterate are left in this storage. */ boolean advance(IteratorImpl iterator); /** * Starts iteration on this storage starting from the given member using * the given iterator. * * @param member the member to start the iteration from. * @param iterator the iterator to iterate with. * @return {@code true} if the iterator is positioned to the given * member; or, if the member is not present in this storage, to a member * immediately following it and present in this storage or {@code false} * if no such member exists in this storage. */ boolean iterateAtLeastFrom(int member, IteratorImpl iterator); /** * Advances the given iterator to the given member; or, if the member is * not present in this storage, to a member immediately following it * and present in this storage. * * @param member the member to advance at least to. The member must be * greater than the member this iterator is currently at. * @param iterator the iterator to advance. * @return {@code true} if the iterator is advanced to the given * member; or, if the member is not present in this storage, to a member * immediately following it and present in this storage or {@code false} * if no such member exists in this storage. */ boolean advanceAtLeastTo(int member, IteratorImpl iterator); } /** * Manages sorted int array of indexes of set bits. */ private static final class ArrayStorage32 implements Storage32 { private static final int MIN_CAPACITY = 1; private int size; private int[] members; ArrayStorage32(int member) { this.size = 1; this.members = new int[MIN_CAPACITY]; members[0] = member; } @Override public Storage32 add(int member) { int index = unsignedBinarySearch(members, size, toUnsignedLong(member)); if (index >= 0) { // already in the array return this; } index = -(index + 1); if (size == members.length) { // No space left: try to grow members array. if (size == ARRAY_STORAGE_32_MAX_SIZE) { return new PrefixStorage32(members, member, index); } int newCapacity = Math.min(ARRAY_STORAGE_32_MAX_SIZE, size + capacityDeltaInt(members.length)); int[] newMembers = new int[newCapacity]; arraycopy(members, 0, newMembers, 0, index); arraycopy(members, index, newMembers, index + 1, size - index); members = newMembers; } else { // shift members right to free a slot for the new member arraycopy(members, index, members, index + 1, size - index); } members[index] = member; ++size; return this; } @Override public boolean remove(int member) { int index = unsignedBinarySearch(members, size, toUnsignedLong(member)); if (index < 0) { // not a member return false; } --size; if (size == 0) { // emptied return true; } int delta = capacityDeltaInt(members.length); int wasted = members.length - size; int newCapacity = members.length - delta; if (wasted >= delta && newCapacity >= MIN_CAPACITY) { // We are wasting too much: shrink the array. int[] newMembers = new int[newCapacity]; arraycopy(members, 0, newMembers, 0, index); arraycopy(members, index + 1, newMembers, index, size - index); members = newMembers; } else { // shift members left to fill the gap arraycopy(members, index + 1, members, index, size - index); } return false; } @Override public void iterate(IteratorImpl iterator) { assert size > 0; iterator.position32 = 1; iterator.index = iterator.index & INT_PREFIX_MASK | toUnsignedLong(members[0]); } @Override public boolean advance(IteratorImpl iterator) { int position = iterator.position32; if (position < size) { iterator.index = iterator.index & INT_PREFIX_MASK | toUnsignedLong(members[position]); iterator.position32 = position + 1; return true; } else { return false; } } @Override public boolean iterateAtLeastFrom(int member, IteratorImpl iterator) { long unsignedMember = toUnsignedLong(member); int position = unsignedBinarySearch(members, size, unsignedMember); if (position < 0) { position = -(position + 1); if (position == size) { return false; } unsignedMember = toUnsignedLong(members[position]); } iterator.index = iterator.index & INT_PREFIX_MASK | unsignedMember; iterator.position32 = position + 1; return true; } @Override public boolean advanceAtLeastTo(int member, IteratorImpl iterator) { long unsignedMember = toUnsignedLong(member); long current = iterator.index; assert (current & INT_POSTFIX_MASK) < unsignedMember; int position = iterator.position32; if (position == size) { return false; } position = unsignedBinarySearch(members, position, size, unsignedMember); if (position < 0) { position = -(position + 1); if (position == size) { return false; } unsignedMember = toUnsignedLong(members[position]); } iterator.index = current & INT_PREFIX_MASK | unsignedMember; iterator.position32 = position + 1; return true; } } /** * Manages sorted array of 16-bit prefixes to lookup storages for 16-bit * postfixes. */ private static final class PrefixStorage32 implements Storage32 { private static final int MIN_CAPACITY = 2; private static final int MAX_CAPACITY = 64 * 1024; private int size; private short[] prefixes; private Storage16[] storages; // used for caching of the last resolved 16-bit storage private int lastPrefix = -1; private Storage16 lastStorage; /** * Constructs a new prefix storage for the given sorted members array * and the given member to insert at the given index. */ PrefixStorage32(int[] members, int member, int index) { this.prefixes = new short[MIN_CAPACITY]; this.storages = new Storage16[MIN_CAPACITY]; for (int i = 0; i < index; ++i) { append(members[i]); } append(member); for (int i = index; i < members.length; ++i) { append(members[i]); } } @Override public Storage32 add(int member) { short prefix = (short) (member >>> Short.SIZE); int unsignedPrefix = toUnsignedInt(prefix); // Try to resolve the corresponding 16-bit postfix storage by its // 16-bit prefix. if (unsignedPrefix == lastPrefix) { // We are lucky: just add the member to the cached storage. Storage16 newStorage = lastStorage.add((short) member); // handle potential storage upgrade if (newStorage != lastStorage) { int index = unsignedBinarySearch(prefixes, size, unsignedPrefix); assert index >= 0; storages[index] = newStorage; lastStorage = newStorage; } return this; } int index = unsignedBinarySearch(prefixes, size, unsignedPrefix); if (index >= 0) { // The storage already exists: just add the member to it. Storage16 storage = storages[index]; Storage16 newStorage = storage.add((short) member); // handle potential storage upgrade if (newStorage != storage) { storages[index] = newStorage; } lastPrefix = unsignedPrefix; lastStorage = newStorage; return this; } index = -(index + 1); // No storage exists yet: we need to allocate a new postfix storage // and insert it into this prefix storage. if (size == prefixes.length) { // Grow the arrays. int newCapacity = Math.min(MAX_CAPACITY, size + capacityDeltaShort(prefixes.length)); short[] newPrefixes = new short[newCapacity]; arraycopy(prefixes, 0, newPrefixes, 0, index); arraycopy(prefixes, index, newPrefixes, index + 1, size - index); prefixes = newPrefixes; Storage16[] newStorages = new Storage16[newCapacity]; arraycopy(storages, 0, newStorages, 0, index); arraycopy(storages, index, newStorages, index + 1, size - index); storages = newStorages; } else { // Shift the arrays right to free a slot. arraycopy(prefixes, index, prefixes, index + 1, size - index); arraycopy(storages, index, storages, index + 1, size - index); } ArrayStorage16 createdStorage = new ArrayStorage16((short) member); prefixes[index] = prefix; storages[index] = createdStorage; lastPrefix = unsignedPrefix; lastStorage = createdStorage; ++size; return this; } @Override public boolean remove(int member) { short prefix = (short) (member >>> Short.SIZE); int unsignedPrefix = toUnsignedInt(prefix); // Try to resolve the corresponding 16-bit postfix storage by its // 16-bit prefix. Storage16 newStorage; int index; if (unsignedPrefix == lastPrefix) { // We are lucky: just remove the member from the cached storage. Storage16 storage = lastStorage; newStorage = storage.remove((short) member); if (newStorage == storage) { return false; } // To handle the storage downgrade or removal we need to know // its prefix index. index = unsignedBinarySearch(prefixes, size, unsignedPrefix); assert index >= 0; } else { index = unsignedBinarySearch(prefixes, size, unsignedPrefix); if (index < 0) { // no storage, no problems return false; } Storage16 storage = storages[index]; newStorage = storage.remove((short) member); if (newStorage == storage) { lastStorage = storage; lastPrefix = unsignedPrefix; return false; } } // The 16-bit postfix storage is either emptied or downgraded at // this point. if (newStorage == null) { // The postfix storage is emptied: remove it from this prefix // storage. --size; lastStorage = null; lastPrefix = -1; if (size == 0) { // this prefix storage is also emptied return true; } int delta = capacityDeltaShort(prefixes.length); int wasted = prefixes.length - size; int newCapacity = prefixes.length - delta; if (wasted >= delta && newCapacity >= MIN_CAPACITY) { // Wasting too much: shrink the arrays. short[] newPrefixes = new short[newCapacity]; arraycopy(prefixes, 0, newPrefixes, 0, index); arraycopy(prefixes, index + 1, newPrefixes, index, size - index); prefixes = newPrefixes; Storage16[] newStorages = new Storage16[newCapacity]; arraycopy(storages, 0, newStorages, 0, index); arraycopy(storages, index + 1, newStorages, index, size - index); storages = newStorages; } else { // Shift the arrays left to fill the gap. arraycopy(prefixes, index + 1, prefixes, index, size - index); arraycopy(storages, index + 1, storages, index, size - index); } } else { // The postfix storage is downgraded: update the records. lastStorage = newStorage; lastPrefix = unsignedPrefix; storages[index] = newStorage; } return false; } @Override public void iterate(IteratorImpl iterator) { assert size > 0; iterator.position32 = 1; Storage16 storage = storages[0]; iterator.storage16 = storage; iterator.index = iterator.index & INT_PREFIX_MASK | toUnsignedLong(prefixes[0]) << Short.SIZE; storage.iterate(iterator); } @Override public boolean advance(IteratorImpl iterator) { // Try advance the current postfix storage. if (iterator.storage16.advance(iterator)) { return true; } // Try to advance to the next postfix storage and iterate it. int position = iterator.position32; if (position < size) { Storage16 storage = storages[position]; iterator.storage16 = storage; iterator.index = iterator.index & INT_PREFIX_MASK | toUnsignedLong(prefixes[position]) << Short.SIZE; iterator.position32 = position + 1; storage.iterate(iterator); return true; } else { return false; } } @Override public boolean iterateAtLeastFrom(int member, IteratorImpl iterator) { return iterateAtLeastFrom(member, 0, iterator); } @Override public boolean advanceAtLeastTo(int member, IteratorImpl iterator) { short prefix = (short) (member >>> Short.SIZE); int unsignedPrefix = toUnsignedInt(prefix); long current = iterator.index; int currentUnsignedPrefix = (int) (current & SHORT_PREFIX_MASK) >>> Short.SIZE; assert currentUnsignedPrefix <= unsignedPrefix; if (unsignedPrefix == currentUnsignedPrefix) { // The requested member is within the current 16-bit postfix // storage. if (iterator.storage16.advanceAtLeastTo((short) member, iterator)) { // found in the current postfix storage return true; } int position = iterator.position32; if (position == size) { // the end return false; } // Iterate the next prefix. Storage16 storage = storages[position]; iterator.storage16 = storage; iterator.index = current & INT_PREFIX_MASK | toUnsignedLong(prefixes[position]) << Short.SIZE; iterator.position32 = position + 1; storage.iterate(iterator); return true; } // Resolve and iterate the requested prefix. int position = iterator.position32; if (position == size) { return false; } return iterateAtLeastFrom(member, position, iterator); } private void append(int member) { short prefix = (short) (member >>> Short.SIZE); if (size != 0 && prefix == prefixes[size - 1]) { ((ArrayStorage16) storages[size - 1]).append((short) member); return; } if (size == prefixes.length) { int newCapacity = Math.min(MAX_CAPACITY, size + capacityDeltaShort(prefixes.length)); prefixes = copyOf(prefixes, newCapacity); storages = copyOf(storages, newCapacity); } prefixes[size] = prefix; storages[size] = new ArrayStorage16((short) member); ++size; } private boolean iterateAtLeastFrom(int member, int fromPosition, IteratorImpl iterator) { short prefix = (short) (member >>> Short.SIZE); int position = unsignedBinarySearch(prefixes, fromPosition, size, toUnsignedInt(prefix)); if (position < 0) { position = -(position + 1); if (position == size) { // no such member return false; } } Storage16 storage = storages[position]; if (storage.iterateAtLeastFrom((short) member, iterator)) { // The postfix storage corresponding to the requested member is // successfully advanced at least to the requested member. iterator.storage16 = storage; iterator.index = iterator.index & INT_PREFIX_SHORT_POSTFIX_MASK | toUnsignedLong(prefixes[position]) << Short.SIZE; iterator.position32 = position + 1; } else { // The postfix storage corresponding to the requested member // doesn't contain the requested member or any members greater // than it: try to iterate from the next prefix storage. ++position; if (position == size) { return false; } storage = storages[position]; iterator.storage16 = storage; iterator.index = iterator.index & INT_PREFIX_MASK | toUnsignedLong(prefixes[position]) << Short.SIZE; iterator.position32 = position + 1; storage.iterate(iterator); } return true; } } /** * Defines internal contract of storages responsible for storing of 16-bit * postfixes. */ private interface Storage16 { /** * Adds the given member to this storage. * * @param member the member to add. * @return a new storage instance if this storage was converted to * another storage flavor; this storage otherwise. */ Storage16 add(short member); /** * Removes the given member from this storage. * * @param member the member to remove. * @return {@code null} if this storage became empty as a result of the * member removal; a new storage instance if this storage was converted * to another storage flavor; this storage otherwise. */ Storage16 remove(short member); /** * Starts iteration on this storage using the given iterator. *

* Always succeeds since we never keep empty storages. * * @param iterator the iterator to iterate with. */ void iterate(IteratorImpl iterator); /** * Advances the given iterator on this storage. * * @param iterator the iterator to advance. * @return {@code true} if the iterator is advanced to the next member, * {@code false} if no members to iterate are left in this storage. */ boolean advance(IteratorImpl iterator); /** * Starts iteration on this storage starting at least from the given * member using the given iterator. * * @param member the member to start the iteration from. * @param iterator the iterator to iterate with. * @return {@code true} if the iterator is positioned to the given * member; or, if the member is not present in this storage, to a member * immediately following it and present in this storage or {@code false} * if no such member exists in this storage. */ boolean iterateAtLeastFrom(short member, IteratorImpl iterator); /** * Advances the given iterator to the given member; or, if the member is * not present in this storage, to a member immediately following it and * present in this storage. * * @param member the member to advance at least to. The member must be * greater than the member this iterator is currently at. * @param iterator the iterator to advance. * @return {@code true} if the iterator is advanced to the given * member; or, if the member is not present in this storage, to a member * immediately following it and present in this storage or {@code false} * if no such member exists in this storage. */ boolean advanceAtLeastTo(short member, IteratorImpl iterator); } /** * Manages sorted short array of indexes of set bits. */ private static final class ArrayStorage16 implements Storage16 { private static final int MIN_CAPACITY = 2; private int size; private short[] members; ArrayStorage16(short member) { this.size = 1; this.members = new short[MIN_CAPACITY]; members[0] = member; } /** * Constructs a new storage by downgrading from the given {@link * BitSetStorage16} data. */ ArrayStorage16(long[] bits, int size) { assert size == BitSetStorage16.MIN_SIZE; this.size = size; short[] members = new short[ARRAY_STORAGE_16_MAX_SIZE]; int index = 0; for (int i = 0; i < bits.length; ++i) { long value = bits[i]; int base = i << BitSetStorage16.BIT_SET_LONG_SHIFT; while (value != 0) { int offset = numberOfTrailingZeros(value); members[index++] = (short) (base + offset); // zero out the consumed bit value &= value - 1; } } assert index == size; this.members = members; } @Override public Storage16 add(short member) { int index = unsignedBinarySearch(members, size, toUnsignedInt(member)); if (index >= 0) { // already in the array return this; } index = -(index + 1); if (size == members.length) { // No space left: try to grow members array. if (size == ARRAY_STORAGE_16_MAX_SIZE) { return new BitSetStorage16(members, member, index); } int newCapacity = Math.min(ARRAY_STORAGE_16_MAX_SIZE, size + capacityDeltaShort(members.length)); short[] newMembers = new short[newCapacity]; arraycopy(members, 0, newMembers, 0, index); arraycopy(members, index, newMembers, index + 1, size - index); members = newMembers; } else { // shift members right to free a slot for the new member arraycopy(members, index, members, index + 1, size - index); } members[index] = member; ++size; return this; } @Override public Storage16 remove(short member) { int index = unsignedBinarySearch(members, size, toUnsignedInt(member)); if (index < 0) { // not a member return this; } --size; if (size == 0) { // emptied return null; } int delta = capacityDeltaShort(members.length); int wasted = members.length - size; int newCapacity = members.length - delta; if (wasted >= delta && newCapacity >= MIN_CAPACITY) { // We are wasting too much: shrink the array. short[] newMembers = new short[newCapacity]; arraycopy(members, 0, newMembers, 0, index); arraycopy(members, index + 1, newMembers, index, size - index); members = newMembers; } else { // shift members left to fill the gap arraycopy(members, index + 1, members, index, size - index); } return this; } @Override public void iterate(IteratorImpl iterator) { assert size > 0; iterator.position16 = 1; iterator.index = iterator.index & INT_PREFIX_SHORT_PREFIX_MASK | toUnsignedInt(members[0]); } @Override public boolean advance(IteratorImpl iterator) { int index = iterator.position16; if (index < size) { iterator.index = iterator.index & INT_PREFIX_SHORT_PREFIX_MASK | toUnsignedInt(members[index]); iterator.position16 = index + 1; return true; } else { return false; } } @Override public boolean iterateAtLeastFrom(short member, IteratorImpl iterator) { int unsignedMember = toUnsignedInt(member); int index = unsignedBinarySearch(members, size, unsignedMember); if (index < 0) { index = -(index + 1); if (index == size) { return false; } unsignedMember = toUnsignedInt(members[index]); } iterator.index = iterator.index & INT_PREFIX_SHORT_PREFIX_MASK | unsignedMember; iterator.position16 = index + 1; return true; } @Override public boolean advanceAtLeastTo(short member, IteratorImpl iterator) { int unsignedMember = toUnsignedInt(member); long current = iterator.index; assert (current & SHORT_POSTFIX_MASK) < unsignedMember; int position = iterator.position16; if (position == size) { return false; } position = unsignedBinarySearch(members, position, size, unsignedMember); if (position < 0) { position = -(position + 1); if (position == size) { return false; } unsignedMember = toUnsignedInt(members[position]); } iterator.index = current & INT_PREFIX_SHORT_PREFIX_MASK | unsignedMember; iterator.position16 = position + 1; return true; } /** * Appends the given member to this storage. The given member must be * greater than any member already known by this storage. */ public void append(short member) { if (size == members.length) { int newCapacity = size + capacityDeltaShort(members.length); assert newCapacity <= ARRAY_STORAGE_16_MAX_SIZE; members = copyOf(members, newCapacity); } members[size] = member; ++size; } } /** * Manages directly indexable long array of bits. */ private static final class BitSetStorage16 implements Storage16 { // 2^6 = 64 = number of bits a long can store public static final int BIT_SET_LONG_SHIFT = 6; private static final int MIN_SIZE = ARRAY_STORAGE_16_MAX_SIZE - 1; private static final int SIZE = 1024; // masks lower 6 bits private static final long POSTFIX_MASK = 0xFFFFFFFFFFFFFFC0L; private final long[] members = new long[SIZE]; private int size; /** * Constructs a new bit set storage for the given sorted members array * and the given member to insert at the given index. */ BitSetStorage16(short[] members, short member, int index) { for (int i = 0; i < index; ++i) { append(members[i]); } append(member); for (int i = index; i < members.length; ++i) { append(members[i]); } this.size = members.length + 1; } @Override public Storage16 add(short member) { int bitIndex = toUnsignedInt(member); int longIndex = bitIndex >>> BIT_SET_LONG_SHIFT; long bitSet = members[longIndex]; long newBitSet = bitSet | 1L << bitIndex; members[longIndex] = newBitSet; if (newBitSet != bitSet) { ++size; } return this; } @Override public Storage16 remove(short member) { int bitIndex = toUnsignedInt(member); int longIndex = bitIndex >>> BIT_SET_LONG_SHIFT; long bitSet = members[longIndex]; long newBitSet = bitSet & ~(1L << bitIndex); members[longIndex] = newBitSet; if (newBitSet != bitSet) { --size; if (size == MIN_SIZE) { return new ArrayStorage16(members, size); } } return this; } @Override public void iterate(IteratorImpl iterator) { assert size > 0; iterator.position16 = 0; iterator.bitSet16 = members[0]; iterator.index = iterator.index & INT_PREFIX_SHORT_PREFIX_MASK; boolean advanced = advance(iterator); assert advanced; } @Override public boolean advance(IteratorImpl iterator) { // Consume bits from the current long until it's empty. long bitSet = iterator.bitSet16; if (bitSet != 0) { iterator.index = iterator.index & POSTFIX_MASK | numberOfTrailingZeros(bitSet); // zero out the consumed bit iterator.bitSet16 = bitSet & bitSet - 1; return true; } // Try to find the next non-zero long. int index = iterator.position16; do { ++index; if (index == members.length) { // nothing left return false; } bitSet = members[index]; } while (bitSet == 0); iterator.index = iterator.index & INT_PREFIX_SHORT_PREFIX_MASK | index << BIT_SET_LONG_SHIFT | numberOfTrailingZeros(bitSet); iterator.bitSet16 = bitSet & bitSet - 1; iterator.position16 = index; return true; } @Override public boolean iterateAtLeastFrom(short member, IteratorImpl iterator) { int bitIndex = toUnsignedInt(member); int longIndex = bitIndex >>> BIT_SET_LONG_SHIFT; iterator.position16 = longIndex; // consume all preceding bits by zeroing them out iterator.bitSet16 = members[longIndex] & -(1L << bitIndex); iterator.index = iterator.index & INT_PREFIX_SHORT_PREFIX_MASK | longIndex << BIT_SET_LONG_SHIFT; return advance(iterator); } @Override public boolean advanceAtLeastTo(short member, IteratorImpl iterator) { long current = iterator.index; int bitIndex = toUnsignedInt(member); assert (current & SHORT_POSTFIX_MASK) < bitIndex; int longIndex = bitIndex >>> BIT_SET_LONG_SHIFT; iterator.position16 = longIndex; // consume all preceding bits by zeroing them out iterator.bitSet16 = members[longIndex] & -(1L << bitIndex); iterator.index = current & INT_PREFIX_SHORT_PREFIX_MASK | longIndex << BIT_SET_LONG_SHIFT; return advance(iterator); } private void append(short member) { int bitIndex = toUnsignedInt(member); members[bitIndex >>> BIT_SET_LONG_SHIFT] |= 1L << bitIndex; } } /** * Iterates over sparse bit sets. */ private static final class IteratorImpl extends SparseIntArray.Iterator implements AscendingLongIterator { // The idea: use a single iterator instance to iterate over the entire // bit set including all its internal storages. This way we are avoiding // frequent sub iterators allocation, producing no heap litter and // keeping the iteration state just in a few cache lines. // the root storage mapping 32-bit prefixes to 32-bit postfix storages private final SparseIntArray storage64; // the current position of the current Storage32 private int position32; // the current Storage16 private Storage16 storage16; // its position private int position16; // the current bit set of BitSetStorage16 private long bitSet16; // the current index (member), constructed cooperatively by all storages private long index; IteratorImpl(SparseIntArray storage64) { this.storage64 = storage64; long prefix = storage64.iterate(this); if (prefix != SparseIntArray.Iterator.END) { index = prefix << Integer.SIZE; getStorage32().iterate(this); } else { index = AscendingLongIterator.END; } } @Override public long getIndex() { return index; } @Override public long advance() { long current = index; if (current == AscendingLongIterator.END) { return AscendingLongIterator.END; } if (getStorage32().advance(this)) { return current; } long prefix = storage64.advance((int) (current >>> Integer.SIZE), this); if (prefix != SparseIntArray.Iterator.END) { index = prefix << Integer.SIZE; getStorage32().iterate(this); return current; } index = AscendingLongIterator.END; return current; } @SuppressWarnings("checkstyle:nestedifdepth") @Override public long advanceAtLeastTo(long member) { assert member >= 0; long current = index; if (current == AscendingLongIterator.END) { return AscendingLongIterator.END; } if (current >= member) { // Already at or beyond the requested member. return current; } int memberPrefix = (int) (member >>> Integer.SIZE); int currentPrefix = (int) (current >>> Integer.SIZE); if (memberPrefix == currentPrefix) { // Try to advance the current storage. if (getStorage32().advanceAtLeastTo((int) member, this)) { return index; } else { // Try to advance to the next storage. long prefix = storage64.advance(currentPrefix, this); if (prefix != SparseIntArray.Iterator.END) { index = prefix << Integer.SIZE; getStorage32().iterate(this); return index; } } } else { // Try to advance to the requested storage. long prefix = storage64.advanceAtLeastTo(memberPrefix, currentPrefix, this); if (prefix != SparseIntArray.Iterator.END) { if (prefix == memberPrefix) { // We got the storage we have requested. index = prefix << Integer.SIZE; if (getStorage32().iterateAtLeastFrom((int) member, this)) { return index; } else { // The storage we got doesn't contain the requested // postfix or any postfix beyond it: try to advance // to the next storage (storages are guaranteed to // be non-empty). prefix = storage64.advance((int) prefix, this); if (prefix != SparseIntArray.Iterator.END) { index = prefix << Integer.SIZE; getStorage32().iterate(this); return index; } } } else { // We got a storage corresponding to some other prefix // beyond the requested one: just iterate the storage // (storages are guaranteed to be non-empty). index = prefix << Integer.SIZE; getStorage32().iterate(this); return index; } } } // The end. index = AscendingLongIterator.END; return AscendingLongIterator.END; } // just an alias for getValue private Storage32 getStorage32() { return getValue(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy