package org.basex.util;
import java.util.Arrays;
/**
* <p>This class provides operations to compress and decompress 4-byte integer
* values in byte arrays in order to save memory.</p>
*
* <p>The first two bits of a {@code Num} array indicate the range of the
* compressed number:</p>
*
* <ul>
* <li>{@code 00}: the number (0x00-0x3F) is encoded in the remaining 6 bits of
* the current byte</li>
* <li>{@code 01}: the number (0x40-0x3FFF) is encoded in 14 bits of the current
* and the following byte</li>
* <li>{@code 10}: the number (0x4000-0x3FFFFFFF) is encoded in 30 bits of the
* current and the following three bytes</li>
* <li>{@code 11}: the number (0x40000000-0xFFFFFFFF) is encoded in 32 bits of
* the following four bytes</li>
* </ul>
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class Num {
/** Private constructor, preventing instantiation. */
private Num() { }
// STATIC METHODS ===========================================================
/**
* Creates a new number array, in which the first four bytes contain
* the number of occupied bytes.
* @param value initial value to be compressed and stored
* @return new number array
*/
public static byte[] newNum(final int value) {
final int len = length(value);
final byte[] array = new byte[4 + len];
set(array, value, 4, len);
size(array, 4 + len);
return array;
}
/**
* Creates a compressed representation of the specified value.
* @param value value to be compressed
* @return new number array
*/
public static byte[] num(final int value) {
final int len = length(value);
final byte[] array = new byte[len];
set(array, value, 0, len);
return array;
}
/**
* Compresses and adds a value to the specified array and
* returns the resulting array.
* @param array input array
* @param value value to be added
* @return resulting array (may be the same as input array)
*/
public static byte[] add(final byte[] array, final int value) {
final int len = length(value);
final int pos = size(array);
final byte[] tmp = check(array, pos, len);
set(tmp, value, pos, len);
size(tmp, pos + len);
return tmp;
}
/**
* Decompresses and returns a value from the specified byte array.
* @param array array
* @param pos position where the value is found
* @return decompressed value
*/
public static int get(final byte[] array, final int pos) {
int p = pos;
final int v = array[p++] & 0xFF;
switch((v & 0xC0) >>> 6) {
case 0: return v;
case 1: return (v & 0x3F) << 8 | array[p] & 0xFF;
case 2: return (v & 0x3F) << 24 | (array[p++] & 0xFF) << 16 |
(array[p++] & 0xFF) << 8 | array[p] & 0xFF;
default: return (array[p++] & 0xFF) << 24 | (array[p++] & 0xFF) << 16 |
(array[p++] & 0xFF) << 8 | array[p] & 0xFF;
}
}
/**
* Compresses and stores an integer value to the specified byte array.
* @param array array
* @param value value to be stored
* @param pos position where the value is to be stored
*/
public static void set(final byte[] array, final int value, final int pos) {
set(array, value, pos, length(value));
}
/**
* Returns the length value of the specified array, stored in the first
* four bytes.
* @param array input array
* @return array length
*/
public static int size(final byte[] array) {
return ((array[0] & 0xFF) << 24) + ((array[1] & 0xFF) << 16) +
((array[2] & 0xFF) << 8) + (array[3] & 0xFF);
}
/**
* Stores the specified length value in the first bytes of the
* specified array.
* @param array input array
* @param length length to be stored
*/
public static void size(final byte[] array, final int length) {
array[0] = (byte) (length >>> 24);
array[1] = (byte) (length >>> 16);
array[2] = (byte) (length >>> 8);
array[3] = (byte) length;
}
/**
* Returns the compressed length of the value at the specified position.
* @param array array
* @param pos position where the value is found
* @return value length
*/
public static int length(final byte[] array, final int pos) {
final int v = (array[pos] & 0xFF) >>> 6;
return v == 0 ? 1 : v == 1 ? 2 : v == 2 ? 4 : 5;
}
/**
* Returns the compressed length of the specified value.
* @param v integer value
* @return value length
*/
public static int length(final int v) {
return v < 0 || v > 0x3FFFFFFF ? 5 : v > 0x3FFF ? 4 : v > 0x3F ? 2 : 1;
}
// PRIVATE STATIC METHODS ===================================================
/**
* Resizes the specified array if no space is left.
* @param a array to be resized
* @param p current array position
* @param l length of new entry
* @return new array
*/
private static byte[] check(final byte[] a, final int p, final int l) {
final int s = a.length;
return p + l < s ? a : Arrays.copyOf(a, s + Math.max(l, s >> 3));
}
/**
* Compresses and writes an integer value to the specified byte array.
* @param a array
* @param v value to be written
* @param p position
* @param l value length
*/
private static void set(final byte[] a, final int v, final int p,
final int l) {
int i = p;
if(l == 5) {
a[i++] = (byte) 0xC0;
a[i++] = (byte) (v >>> 24);
a[i++] = (byte) (v >>> 16);
a[i++] = (byte) (v >>> 8);
} else if(l == 4) {
a[i++] = (byte) (v >>> 24 | 0x80);
a[i++] = (byte) (v >>> 16);
a[i++] = (byte) (v >>> 8);
} else if(l == 2) {
a[i++] = (byte) (v >>> 8 | 0x40);
}
a[i] = (byte) v;
}
}