Temporarily include bndlib 1.47 for testing purposes (not yet on central)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1185095 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundleplugin/src/main/java/aQute/lib/index/Index.java b/bundleplugin/src/main/java/aQute/lib/index/Index.java
new file mode 100644
index 0000000..35662d0
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/index/Index.java
@@ -0,0 +1,352 @@
+package aQute.lib.index;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.channels.FileChannel.MapMode;
+import java.util.*;
+
+/**
+ * <pre>
+ * 0 -> 0, 122 -> 1
+ * 123 -> 123, 244 -> 2
+ * 245 -> 245, ...
+ * </pre>
+ *
+ *
+ */
+public class Index implements Iterable<byte[]> {
+ final static int LEAF = 0;
+ final static int INDEX = 1;
+
+ final static int SIGNATURE = 0;
+ final static int MAGIC = 0x494C4458;
+ final static int KEYSIZE = 4;
+
+ private FileChannel file;
+ final int pageSize = 4096;
+ final int keySize;
+ final int valueSize = 8;
+ final int capacity;
+ public Page root;
+ final LinkedHashMap<Integer, Page> cache = new LinkedHashMap<Integer, Index.Page>();
+ final MappedByteBuffer settings;
+
+ private int nextPage;
+
+ class Page {
+ final int TYPE_OFFSET = 0;
+ final int COUNT_OFFSET = 2;
+ final static int START_OFFSET = 4;
+ final int number;
+ boolean leaf;
+ final MappedByteBuffer buffer;
+ int n = 0;
+ boolean dirty;
+
+ Page(int number) throws IOException {
+ this.number = number;
+ buffer = file.map(MapMode.READ_WRITE, number * pageSize, pageSize);
+ n = buffer.getShort(COUNT_OFFSET);
+ int type = buffer.getShort(TYPE_OFFSET);
+ leaf = type != 0;
+ }
+
+ Page(int number, boolean leaf) throws IOException {
+ this.number = number;
+ this.leaf = leaf;
+ this.n = 0;
+ buffer = file.map(MapMode.READ_WRITE, number * pageSize, pageSize);
+ }
+
+ Iterator<byte[]> iterator() {
+ return new Iterator<byte[]>() {
+ Iterator<byte[]> i;
+ int rover = 0;
+
+ public byte[] next() {
+ if (leaf) {
+ return k(rover++);
+ }
+
+ return i.next();
+ }
+
+ public boolean hasNext() {
+ try {
+ if (leaf)
+ return rover < n;
+ else {
+ while (i == null || i.hasNext() == false) {
+ int c = (int) c(rover++);
+ i = getPage(c).iterator();
+ }
+ return i.hasNext();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ };
+ }
+
+ void write() throws IOException {
+ buffer.putShort(COUNT_OFFSET, (short) n);
+ buffer.put(TYPE_OFFSET, (byte) (leaf ? 1 : 0));
+ buffer.force();
+ }
+
+ int compare(byte[] key, int i) {
+ int index = pos(i);
+ for (int j = 0; j < keySize; j++, index++) {
+ int a = 0;
+ if (j < key.length)
+ a = key[j] & 0xFF;
+
+ int b = buffer.get(index) & 0xFF;
+ if (a == b)
+ continue;
+
+ return a > b ? 1 : -1;
+ }
+ return 0;
+ }
+
+ int pos(int i) {
+ return START_OFFSET + size(i);
+ }
+
+ int size(int n) {
+ return n * (keySize + valueSize);
+ }
+
+ void copyFrom(Page page, int start, int length) {
+ copy(page.buffer, pos(start), buffer, pos(0), size(length));
+ }
+
+ void copy(ByteBuffer src, int srcPos, ByteBuffer dst, int dstPos, int length) {
+ if (srcPos < dstPos) {
+ while (length-- > 0)
+ dst.put(dstPos + length, src.get(srcPos + length));
+
+ } else {
+ while (length-- > 0)
+ dst.put(dstPos++, src.get(srcPos++));
+ }
+ }
+
+ long search(byte[] k) throws Exception {
+ int cmp = 0;
+ int i = n - 1;
+ while (i >= 0 && (cmp = compare(k, i)) < 0)
+ i--;
+
+ if (leaf) {
+ if (cmp != 0)
+ return -1;
+ else
+ return c(i);
+ } else {
+ long value = c(i);
+ Page child = getPage((int) value);
+ return child.search(k);
+ }
+ }
+
+ void insert(byte[] k, long v) throws IOException {
+ if (n == capacity) {
+ int t = capacity / 2;
+ Page left = allocate(leaf);
+ Page right = allocate(leaf);
+ left.copyFrom(this, 0, t);
+ left.n = t;
+ right.copyFrom(this, t, capacity - t);
+ right.n = capacity - t;
+ leaf = false;
+ set(0, left.k(0), left.number);
+ set(1, right.k(0), right.number);
+ n = 2;
+ left.write();
+ right.write();
+ }
+ insertNonFull(k, v);
+ }
+
+ byte[] k(int i) {
+ buffer.position(pos(i));
+ byte[] key = new byte[keySize];
+ buffer.get(key);
+ return key;
+ }
+
+ long c(int i) {
+ if (i < 0) {
+ System.out.println("Arghhh");
+ }
+ int index = pos(i) + keySize;
+ return buffer.getLong(index);
+ }
+
+ void set(int i, byte[] k, long v) {
+ int index = pos(i);
+ for (int j = 0; j < keySize; j++) {
+ byte a = 0;
+ if (j < k.length)
+ a = k[j];
+ buffer.put(index + j, a);
+ }
+ buffer.putLong(index + keySize, v);
+ }
+
+ void insertNonFull(byte[] k, long v) throws IOException {
+ int cmp = 0;
+ int i = n - 1;
+ while (i >= 0 && (cmp = compare(k, i)) < 0)
+ i--;
+
+ if (leaf) {
+ if (cmp != 0) {
+ i++;
+ if (i != n)
+ copy(buffer, pos(i), buffer, pos(i + 1), size(n - i));
+ }
+ set(i, k, v);
+ n++;
+ dirty = true;
+ } else {
+ long value = c(i);
+ Page child = getPage((int) value);
+
+ if (child.n == capacity) {
+ Page left = child;
+ int t = capacity / 2;
+ Page right = allocate(child.leaf);
+ right.copyFrom(left, t, capacity - t);
+ right.n = capacity - t;
+ left.n = t;
+ i++; // place to insert
+ if (i < n) // ok if at end
+ copy(buffer, pos(i), buffer, pos(i + 1), size(n - i));
+ set(i, right.k(0), right.number);
+ n++;
+ assert i < n;
+ child = right.compare(k, 0) >= 0 ? right : left;
+ left.dirty = true;
+ right.dirty = true;
+ this.dirty = true;
+ }
+ child.insertNonFull(k, v);
+ }
+ write();
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ try {
+ toString( sb, "");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return sb.toString();
+ }
+
+ public void toString( StringBuilder sb, String indent ) throws IOException {
+ for (int i = 0; i < n; i++) {
+ sb.append(String.format("%s %02d:%02d %20s %s %d\n", indent, number, i, hex(k(i), 0, 4), leaf ? "==" : "->", c(i)));
+ if (! leaf ) {
+ long c = c(i);
+ Page sub = getPage((int)c);
+ sub.toString(sb,indent+" ");
+ }
+ }
+ }
+
+ private String hex(byte[] k, int i, int j) {
+ StringBuilder sb = new StringBuilder();
+
+ while ( i < j) {
+ int b = 0xFF & k[i];
+ sb.append(nibble(b>>4));
+ sb.append(nibble(b));
+ i++;
+ }
+ return sb.toString();
+ }
+
+ private char nibble(int i) {
+ i = i & 0xF;
+ return (char) ( i >= 10 ? i + 'A' - 10 : i + '0');
+ }
+
+ }
+
+ public Index(File file, int keySize) throws IOException {
+ capacity = (pageSize - Page.START_OFFSET) / (keySize + valueSize);
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+ this.file = raf.getChannel();
+ settings = this.file.map(MapMode.READ_WRITE, 0, pageSize);
+ if (this.file.size() == pageSize) {
+ this.keySize = keySize;
+ settings.putInt(SIGNATURE, MAGIC);
+ settings.putInt(KEYSIZE, keySize);
+ nextPage = 1;
+ root = allocate(true);
+ root.n = 1;
+ root.set(0, new byte[KEYSIZE], 0);
+ root.write();
+ } else {
+ if (settings.getInt(SIGNATURE) != MAGIC)
+ throw new IllegalStateException("No Index file, magic is not " + MAGIC);
+
+ this.keySize = settings.getInt(KEYSIZE);
+ if (keySize != 0 && this.keySize != keySize)
+ throw new IllegalStateException("Invalid key size for Index file. The file is "
+ + this.keySize + " and was expected to be " + this.keySize);
+
+ root = getPage(1);
+ nextPage = (int) (this.file.size() / pageSize);
+ }
+ }
+
+ public void insert(byte[] k, long v) throws Exception {
+ root.insert(k, v);
+ }
+
+ public long search(byte[] k) throws Exception {
+ return root.search(k);
+ }
+
+ Page allocate(boolean leaf) throws IOException {
+ Page page = new Page(nextPage++, leaf);
+ cache.put(page.number, page);
+ return page;
+ }
+
+ Page getPage(int number) throws IOException {
+ Page page = cache.get(number);
+ if (page == null) {
+ page = new Page(number);
+ cache.put(number, page);
+ }
+ return page;
+ }
+
+ public String toString() {
+ return root.toString();
+ }
+
+ public void close() throws IOException {
+ file.close();
+ cache.clear();
+ }
+
+ public Iterator<byte[]> iterator() {
+ return root.iterator();
+ }
+}
diff --git a/bundleplugin/src/main/java/aQute/lib/index/packageinfo b/bundleplugin/src/main/java/aQute/lib/index/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/bundleplugin/src/main/java/aQute/lib/index/packageinfo
@@ -0,0 +1 @@
+version 1.0