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

com.gemstone.gemfire.internal.UniqueIdGenerator Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2015 Pivotal Software, 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. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.internal;

import com.gemstone.gemfire.internal.i18n.LocalizedStrings;

/** UniqueIdGenerator is factory that will produce unique ids that fall
 * in a range between 0 and numIds-1 inclusive.
 * Obtained ids will not be reissued until they are released and until every
 * other non-obtained id has been obtained first.
 * 

* Memory Use: this implementation allocates numIds/64 8-byte longs at construction * time and keeps them allocated. *

* Instances of this class are thread safe. * * @author Darrel * @since 5.0.2 */ public class UniqueIdGenerator { /* * BitSets are packed into arrays of "units." Currently a unit is a long, * which consists of 64 bits, requiring 6 address bits. The choice of unit * is determined purely by performance concerns. */ private final static int ADDRESS_BITS_PER_UNIT = 6; private final static int BITS_PER_UNIT = 1 << ADDRESS_BITS_PER_UNIT; private final static int BIT_INDEX_MASK = BITS_PER_UNIT - 1; /** * The units in this BitSet. The ith bit is stored in units[i/64] at * bit position i % 64 (where bit position 0 refers to the least * significant bit and 63 refers to the most significant bit). */ private final long units[]; /** * The maximum id supported by this generator */ private final int MAX_ID; /** * The next id to return from obtain if it is available. */ private int ctr = 0; /** * Given a bit index return unit index containing it. */ private static int unitIndex(int bitIndex) { return bitIndex >> ADDRESS_BITS_PER_UNIT; } /** * Given a bit index, return a unit that masks that bit in its unit. */ private static long bit(int bitIndex) { return 1L << (bitIndex & BIT_INDEX_MASK); } /** * Sets the bit at the specified index to true if it is not * already in the set. Otherwise does nothing. * * @param bitIndex a bit index. * @exception IndexOutOfBoundsException if the specified index is negative. */ private void setBit(int bitIndex) { int unitIndex = unitIndex(bitIndex); long bit = bit(bitIndex); this.units[unitIndex] |= bit; } /** * Sets the bit specified by the index to false. * * @param bitIndex the index of the bit to be cleared. */ private void clearBit(int bitIndex) { int unitIndex = unitIndex(bitIndex); long bit = bit(bitIndex); this.units[unitIndex] &= ~bit; } /* * trailingZeroTable[i] is the number of trailing zero bits in the binary * representation of i. */ private final static byte trailingZeroTable[] = { -25, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}; private static int trailingZeroCnt(long val) { // Loop unrolled for performance int byteVal = (int)val & 0xff; if (byteVal != 0) { return trailingZeroTable[byteVal]; } byteVal = (int)(val >>> 8) & 0xff; if (byteVal != 0) { return trailingZeroTable[byteVal] + 8; } byteVal = (int)(val >>> 16) & 0xff; if (byteVal != 0) { return trailingZeroTable[byteVal] + 16; } byteVal = (int)(val >>> 24) & 0xff; if (byteVal != 0) { return trailingZeroTable[byteVal] + 24; } byteVal = (int)(val >>> 32) & 0xff; if (byteVal != 0) { return trailingZeroTable[byteVal] + 32; } byteVal = (int)(val >>> 40) & 0xff; if (byteVal != 0) { return trailingZeroTable[byteVal] + 40; } byteVal = (int)(val >>> 48) & 0xff; if (byteVal != 0) { return trailingZeroTable[byteVal] + 48; } byteVal = (int)(val >>> 56) & 0xff; return trailingZeroTable[byteVal] + 56; } private static final long WORD_MASK = 0xffffffffffffffffL; /** * Returns the index of the first bit that is set to false * that occurs on or after the specified starting index. * * @param fromIndex the index to start checking from (inclusive). * @return the index of the next clear bit. */ private int nextClearBit(int fromIndex) { int u = unitIndex(fromIndex); int testIndex = (fromIndex & BIT_INDEX_MASK); long unit = this.units[u] >> testIndex; if (unit == (WORD_MASK >> testIndex)) { testIndex = 0; } while((unit==WORD_MASK) && (u < this.units.length-1)) { unit = this.units[++u]; } if (unit == WORD_MASK) { return -1; // could not find a clear bit } int result; if (unit == 0) { result = u * BITS_PER_UNIT + testIndex; } else { testIndex += trailingZeroCnt(~unit); result = ((u * BITS_PER_UNIT) + testIndex); } if (result > MAX_ID || result < 0) { return -1; } else { return result; } } /** * Creates unique id pool that has numIds in it. * * @param numIds the maximum number of ids */ public UniqueIdGenerator(int numIds) { if (numIds <= 0) { throw new IllegalArgumentException(LocalizedStrings.UniqueIdGenerator_NUMIDS_0.toLocalizedString()); } this.units = new long[(unitIndex(numIds-1) + 1)]; this.MAX_ID = numIds-1; this.ctr = 0; } /** * Obtain an id form the pool of available ids and return it. * @throws IllegalStateException if all ids have been obtained */ public int obtain() { synchronized (this) { int candidate = this.ctr; int result = nextClearBit(candidate); if (result == -1 && candidate != 0) { // The following check will do some additional work by scanning // the range [candidate..MAX_ID] again if it does not find a clear bit // in the range [0..candidate-1] but it will only do this when // we are going to throw an exception. result = nextClearBit(0); } if (result == -1) { throw new IllegalStateException(LocalizedStrings.UniqueIdGenerator_RAN_OUT_OF_MESSAGE_IDS.toLocalizedString()); } else { setBit(result); if (result == MAX_ID) { this.ctr = 0; } else { this.ctr = result+1; } return result; } } } /** * Release a previously obtained id allowing it to be obtained in the future. */ public void release(int id) { if (id < 0) { throw new IllegalArgumentException(LocalizedStrings.UniqueIdGenerator_NEGATIVE_ID_0.toLocalizedString(Integer.valueOf(id))); } else if (id > this.MAX_ID) { throw new IllegalArgumentException(LocalizedStrings.UniqueIdGenerator_ID_MAX_ID_0.toLocalizedString(Integer.valueOf(id))); } synchronized (this) { clearBit(id); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy