/*
* Copyright (c) 2013-2017 Cinchapi Inc.
*
* 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.cinchapi.concourse.server.concurrent;
import java.nio.ByteBuffer;
import com.cinchapi.concourse.annotate.PackagePrivate;
import com.cinchapi.concourse.server.io.Byteable;
import com.cinchapi.concourse.server.storage.cache.LazyCache;
import com.cinchapi.concourse.util.ByteBuffers;
import com.cinchapi.concourse.util.TArrays;
import com.google.common.io.BaseEncoding;
/**
* A {@link Token} wraps multiple other objects.
*
* @author Jeff Nelson
*/
public class Token implements Byteable {
/**
* Return the Token encoded in {@code bytes} so long as those bytes adhere
* to the format specified by the {@link #getBytes()} method. This method
* assumes that all the bytes in the {@code bytes} belong to the Token. In
* general, it is necessary to get the appropriate Token slice from the
* parent ByteBuffer using {@link ByteBuffers#slice(ByteBuffer, int, int)}.
*
* @param bytes
* @return the Token
*/
public static Token fromByteBuffer(ByteBuffer bytes) {
return new Token(bytes);
}
/**
* Return a {@link Token} that wraps the specified {@code objects}.
*
* @param objects
* @return the Token
*/
public static Token wrap(Object... objects) {
Token token = new Token(TArrays.hash(objects));
token.cardinality = objects.length;
return token;
}
/**
* Return a {@link Token} that wraps the specified {@code key}. This method
* takes advantage of caching since the keys are often to be reused
* frequently.
*
* @param key
* @return the Token
*/
public static Token wrap(String key) {
Token token = cache.get(key);
if(token == null) {
token = new Token(TArrays.hash(key));
token.cardinality = 1;
cache.put(key, token);
}
return token;
}
/**
* The cache of string tokens that represent record keys.
*/
private static final LazyCache<String, Token> cache = LazyCache
.withExpectedSize(5000);
/**
* The sequence of bytes is a 128-bit (16 byte) hash.
*/
private final ByteBuffer bytes;
/**
* The number of objects that are embedded within the token. This is only
* used by {@link TokenReadWriteLock} to determine lock granularity.
*/
@PackagePrivate
int cardinality = 1;
/**
* Construct a new instance.
*
* @param bytes
*/
protected Token(ByteBuffer bytes) {
this.bytes = bytes;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Token) {
return getBytes().equals(((Token) obj).getBytes());
}
return false;
}
@Override
public ByteBuffer getBytes() {
return ByteBuffers.asReadOnlyBuffer(bytes);
}
@Override
public int hashCode() {
return getBytes().hashCode();
}
/**
* "Upgrade" this token by ensuring that the cardinality is greater than 1.
*/
public void upgrade() {
this.cardinality += 1;
}
@Override
public int size() {
return bytes.capacity();
}
@Override
public String toString() {
return BaseEncoding.base16()
.encode(ByteBuffers.toByteArray(getBytes())).toLowerCase();
}
@Override
public void copyTo(ByteBuffer buffer) {
ByteBuffers.copyAndRewindSource(bytes, buffer);
}
}