io.nats.client.NUID Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jnats Show documentation
Show all versions of jnats Show documentation
Client library for working with the NATS messaging system.
// Copyright 2015-2018 The NATS Authors
// 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.
// Copied from 1.0 Java client for consistency and expedience.
package io.nats.client;
import java.util.concurrent.locks.ReentrantLock;
import static io.nats.client.support.RandomUtils.*;
/**
* A highly performant unique identifier generator. The library uses this to generate
* an inbox for request-replies. Applications can use their own NUID to generate
* subjects as well. A shareable Global instance is also available via {@link #nextGlobal nextGlobal()}.
*/
public final class NUID {
/*
* NUID needs to be very fast to generate and truly unique, all while being
* entropy pool friendly. We will use 12 bytes of crypto generated data (entropy
* draining), and 10 bytes of sequential data that is started at a pseudo random
* number and increments with a pseudo-random increment. Total is 22 bytes of
* base 62 ascii text :)
*/
// Constants
static final char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z' };
static final int base = 62;
static final int preLen = 12;
static final int seqLen = 10;
static final long maxSeq = 839299365868340224L; // base^seqLen == 62^10
static final long minInc = 33L;
static final long maxInc = 333L;
static final int totalLen = preLen + seqLen;
// Instance fields
char[] pre;
private long seq;
private long inc;
private static final NUID globalNUID;
private final ReentrantLock nextLock;
static {
globalNUID = new NUID();
}
static NUID getInstance() {
return globalNUID;
}
/**
* The default NUID constructor.
*
* Relies on the OS Default instance of SecureRandom.
*
* @throws IllegalStateException
* if the class cannot find the necessary
* SecureRandom instance.
*/
public NUID() {
nextLock = new ReentrantLock();
// Generate a cryto random int, 0 <= val < max to seed pseudorandom
seq = nextLong(PRAND, maxSeq);
inc = minInc + nextLong(PRAND, maxInc - minInc);
pre = new char[preLen];
for (int i = 0; i < preLen; i++) {
pre[i] = '0';
}
randomizePrefix();
}
/**
* @return the next NUID string from a shared global NUID instance
*/
public static String nextGlobal() {
return globalNUID.next();
}
/**
* @return the next sequence portion of the NUID string from a shared global NUID instance
*/
public static String nextGlobalSequence() {
return globalNUID.nextSequence();
}
/**
* Generate the next NUID string from this instance.
*
* @return the next NUID string from this instance.
*/
public String next() {
nextLock.lock();
try {
// Increment and capture.
seq += inc;
if (seq >= maxSeq) {
randomizePrefix();
resetSequential();
}
// Copy prefix
char[] b = new char[totalLen];
System.arraycopy(pre, 0, b, 0, preLen);
// copy in the seq
int i = b.length;
for (long l = seq; i > preLen; l /= base) {
b[--i] = digits[(int) (l % base)];
}
return new String(b);
}
finally {
nextLock.unlock();
}
}
/**
* Generate the next NUID string from this instance and return only the sequence portion.
* @return the next sequence portion of the NUID string from a shared global NUID instance
*/
public String nextSequence() {
nextLock.lock();
try {
// Increment and capture.
seq += inc;
if (seq >= maxSeq) {
randomizePrefix();
resetSequential();
}
char[] b = new char[seqLen];
// copy in the seq
int ix = seqLen;
for (long l = seq; ix > 0; l /= base) {
b[--ix] = digits[(int) (l % base)];
}
return new String(b);
}
finally {
nextLock.unlock();
}
}
// Resets the sequential portion of the NUID
void resetSequential() {
seq = nextLong(PRAND, maxSeq);
inc = minInc + nextLong(PRAND, maxInc - minInc);
}
/*
* Generate a new prefix from random. This *can* drain entropy and will be
* called automatically when we exhaust the sequential range.
*/
final void randomizePrefix() {
byte[] cb = new byte[preLen];
// Use SecureRandom for prefix only
SRAND.nextBytes(cb);
for (int i = 0; i < preLen; i++) {
pre[i] = digits[(cb[i] & 0xFF) % base];
}
}
/**
* @return the pre
*/
char[] getPre() {
return pre;
}
/**
* Return the current sequence value.
*
* @return the seq
*/
long getSeq() {
return seq;
}
/**
* Set the sequence to the supplied value.
*
* @param seq
* the seq to set
*/
void setSeq(long seq) {
this.seq = seq;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy