/*
* Copyright (c) 2009-2011, Jan Stender, Bjoern Kolbeck, Mikael Hoegqvist,
* Felix Hupfeld, Felix Langner, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
package de.mxro.thrd.babudb05.snapshots;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import de.mxro.thrd.babudb05.index.DefaultByteRangeComparator;
/**
* A set of configuration parameters for a new snapshot.
*
* @author stender
*
*/
public class DefaultSnapshotConfig implements SnapshotConfig {
private static final long serialVersionUID = -4976962150947558070L;
private int[] indices;
private String name;
private byte[][][] prefixes;
private byte[][][] excludedPrefixes;
private transient Map<Integer, Integer> indexMap;
public DefaultSnapshotConfig(String snapName, int[] indices, byte[][][] prefixes,
byte[][][] excludedPrefixes) {
this.indices = indices;
this.name = snapName;
this.prefixes = removeCoveringPrefixes(prefixes);
this.excludedPrefixes = excludedPrefixes;
if (excludedPrefixes != null)
for (byte[][] keys : excludedPrefixes) {
if (keys != null)
Arrays.sort(keys, DefaultByteRangeComparator.getInstance());
}
initIndexMap();
}
@Override
public String getName() {
return name;
}
@Override
public int[] getIndices() {
return indices;
}
@Override
public byte[][] getPrefixes(int index) {
if (indexMap == null)
initIndexMap();
return prefixes == null ? null : prefixes[indexMap.get(index)];
}
@Override
public boolean containsKey(int index, byte[] key) {
if (excludedPrefixes == null)
return true;
if (indexMap == null)
initIndexMap();
if (excludedPrefixes[indexMap.get(index)] == null)
return true;
// search for a matching prefix
byte[][] excl = excludedPrefixes[indexMap.get(index)];
int i = binarySearch(key, excl);
return i == -1;
}
private static int binarySearch(byte[] key, byte[][] prefixes) {
int low = 0;
int high = prefixes.length - 1;
int mid = high;
int cmp = 0;
// binary search
while (low <= high) {
mid = (low + high) >>> 1;
byte[] currPrefix = prefixes[mid];
cmp = startsWith(key, currPrefix);
if (cmp > 0)
low = mid + 1;
else if (cmp < 0)
high = mid - 1;
else
return mid;
}
return -1;
}
private static int startsWith(byte[] key, byte[] prefix) {
for (int i = 0; i < key.length; i++) {
if (i >= prefix.length)
return 0;
if (key[i] < prefix[i])
return -1;
if (key[i] > prefix[i])
return 1;
}
return key.length == prefix.length? 0: -1;
}
/**
* Removes all prefixes that are covered by other prefixes.
*
* @param prefixes
* the list of input prefixes
* @return the list of output prefixes
*/
private byte[][][] removeCoveringPrefixes(byte[][][] prefixes) {
if (prefixes == null)
return null;
byte[][][] newPrefixes = new byte[prefixes.length][][];
for (int i = 0; i < prefixes.length; i++)
newPrefixes[i] = removeCoveringPrefixes(prefixes[i]);
return newPrefixes;
}
private byte[][] removeCoveringPrefixes(byte[][] prefixes) {
if (prefixes == null)
return null;
Set<Integer> covered = new HashSet<Integer>();
for (int i = 0; i < prefixes.length; i++)
for (int j = i + 1; j < prefixes.length; j++)
if (covers(prefixes[j], prefixes[i]))
covered.add(i);
else if (covers(prefixes[i], prefixes[j]))
covered.add(j);
int count = 0;
byte[][] newPrefixes = new byte[prefixes.length - covered.size()][];
for (int i = 0; i < prefixes.length; i++)
if (!covered.contains(i))
newPrefixes[count++] = prefixes[i];
return newPrefixes;
}
private boolean covers(byte[] prefix1, byte[] prefix2) {
if (prefix1.length > prefix2.length)
return false;
for (int i = 0; i < prefix1.length; i++)
if (prefix1[i] != prefix2[i])
return false;
return true;
}
protected void initIndexMap() {
indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < indices.length; i++)
indexMap.put(indices[i], i);
}
// public static void main(String[] args) throws Exception {
// System.out.println(DefaultSnapshotConfig.binarySearch("mo".getBytes(),
// new byte[][] {
// "algo".getBytes(), "blarg".getBytes(), "mo".getBytes(), "o".getBytes(),
// "xzu".getBytes() }));
// }
//
// /**
// * Serializes the snapshot configuration to a buffer.
// *
// * @return a buffer containing a binary representation of the snapshot
// * configuration
// */
// public ReusableBuffer serialize(int dbId) {
//
// byte[] nameBytes = name.getBytes();
// int bufSize = Integer.SIZE / 8 + // dbId
// Integer.SIZE / 8 + // snapshot name length
// nameBytes.length + // snapshot name
// (indices.length + 1) * Integer.SIZE / 8 + // index list including
// // length
// Integer.SIZE / 8; // prefixes length
//
// if (prefixes != null)
// for (byte[][] prefixList : prefixes) {
// bufSize += Integer.SIZE / 8; // prefix count
// if (prefixList != null) {
// for (byte[] bytes : prefixList)
// bufSize += bytes.length + // prefix
// Integer.SIZE / 8; // prefix length
// }
// }
//
// ReusableBuffer buf = BufferPool.allocate(bufSize);
// buf.putInt(dbId);
// buf.putInt(nameBytes.length);
// buf.put(nameBytes);
//
// // store the index list
// buf.putInt(indices.length);
// for (int i : indices)
// buf.putInt(i);
//
// // store the prefix matrix
// buf.putInt(prefixes == null ? 0 : prefixes.length);
// if (prefixes != null)
// for (byte[][] prefixList : prefixes) {
// buf.putInt(prefixList == null ? 0 : prefixList.length);
// if (prefixList != null)
// for (byte[] bytes : prefixList) {
// buf.putInt(bytes.length);
// buf.put(bytes);
// }
// }
//
// buf.flip();
//
// return buf;
// }
//
// /**
// * Deserializes a snapshot configuration from a buffer.
// *
// * @param buf
// * the buffer
// * @return the snapshot configuration
// */
// public static SnapshotConfig deserialize(ReusableBuffer buf) {
//
// buf.getInt(); // skip the dbId
// byte[] nameBytes = new byte[buf.getInt()];
// buf.get(nameBytes);
//
// // store the index list
// int[] indices = new int[buf.getInt()];
// for (int i = 0; i < indices.length; i++)
// indices[i] = buf.getInt();
//
// // store the prefix matrix
// int prefLength = buf.getInt();
// byte[][][] prefixes = prefLength == 0 ? null : new byte[prefLength][][];
// if (prefixes != null)
// for (int i = 0; i < prefixes.length; i++) {
// int len = buf.getInt();
// prefixes[i] = len == 0 ? null : new byte[len][];
// if (prefixes[i] != null)
// for (int j = 0; j < prefixes[i].length; j++) {
// prefixes[i][j] = new byte[buf.getInt()];
// buf.get(prefixes[i][j]);
// }
// }
//
// return new SnapshotConfig(new String(nameBytes), indices, prefixes);
// }
}