/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.json_voltpatches.JSONException;
import org.json_voltpatches.JSONStringer;
import org.voltcore.logging.VoltLogger;
import org.voltcore.utils.Pair;
public class LegacyHashinator extends TheHashinator {
private final int catalogPartitionCount;
private final byte m_configBytes[];
private final String m_configJSON;
private final long m_signature;
@SuppressWarnings("unused")
private static final VoltLogger hostLogger = new VoltLogger("HOST");
@Override
public int pHashinateLong(long value) {
// special case this hard to hash value to 0 (in both c++ and java)
if (value == Long.MIN_VALUE) return 0;
// hash the same way c++ does
int index = (int)(value^(value>>>32));
return java.lang.Math.abs(index % catalogPartitionCount);
}
@Override
public int pHashinateBytes(byte[] bytes) {
int hashCode = 0;
int offset = 0;
for (int ii = 0; ii < bytes.length; ii++) {
hashCode = 31 * hashCode + bytes[offset++];
}
return java.lang.Math.abs(hashCode % catalogPartitionCount);
}
/**
* Constructor
* @param configBytes config data
* @param cooked (ignored by legacy)
*/
public LegacyHashinator(byte configBytes[], boolean cooked) {
catalogPartitionCount = ByteBuffer.wrap(configBytes).getInt();
m_configBytes = Arrays.copyOf(configBytes, configBytes.length);
try {
m_configJSON = new JSONStringer().
array().value(catalogPartitionCount).endArray().toString();
} catch (JSONException e) {
throw new RuntimeException("Failed to serialized Hashinator Configuration to JSON.", e);
}
m_signature = TheHashinator.computeConfigurationSignature(m_configBytes);
}
public static byte[] getConfigureBytes(int catalogPartitionCount) {
ByteBuffer buf = ByteBuffer.allocate(4);
buf.putInt(catalogPartitionCount);
return buf.array();
}
@Override
public HashinatorConfig pGetCurrentConfig() {
return new HashinatorConfig(HashinatorType.LEGACY, m_configBytes, 0, 0);
}
@Override
public Map<Integer, Integer> pPredecessors(int partition)
{
throw new RuntimeException("Legacy hashinator doesn't support predecessors");
}
@Override
public Pair<Integer, Integer> pPredecessor(int partition, int token)
{
throw new RuntimeException("Legacy hashinator doesn't support predecessors");
}
@Override
public Map<Integer, Integer> pGetRanges(int partition)
{
throw new RuntimeException("Getting ranges is not supported in the legacy hashinator");
}
@Override
public long pGetConfigurationSignature() {
return m_signature;
}
@Override
public boolean pIsPristine() {
return true;
}
/**
* Returns straight config bytes (not for serialization).
* @return config bytes
*/
@Override
public byte[] getConfigBytes()
{
return m_configBytes;
}
/**
* Returns raw config JSONString.
* @return config JSONString
*/
@Override
public String getConfigJSON()
{
return m_configJSON;
}
@Override
public HashinatorType getConfigurationType() {
return TheHashinator.HashinatorType.LEGACY;
}
@Override
public int pHashToPartition(VoltType type, Object obj) {
assert(obj != null);
// Annoying, legacy hashes numbers and bytes differently, need to preserve that.
if (VoltType.isVoltNullValue(obj)) {
return 0;
}
long value = 0;
if (obj instanceof Long) {
value = ((Long) obj).longValue();
}
else if (obj instanceof Integer) {
value = ((Integer) obj).intValue();
}
else if (obj instanceof Short) {
value = ((Short) obj).shortValue();
}
else if (obj instanceof Byte) {
value = ((Byte) obj).byteValue();
}
else {
// The hash formula for a value represented as serialized bytes
// must still be appropriate for the expected partitioning type,
// even if this requires a round-trip conversion.
if (obj.getClass() == byte[].class) {
obj = type.bytesToValue((byte[]) obj);
}
return pHashinateBytes(VoltType.valueToBytes(obj));
}
return pHashinateLong(value);
}
@Override
protected Set<Integer> pGetPartitions() {
Set<Integer> set = new HashSet<Integer>();
for (int ii = 0; ii < catalogPartitionCount; ii++) {
set.add(ii);
}
return set;
}
@Override
public int getPartitionFromHashedToken(int hashedToken) {
return java.lang.Math.abs(hashedToken % catalogPartitionCount);
}
}