/* * Copyright 2005-2010 Roger Kapsi * * 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 org.ardverk.collection; import java.io.Serializable; /** * A {@link KeyAnalyzer} for {@code byte[]}s */ public abstract class ByteArrayKeyAnalyzer extends AbstractKeyAnalyzer<byte[]> implements Serializable { private static final long serialVersionUID = 6496047734419335722L; /** * A {@link ByteArrayKeyAnalyzer} for constant length {@code byte[]}. */ public static final ByteArrayKeyAnalyzer CONSTANT = new Constant(); /** * A {@link ByteArrayKeyAnalyzer} for variable length {@code byte[]}. */ public static final ByteArrayKeyAnalyzer VARIABLE = new Variable(); @Deprecated public static final ByteArrayKeyAnalyzer INSTANCE = CONSTANT; /** * A bit mask where the first bit is 1 and the others are zero */ private static final int MSB = 1 << Byte.SIZE-1; /** * Creates and returns a {@link ByteArrayKeyAnalyzer} for * fixed-length keys. The maximum length of a key is defined * in bytes. */ public static ByteArrayKeyAnalyzer create(int maxLength) { return new Constant(maxLength); } @Override public int compare(byte[] o1, byte[] o2) { if (o1 == null) { return (o2 == null) ? 0 : -1; } else if (o2 == null) { return (o1 == null) ? 0 : 1; } if (o1.length != o2.length) { return o1.length - o2.length; } for (int i = 0; i < o1.length; i++) { int diff = (o1[i] & 0xFF) - (o2[i] & 0xFF); if (diff != 0) { return diff; } } return 0; } public int lengthInBits(byte[] key) { return key.length * Byte.SIZE; } public boolean isPrefix(byte[] key, byte[] prefix) { if (key.length < prefix.length) { return false; } for (int i = 0; i < prefix.length; i++) { if (key[i] != prefix[i]) { return false; } } return true; } /** * Returns a bit mask where the given bit is set */ private static int mask(int bit) { return MSB >>> bit; } /** * Returns the {@code byte} value at the given index. */ private static byte valueAt(byte[] values, int index) { if (index >= 0 && index < values.length) { return values[index]; } return 0; } /** * A {@link ByteArrayKeyAnalyzer} for variable length {@code byte[]}. */ private static class Variable extends ByteArrayKeyAnalyzer { private static final long serialVersionUID = 5360165640553653434L; public boolean isBitSet(byte[] key, int bitIndex) { if (bitIndex >= lengthInBits(key)) { return false; } int index = (int)(bitIndex / Byte.SIZE); int bit = (int)(bitIndex % Byte.SIZE); return (key[index] & mask(bit)) != 0; } public int bitIndex(byte[] key, byte[] otherKey) { int length = Math.max(key.length, otherKey.length); boolean allNull = true; for (int i = 0; i < length; i++) { byte b1 = valueAt(key, i); byte b2 = valueAt(otherKey, i); if (b1 != b2) { int xor = b1 ^ b2; for (int j = 0; j < Byte.SIZE; j++) { if ((xor & mask(j)) != 0) { return (i * Byte.SIZE) + j; } } } if (b1 != 0) { allNull = false; } } if (allNull) { return KeyAnalyzer.NULL_BIT_KEY; } return KeyAnalyzer.EQUAL_BIT_KEY; } } /** * A {@link ByteArrayKeyAnalyzer} for constant length {@code byte[]}. */ private static class Constant extends ByteArrayKeyAnalyzer { private static final long serialVersionUID = 6464528643075848768L; private static final int DEFAULT_LENGTH = Integer.MAX_VALUE / Byte.SIZE; private final int maxLength; public Constant() { this(DEFAULT_LENGTH); } public Constant(int maxLength) { this.maxLength = maxLength; } public boolean isBitSet(byte[] key, int bitIndex) { if (maxLength < key.length) { throw new IllegalArgumentException(); } int lengthInBits = lengthInBits(key); int prefix = (maxLength * Byte.SIZE) - lengthInBits; int keyBitIndex = bitIndex - prefix; if (keyBitIndex >= lengthInBits || keyBitIndex < 0) { return false; } int index = (int)(keyBitIndex / Byte.SIZE); int bit = (int)(keyBitIndex % Byte.SIZE); return (key[index] & mask(bit)) != 0; } public int bitIndex(byte[] key, byte[] otherKey) { // NOTE: We don't have to check the otherKey because // it's a key that is already in the Trie. It has // already passed this test when it was added to the // Trie back in the days... if (maxLength < key.length) { throw new IllegalArgumentException(); } boolean allNull = true; int length = Math.max(key.length, otherKey.length); int prefixBits = (maxLength - length) * Byte.SIZE; for (int i = 0; i < length; i++) { byte b1 = valueAt(key, key.length - length + i); byte b2 = valueAt(otherKey, otherKey.length - length + i); if (b1 != b2) { int xor = b1 ^ b2; for (int j = 0; j < Byte.SIZE; j++) { if ((xor & mask(j)) != 0) { return prefixBits + (i * Byte.SIZE) + j; } } } if (b1 != 0) { allNull = false; } } if (allNull) { return KeyAnalyzer.NULL_BIT_KEY; } return KeyAnalyzer.EQUAL_BIT_KEY; } } }