You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@labs.apache.org by ka...@apache.org on 2009/03/17 11:14:04 UTC

svn commit: r755179 [2/3] - in /labs/bananadb/trunk: ./ src/main/java/org/apache/labs/bananadb/entity/ src/main/java/org/apache/labs/bananadb/entity/isolation/ src/main/java/org/apache/labs/bananadb/entity/serialization/ src/main/java/org/apache/labs/b...

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Accessor.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Accessor.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Accessor.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Accessor.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,364 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+import org.apache.labs.bananadb.store.lock.Lock;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * This class is not thread safe. I.e. you'll need to instantiate one per thread!
+ *
+ * @author kalle
+ * @see org.apache.labs.bananadb.store.Store#createAccessor(boolean)
+ * @since 2009-mar-16 14:20:33
+ */
+public class Accessor {
+
+  private static final Log log = new Log(Accessor.class);
+
+  private Store store;
+  private String access;
+
+  private Lock writeLock;
+
+  private Metadata metadata;
+  private Hashtable hashtable;
+  private Map<Integer, HashCodesPartition> hashCodesPartitions = new HashMap<Integer, HashCodesPartition>();
+  private Map<Integer, KeysPartition> keyPartitions = new HashMap<Integer, KeysPartition>();
+  private Map<Integer, ValuesPartition> valuePartitions = new HashMap<Integer, ValuesPartition>();
+
+  Accessor(final Store store, boolean readOnly) throws IOException {
+    this.store = store;
+    access = readOnly ? "r" : "rw";
+    writeLock = store.getConfiguration().getLockFactory().makeLock("lock");
+    metadata = new Metadata(store.getConfiguration().getDataPath(), access);
+
+    if (metadata.getFile().exists()) {
+      metadata.open();      
+    } else {
+
+      long ms = System.currentTimeMillis();
+
+      if (readOnly) {
+        throw new IOException("Can not create a new store when accessor is in read only mode");
+      }
+
+      log.info("Creating new store..");
+
+      Lock.With width = new Lock.With(writeLock, store.getConfiguration().getLockWaitTimeoutMilliseconds()) {
+        protected Object doBody() throws IOException {
+          if (!metadata.getFile().exists()) {
+
+            metadata.format(metadata.getHeaderByteSize());
+            metadata.open();
+            Metadata.Header mdh = new Metadata.Header();
+            mdh.setFileFormatVersion(0);
+            mdh.setCommitVersion(0);
+            mdh.setCurrentHashtableId(0);
+            mdh.setCurrentHashCodesPartition(0);
+            mdh.setCurrentKeysPartition(0);
+            mdh.setValuePostingsCount(0);
+            metadata.writeHeader(mdh);
+
+            hashtable = new Hashtable(store.getConfiguration().getDataPath(), 0, access);
+            hashtable.format((store.getConfiguration().getInitialCapacity() * Hashtable.Posting.POSTING_BYTE_SIZE) + hashtable.getHeaderByteSize());
+            hashtable.open();
+            Hashtable.Header hth = new Hashtable.Header();
+            hth.setPostingsCapacity(store.getConfiguration().getInitialCapacity());
+            hashtable.writeHeader(hth);
+
+          }
+          return null;
+        }
+      };
+      width.run();
+
+      // these could be opened lazy, but let's do it now.
+      getHashCodesPartition(0);
+      getKeysPartition(0);
+      getValuesPartition(0);
+
+      ms = System.currentTimeMillis() - ms;
+      log.info("New store has been created. Took " + ms + " milliseconds.");
+
+    }
+  }
+
+  public void close() throws IOException {
+    metadata.close();
+    hashtable.close();
+    for (FileHandler fileHandler : hashCodesPartitions.values()) {
+      fileHandler.close();
+    }
+    for (FileHandler fileHandler : keyPartitions.values()) {
+      fileHandler.close();
+    }
+    for (FileHandler fileHandler : valuePartitions.values()) {
+      fileHandler.close();
+    }
+    metadata = null;
+    hashtable = null;
+    hashCodesPartitions = null;
+    keyPartitions = null;
+    valuePartitions = null;
+
+    store.getAccessors().remove(this);
+  }
+
+
+  public Metadata getMetadata() throws IOException {
+    return metadata;
+  }
+
+
+  public Hashtable getHashtable() throws IOException {
+    Metadata.Header metadataHeader = new Metadata.Header();
+    metadata.readHeader(metadataHeader);
+    if (hashtable == null || metadataHeader.getCurrentHashtableId() != hashtable.getVersionId()) {
+      if (hashtable != null) {
+        hashtable.getRAF().close();
+      }
+      hashtable = new Hashtable(store.getConfiguration().getDataPath(), metadataHeader.getCurrentHashtableId(), access);
+      hashtable.open();
+    }
+    return hashtable;
+  }
+
+  public HashCodesPartition getHashCodesPartition(int partitionId) throws IOException {
+    HashCodesPartition partition = hashCodesPartitions.get(partitionId);
+    if (partition == null) {
+      partition = new HashCodesPartition(store.getConfiguration().getDataPath(), partitionId, access);
+      if (!partition.getFile().exists()) {
+        final HashCodesPartition p = partition;
+        Lock.With with = new Lock.With(writeLock, store.getConfiguration().getLockWaitTimeoutMilliseconds()) {
+          protected Object doBody() throws IOException {
+            if (!p.getFile().exists()) {
+              p.format(store.getConfiguration().getHashCodesPartitionByteSize());
+              p.open();
+              HashCodesPartition.Header hch = new HashCodesPartition.Header();
+              hch.setNextPostingOffset(p.getHeaderByteSize());
+              hch.setBytesLeft(store.getConfiguration().getHashCodesPartitionByteSize() - p.getHeaderByteSize());
+              p.writeHeader(hch);
+            }
+            return null;
+          }
+        };
+        with.run();
+      } else {
+        partition.open();
+      }
+      hashCodesPartitions.put(partitionId, partition);
+    }
+    return partition;
+  }
+
+  public KeysPartition getKeysPartition(int partitionId) throws IOException {
+    KeysPartition partition = keyPartitions.get(partitionId);
+    if (partition == null) {
+      partition = new KeysPartition(store.getConfiguration().getDataPath(), partitionId, access);
+      if (!partition.getFile().exists()) {
+        final KeysPartition p = partition;
+        Lock.With with = new Lock.With(writeLock, store.getConfiguration().getLockWaitTimeoutMilliseconds()) {
+          protected Object doBody() throws IOException {
+            if (!p.getFile().exists()) {
+              p.format(store.getConfiguration().getKeysPartitionByteSize());
+              p.open();
+              KeysPartition.Header kh = new KeysPartition.Header();
+              kh.setNextPostingOffset(p.getHeaderByteSize());
+              kh.setBytesLeft(store.getConfiguration().getKeysPartitionByteSize() - p.getHeaderByteSize());
+              p.writeHeader(kh);
+            }
+            return null;
+          }
+        };
+        with.run();
+      } else {
+        partition.open();
+      }
+      keyPartitions.put(partitionId, partition);
+    }
+    return partition;
+  }
+
+  public ValuesPartition getValuesPartition(int partitionId) throws IOException {
+    ValuesPartition partition = valuePartitions.get(partitionId);
+    if (partition == null) {
+      partition = new ValuesPartition(store.getConfiguration().getDataPath(), partitionId, access);
+      if (!partition.getFile().exists()) {
+        final ValuesPartition p = partition;
+        Lock.With with = new Lock.With(writeLock, store.getConfiguration().getLockWaitTimeoutMilliseconds()) {
+          protected Object doBody() throws IOException {
+            if (!p.getFile().exists()) {
+              p.format(store.getConfiguration().getValuesPartitionByteSize());
+              p.open();
+              ValuesPartition.Header vh = new ValuesPartition.Header();
+              vh.setNextPostingOffset(p.getHeaderByteSize());
+              vh.setBytesLeft(store.getConfiguration().getValuesPartitionByteSize() - p.getHeaderByteSize());
+              p.writeHeader(vh);
+            }
+            return null;
+          }
+        };
+        with.run();
+      } else {
+        partition.open();
+      }
+      valuePartitions.put(partitionId, partition);
+    }
+    return partition;
+  }
+
+  /**
+   * Require write lock!
+   *
+   * @param posting
+   * @return
+   * @throws IOException
+   */
+  public RequestPartitionWriterResponse<ValuesPartition> requestValueWrite(ValuesPartition.Posting posting) throws IOException {
+
+    int requestedBytes = posting.getPostingByteSize();
+
+    Metadata.Header mdh = new Metadata.Header();
+    metadata.readHeader(mdh);
+
+    ValuesPartition vp = getValuesPartition(mdh.getCurrentValuesPartition());
+    ValuesPartition.Header vph = new ValuesPartition.Header();
+
+    vp.readHeader(vph);
+    if (vph.getBytesLeft() < requestedBytes) {
+      mdh.setCurrentValuesPartition(mdh.getCurrentValuesPartition() + 1);
+      metadata.writeHeader(mdh);
+
+      vp = getValuesPartition(mdh.getCurrentValuesPartition());
+      vp.readHeader(vph);
+    }
+
+    RequestPartitionWriterResponse<ValuesPartition> response = new RequestPartitionWriterResponse<ValuesPartition>();
+
+    response.fileHandler = vp;
+    response.startOffset = vph.getNextPostingOffset();
+
+    vph.setBytesLeft(vph.getBytesLeft() - requestedBytes);
+    vph.setNextPostingOffset(vph.getNextPostingOffset() + requestedBytes);
+    vp.writeHeader(vph);
+
+    return response;
+  }
+
+  /**
+   * Require write lock!
+   *
+   * @param posting
+   * @return
+   * @throws IOException
+   */
+  public RequestPartitionWriterResponse<KeysPartition> requestValueWrite(KeysPartition.Posting posting) throws IOException {
+
+    int requestedBytes = posting.getPostingByteSize();
+
+    Metadata.Header mdh = new Metadata.Header();
+    metadata.readHeader(mdh);
+
+    KeysPartition kp = getKeysPartition(mdh.getCurrentKeysPartition());
+    KeysPartition.Header kh = new KeysPartition.Header();
+
+    kp.readHeader(kh);
+    if (kh.getBytesLeft() < requestedBytes) {
+      mdh.setCurrentValuesPartition(mdh.getCurrentKeysPartition() + 1);
+      metadata.writeHeader(mdh);
+
+      kp = getKeysPartition(mdh.getCurrentKeysPartition());
+      kp.readHeader(kh);
+    }
+
+    RequestPartitionWriterResponse<KeysPartition> response = new RequestPartitionWriterResponse<KeysPartition>();
+
+    response.fileHandler = kp;
+    response.startOffset = kh.getNextPostingOffset();
+
+    kh.setBytesLeft(kh.getBytesLeft() - requestedBytes);
+    kh.setNextPostingOffset(kh.getNextPostingOffset() + requestedBytes);
+    kp.writeHeader(kh);
+
+    return response;
+  }
+
+  /**
+   * Require write lock!
+   *
+   * @param posting
+   * @return
+   * @throws IOException
+   */
+  public RequestPartitionWriterResponse<HashCodesPartition> requestValueWrite(HashCodesPartition.Posting posting) throws IOException {
+
+    int requestedBytes = posting.getPostingByteSize();
+
+    Metadata.Header mdh = new Metadata.Header();
+    metadata.readHeader(mdh);
+
+    HashCodesPartition hcp = getHashCodesPartition(mdh.getCurrentHashCodesPartition());
+    HashCodesPartition.Header hch = new HashCodesPartition.Header();
+
+    hcp.readHeader(hch);
+    if (hch.getBytesLeft() < requestedBytes) {
+      mdh.setCurrentValuesPartition(mdh.getCurrentHashCodesPartition() + 1);
+      metadata.writeHeader(mdh);
+
+      hcp = getHashCodesPartition(mdh.getCurrentHashCodesPartition());
+      hcp.readHeader(hch);
+    }
+
+    RequestPartitionWriterResponse<HashCodesPartition> response = new RequestPartitionWriterResponse<HashCodesPartition>();
+
+    response.fileHandler = hcp;
+    response.startOffset = hch.getNextPostingOffset();
+
+    hch.setBytesLeft(hch.getBytesLeft() - requestedBytes);
+    hch.setNextPostingOffset(hch.getNextPostingOffset() + requestedBytes);
+    hcp.writeHeader(hch);
+
+    return response;
+  }
+
+  public static class RequestPartitionWriterResponse<T extends FileHandler> {
+    private T fileHandler;
+    private int startOffset;
+
+    public T getFileHandler() {
+      return fileHandler;
+    }
+
+    public int getStartOffset() {
+      return startOffset;
+    }
+  }
+
+  public Lock getWriteLock() {
+    return writeLock;
+  }
+
+  public Store getStore() {
+    return store;
+  }
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Configuration.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Configuration.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Configuration.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Configuration.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,150 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+import org.apache.labs.bananadb.store.lock.LockFactory;
+import org.apache.labs.bananadb.store.lock.NativeFSLockFactory;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author kalle
+ * @since 2009-mar-16 15:53:03
+ */
+public class Configuration {
+
+  /**
+   * Location of directory containing the data files.
+   * Only one hashtable can exist in a path.
+   */
+  private File dataPath;
+
+  public Configuration(File dataPath) throws IOException {
+    this.dataPath = dataPath;
+    lockFactory = new NativeFSLockFactory(dataPath);
+  }
+
+  /**
+   * Number of items you want to fit in the hashtable from the start.
+   */
+  private int initialCapacity = 800000;
+
+  /**
+   * The factor of which to grow the capacity on rehash.
+   */
+  private double automaticRehashCapacityGrowFactor = 1.7d;
+
+  /**
+   * Ratio of available key posting left required to trigger a rehash at put()-time.
+   * <p/>
+   * The default identified sweetspot
+   * is to have at least 8x greater capacity than items in the hashtable.
+   * <p/>
+   * In order to automatically rehash so you never fill the hashtable to more than 1/8
+   * you have set this value to 1/8 (0.125).
+   *
+   * A number greater than 0 and less than 1.
+   */
+  private double automaticRehashThreadshold = 0.125d;
+
+
+  private LockFactory lockFactory;
+
+  private long lockWaitTimeoutMilliseconds = 60000;
+
+  public static final int megaByte = 1024 * 1024;
+
+  private int valuesPartitionByteSize = 100 * megaByte;
+  private int keysPartitionByteSize = 10 * megaByte;
+  private int hashCodesPartitionByteSize = 10 * megaByte;
+
+
+  public int getInitialCapacity() {
+    return initialCapacity;
+  }
+
+  public void setInitialCapacity(int initialCapacity) {
+    this.initialCapacity = initialCapacity;
+  }
+
+  public double getAutomaticRehashCapacityGrowFactor() {
+    return automaticRehashCapacityGrowFactor;
+  }
+
+  public void setAutomaticRehashCapacityGrowFactor(double automaticRehashCapacityGrowFactor) {
+    this.automaticRehashCapacityGrowFactor = automaticRehashCapacityGrowFactor;
+  }
+
+  public double getAutomaticRehashThreadshold() {
+    return automaticRehashThreadshold;
+  }
+
+  public void setAutomaticRehashThreadshold(double automaticRehashThreadshold) {
+    this.automaticRehashThreadshold = automaticRehashThreadshold;
+  }
+
+  public LockFactory getLockFactory() {
+    return lockFactory;
+  }
+
+  public void setLockFactory(LockFactory lockFactory) {
+    this.lockFactory = lockFactory;
+  }
+
+  public long getLockWaitTimeoutMilliseconds() {
+    return lockWaitTimeoutMilliseconds;
+  }
+
+  public void setLockWaitTimeoutMilliseconds(long lockWaitTimeoutMilliseconds) {
+    this.lockWaitTimeoutMilliseconds = lockWaitTimeoutMilliseconds;
+  }
+
+  public int getValuesPartitionByteSize() {
+    return valuesPartitionByteSize;
+  }
+
+  public void setValuesPartitionByteSize(int valuesPartitionByteSize) {
+    this.valuesPartitionByteSize = valuesPartitionByteSize;
+  }
+
+  public int getKeysPartitionByteSize() {
+    return keysPartitionByteSize;
+  }
+
+  public void setKeysPartitionByteSize(int keysPartitionByteSize) {
+    this.keysPartitionByteSize = keysPartitionByteSize;
+  }
+
+  public int getHashCodesPartitionByteSize() {
+    return hashCodesPartitionByteSize;
+  }
+
+  public void setHashCodesPartitionByteSize(int hashCodesPartitionByteSize) {
+    this.hashCodesPartitionByteSize = hashCodesPartitionByteSize;
+  }
+
+  public File getDataPath() {
+    return dataPath;
+  }
+
+  public void setDataPath(File dataPath) {
+    this.dataPath = dataPath;
+  }
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Cursor.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Cursor.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Cursor.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Cursor.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,32 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+/**
+ * Cursor implementations must be thread safe!!!
+ * The easiet way is to synchronize the next() method
+ *
+ * @author kalle
+ * @since 2009-mar-16 15:37:56
+ */
+public interface Cursor {
+
+  public abstract byte[] next(Accessor accessor);
+
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/FileHandler.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/FileHandler.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/FileHandler.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/FileHandler.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,180 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+
+/**
+ * @author kalle
+ * @since 2009-mar-16 15:28:29
+ */
+public abstract class FileHandler<H extends FileHandler.Header, P extends FileHandler.Posting> {
+
+  private static final Log log = new Log(FileHandler.class);
+
+  private File file;
+  private RandomAccessFile RAF;
+  private String access;
+
+  protected FileHandler(File directory, int id, String suffix, String access) throws IOException {
+    StringBuilder sb = new StringBuilder(15);
+    sb.append(String.valueOf(id));
+    while (sb.length() < 8) {
+      sb.insert(0, "0");
+    }
+    sb.append(".");
+    sb.append(suffix);
+    this.file = new File(directory, sb.toString());
+    this.access = access;
+  }
+
+  public void format(long size) throws IOException {
+    format(size, (byte) 0);
+  }
+
+  public void format(long size, byte defaultValue) throws IOException {
+    log.info("Formatting " + file.getAbsolutePath() + "..");
+
+    long ms = System.currentTimeMillis();
+
+    long leftToWrite = size;
+
+    FileOutputStream fos = new FileOutputStream(file);
+    int bufSize = Math.min(1024 * 1024, (int)(size / 5));
+    byte[] bytes = new byte[bufSize];
+    Arrays.fill(bytes, defaultValue);
+    while (leftToWrite >= bytes.length) {
+      fos.write(bytes);
+      leftToWrite -= bytes.length;
+    }
+    if (leftToWrite > 0) {
+      fos.write(bytes, defaultValue, (int) leftToWrite);
+    }
+    fos.close();
+
+    log.info("It took " + (System.currentTimeMillis() - ms) + " milliseconds to format " + file.getAbsolutePath());
+  }
+
+  public void open() throws IOException {
+    if (RAF != null) {
+      throw new IOException("Already open");
+    }
+    this.RAF = new RandomAccessFile(file, access);
+  }
+
+  public void close() throws IOException {
+    if (RAF == null) {
+      throw new IOException("Already closed");
+    }
+    RAF.close();
+  }
+
+  public abstract int getHeaderByteSize();
+
+  public File getFile() {
+    return file;
+  }
+
+  public RandomAccessFile getRAF() {
+    return RAF;
+  }
+
+  public static abstract class Header {
+  }
+
+  public static abstract class Posting {
+    public abstract int getPostingByteSize();
+  }
+
+//  public void writePosting(P posting) throws IOException {
+//    writePosting(posting, getRAF());
+//  }
+
+  /**
+   * Marks the posting at the start offset as deleted
+   * @param startOffset
+   * @throws IOException
+   */
+  public void deletePosting(int startOffset) throws IOException {
+    deletePosting(startOffset, RAF);
+  }
+
+  public abstract void deletePosting(int startOffset, RandomAccessFile RAF) throws IOException;
+
+  public void writePosting(P posting, int startOffset) throws IOException {
+    writePosting(posting, startOffset, getRAF());
+  }
+
+  public void writePosting(P posting, int startOffset, RandomAccessFile RAF) throws IOException {
+    RAF.seek(startOffset);
+    writePosting(posting, RAF);
+  }
+
+  public abstract void writePosting(P posting, RandomAccessFile RAF) throws IOException;
+
+//  public void readPosting(P posting) throws IOException {
+//    readPosting(posting, getRAF());
+//  }
+
+  public void readPosting(P posting, int startOffset) throws IOException {
+    readPosting(posting, startOffset, getRAF());
+  }
+
+  public void readPosting(P posting, int startOffset, RandomAccessFile RAF) throws IOException {
+    RAF.seek(startOffset);
+    readPosting(posting, RAF);
+  }
+
+  public abstract void readPosting(P posting, RandomAccessFile RAF) throws IOException;
+
+
+  public void writeHeader(H header) throws IOException {
+    writeHeader(header, 0, getRAF());
+  }
+
+  public void writeHeader(H header, int startOffset) throws IOException {
+    writeHeader(header, startOffset, getRAF());
+  }
+
+  public void writeHeader(H header, int startOffset, RandomAccessFile RAF) throws IOException {
+    RAF.seek(startOffset);
+    writeHeader(header, RAF);
+  }
+
+  public abstract void writeHeader(H header, RandomAccessFile RAF) throws IOException;
+
+  public void readHeader(H header) throws IOException {
+    readHeader(header, 0, RAF);
+  }
+
+  public void readHeader(H header, int startOffset) throws IOException {
+    readHeader(header, startOffset, getRAF());
+  }
+
+  public void readHeader(H header, int startOffset, RandomAccessFile RAF) throws IOException {
+    RAF.seek(startOffset);
+    readHeader(header, RAF);
+  }
+
+  public abstract void readHeader(H header, RandomAccessFile RAF) throws IOException;
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/HashCodesPartition.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/HashCodesPartition.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/HashCodesPartition.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/HashCodesPartition.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,212 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * Hash code postings partition file.
+ * <p/>
+ * Chained postings. Each postings has a unique hash code value and points at how to find
+ * the key posting for this hash code.
+ * <p/>
+ * This file is affected by rehashing.
+ *
+ * @author kalle
+ * @since 2009-mar-16 14:00:37
+ */
+public class HashCodesPartition extends FileHandler<HashCodesPartition.Header, HashCodesPartition.Posting> {
+
+  private int partitionId;
+
+  public HashCodesPartition(File directory, int partitionId, String access) throws IOException {
+    super(directory, partitionId, "hc", access);
+    this.partitionId = partitionId;
+  }
+
+  public int getPartitionId() {
+    return partitionId;
+  }
+
+  public static final int HEADER_BYTE_SIZE = 1024;  
+  public int getHeaderByteSize() {
+    return HEADER_BYTE_SIZE;
+  }
+
+  public static class Header extends FileHandler.Header {
+
+    /**
+     * Offset in this partition for next new posting.
+     */
+    private int nextPostingOffset;
+    /**
+     * Bytes left for use in this partition.
+     */
+    private int bytesLeft;
+
+    public void update(Posting posting) {
+      nextPostingOffset += POSTING_BYTE_SIZE;
+      bytesLeft -= POSTING_BYTE_SIZE;
+    }
+
+    public int getNextPostingOffset() {
+      return nextPostingOffset;
+    }
+
+    public void setNextPostingOffset(int nextPostingOffset) {
+      this.nextPostingOffset = nextPostingOffset;
+    }
+
+    public int getBytesLeft() {
+      return bytesLeft;
+    }
+
+    public void setBytesLeft(int bytesLeft) {
+      this.bytesLeft = bytesLeft;
+    }
+
+
+  }
+
+  public static final int POSTING_BYTE_SIZE = 1 + 8 + 4 + 4 + 4 + 4;
+
+  public static class Posting extends FileHandler.Posting {
+
+    public int getPostingByteSize() {
+      return POSTING_BYTE_SIZE;
+    }
+
+    /**
+     * 0 = never used
+     * 1 = in use
+     * 2 = deleted
+     */
+    private byte flag;
+
+    /**
+     * Key hash code.
+     */
+    private long keyHashCode;
+
+    /**
+     * Partition id of next hash code posting with the same hashtable posting position.
+     * -1 == no more hash code postings in chain
+     */
+    private int nextPostingPartition;
+
+    /**
+     * Offset in above hash code postings partition.
+     */
+    private int nextPostingPartitionOffset;
+
+
+    /**
+     * Partition id of first key posting with this hash code.
+     */
+    private int firstKeyPostingPartition;
+
+    /**
+     * Offset in above key postings partition.
+     */
+    private int firstKeyPostingPartitionOffset;
+
+    public byte getFlag() {
+      return flag;
+    }
+
+    public void setFlag(byte flag) {
+      this.flag = flag;
+    }
+
+    public long getKeyHashCode() {
+      return keyHashCode;
+    }
+
+    public void setKeyHashCode(long keyHashCode) {
+      this.keyHashCode = keyHashCode;
+    }
+
+    public int getNextPostingPartition() {
+      return nextPostingPartition;
+    }
+
+    public void setNextPostingPartition(int nextPostingPartition) {
+      this.nextPostingPartition = nextPostingPartition;
+    }
+
+    public int getNextPostingPartitionOffset() {
+      return nextPostingPartitionOffset;
+    }
+
+    public void setNextPostingPartitionOffset(int nextPostingPartitionOffset) {
+      this.nextPostingPartitionOffset = nextPostingPartitionOffset;
+    }
+
+    public int getFirstKeyPostingPartition() {
+      return firstKeyPostingPartition;
+    }
+
+    public void setFirstKeyPostingPartition(int firstKeyPostingPartition) {
+      this.firstKeyPostingPartition = firstKeyPostingPartition;
+    }
+
+    public int getFirstKeyPostingPartitionOffset() {
+      return firstKeyPostingPartitionOffset;
+    }
+
+    public void setFirstKeyPostingPartitionOffset(int firstKeyPostingPartitionOffset) {
+      this.firstKeyPostingPartitionOffset = firstKeyPostingPartitionOffset;
+    }
+  }
+
+  public void readHeader(Header header, RandomAccessFile RAF) throws IOException {
+    header.nextPostingOffset = RAF.readInt();
+    header.bytesLeft = RAF.readInt();
+  }
+
+  public void writeHeader(Header header, RandomAccessFile RAF) throws IOException {
+    RAF.writeInt(header.nextPostingOffset);
+    RAF.writeInt(header.bytesLeft);
+  }
+
+
+  public void readPosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    posting.flag = RAF.readByte();
+    posting.keyHashCode = RAF.readLong();
+    posting.nextPostingPartition = RAF.readInt();
+    posting.nextPostingPartitionOffset = RAF.readInt();
+    posting.firstKeyPostingPartition = RAF.readInt();
+    posting.firstKeyPostingPartitionOffset = RAF.readInt();
+  }
+
+  public void writePosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    RAF.writeByte(posting.flag);
+    RAF.writeLong(posting.keyHashCode);
+    RAF.writeInt(posting.nextPostingPartition);
+    RAF.writeInt(posting.nextPostingPartitionOffset);
+    RAF.writeInt(posting.firstKeyPostingPartition);
+    RAF.writeInt(posting.firstKeyPostingPartitionOffset);
+  }
+
+  public void deletePosting(int startOffset, RandomAccessFile RAF) throws IOException {
+    RAF.seek(startOffset);
+    RAF.writeByte((byte)2);
+  }
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Hashtable.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Hashtable.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Hashtable.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Hashtable.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,162 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * Hashtable file. There is never more than one of these that are valid at any given time.
+ * <p/>
+ * The position in the hashtable for a given hash code is calculated as (hash & (capacity - 1)).
+ * At this position there is a posting that points at the first known hash code posting.
+ * <p/>
+ * This file is affected by rehashing.
+ *
+ * @author kalle
+ * @since 2009-mar-16 14:00:56
+ */
+public class Hashtable extends FileHandler<Hashtable.Header, Hashtable.Posting> {
+
+
+  private int versionId;
+
+  /**
+   * header as when file was openend. used only to read capacity. but that is also all the header contains..
+   */
+  private Header header;
+
+  public Hashtable(File directory, int versionId, String access) throws IOException {
+    super(directory, versionId, "ht", access);
+    this.versionId = versionId;    
+  }
+
+  @Override
+  public void open() throws IOException {
+    super.open();
+    readHeader(header = new Header());    
+  }
+
+  public int getVersionId() {
+    return versionId;
+  }
+
+  public static final int HEADER_BYTE_SIZE = 1024;
+  public int getHeaderByteSize() {
+    return HEADER_BYTE_SIZE;
+  }
+
+  public static class Header extends FileHandler.Header {
+    /**
+     * This hashtable postings file capacity.
+     */
+    private int postingsCapacity;
+
+    public int getPostingsCapacity() {
+      return postingsCapacity;
+    }
+
+    public void setPostingsCapacity(int postingsCapacity) {
+      this.postingsCapacity = postingsCapacity;
+    }
+  }
+
+
+
+
+  public static class Posting extends FileHandler.Posting {
+
+    public static final int POSTING_BYTE_SIZE = 1+ 4 + 4;
+    public int getPostingByteSize() {
+      return POSTING_BYTE_SIZE;
+    }
+
+    /**
+     * 0 = never used
+     * 1 = in use
+     * 2 = deleted
+     */
+    private byte flag;
+
+    /**
+     * Partition id of first hash code posting with this hashtable position. -1 == null
+     */
+    private int hashCodePostingPartition;
+
+    /**
+     * Offset in above hash code postings partition.
+     */
+    private int hashCodePostingPartitionOffset;
+
+    public byte getFlag() {
+      return flag;
+    }
+
+    public void setFlag(byte flag) {
+      this.flag = flag;
+    }
+
+    public int getHashCodePostingPartition() {
+      return hashCodePostingPartition;
+    }
+
+    public void setHashCodePostingPartition(int hashCodePostingPartition) {
+      this.hashCodePostingPartition = hashCodePostingPartition;
+    }
+
+    public int getHashCodePostingPartitionOffset() {
+      return hashCodePostingPartitionOffset;
+    }
+
+    public void setHashCodePostingPartitionOffset(int hashCodePostingPartitionOffset) {
+      this.hashCodePostingPartitionOffset = hashCodePostingPartitionOffset;
+    }
+  }
+
+  public void readHeader(Header header, RandomAccessFile RAF) throws IOException {
+    header.postingsCapacity = RAF.readInt();
+  }
+
+  public void writeHeader(Header header, RandomAccessFile RAF) throws IOException {
+    RAF.writeInt(header.postingsCapacity);
+  }
+
+  public int calculateHashCodePostingOffset(long hashCode) {
+    return (int) (HEADER_BYTE_SIZE + (Posting.POSTING_BYTE_SIZE * (hashCode & (header.postingsCapacity - 1))));
+  }
+
+  public void readPosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    posting.flag = RAF.readByte();
+    posting.hashCodePostingPartition = RAF.readInt();
+    posting.hashCodePostingPartitionOffset = RAF.readInt();
+  }
+
+  public void writePosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    RAF.writeByte(posting.flag);
+    RAF.writeInt(posting.hashCodePostingPartition);
+    RAF.writeInt(posting.hashCodePostingPartitionOffset);
+  }
+
+  public void deletePosting(int startOffset, RandomAccessFile RAF) throws IOException {
+    RAF.seek(startOffset);
+    RAF.writeByte((byte)2);
+  }
+
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/KeysPartition.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/KeysPartition.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/KeysPartition.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/KeysPartition.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,251 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+import java.io.IOException;
+import java.io.File;
+import java.io.RandomAccessFile;
+
+/**
+ * Key postings partition file.
+ * <p/>
+ * Chained postings. Each posting contains a unique key value and points at how to
+ * find the value associated with this key.
+ * <p/>
+ * This file is NOT affected by rehashing.
+ *
+ * @author kalle
+ * @since 2009-mar-16 14:00:22
+ */
+public class KeysPartition extends FileHandler<KeysPartition.Header, KeysPartition.Posting> {
+
+  private int partitionId;
+
+  public KeysPartition(File directory, int partitionId, String access) throws IOException {
+    super(directory, partitionId, "k", access);
+    this.partitionId = partitionId;
+  }
+
+  public static final int HEADER_BYTE_SIZE = 1024;
+
+  public int getHeaderByteSize() {
+    return HEADER_BYTE_SIZE;
+  }
+
+  public int getPartitionId() {
+    return partitionId;
+  }
+
+  public static class Header extends FileHandler.Header {
+
+    /**
+     * Offset in this partition for next new posting.
+     */
+    private int nextPostingOffset;
+    /**
+     * Bytes left for use in this partition.
+     */
+    private int bytesLeft;
+
+    public void update(Posting posting) {
+      int postingByteSize = posting.getPostingByteSize();
+      nextPostingOffset += postingByteSize;
+      bytesLeft -= postingByteSize;
+    }    
+
+    public int getNextPostingOffset() {
+      return nextPostingOffset;
+    }
+
+    public void setNextPostingOffset(int nextPostingOffset) {
+      this.nextPostingOffset = nextPostingOffset;
+    }
+
+    public int getBytesLeft() {
+      return bytesLeft;
+    }
+
+    public void setBytesLeft(int bytesLeft) {
+      this.bytesLeft = bytesLeft;
+    }
+  }
+
+  public static class Posting extends FileHandler.Posting {
+
+
+    /**
+     * 0 = never used
+     * 1 = in use
+     * 2 = deleted
+     */
+    private byte flag;
+    
+
+    /**
+     * Partition id of next key posting with the same hash code.
+     * -1 == end of keys chain
+     * -2 == deleted key
+     */
+    private int nextKeyPostingPartition;
+    /**
+     * Offset in above key postings partition.
+     */
+    private int nextKeyPostingPartitionOffset;
+
+    /**
+     * Key hash code
+     */
+    private long keyHashCode;
+
+    /**
+     * Paritition id of value posting. -1 == null
+     */
+    private int valuePostingPartition;
+    /**
+     * Offset in above value postings partition.
+     */
+    private int valuePostingPartitionOffset;
+
+    /**
+     * Length in bytes of serialized key.
+     */
+    private int bytesLength;
+    /**
+     * Serialized key
+     */
+    private byte[] bytes;
+
+    public int getPostingByteSize() {
+      return 1 + 4 + 4 + 8 + 4 + 4 + 4 + bytesLength;
+    }
+
+    public byte getFlag() {
+      return flag;
+    }
+
+    public void setFlag(byte flag) {
+      this.flag = flag;
+    }
+
+    public int getNextKeyPostingPartition() {
+      return nextKeyPostingPartition;
+    }
+
+    public void setNextKeyPostingPartition(int nextKeyPostingPartition) {
+      this.nextKeyPostingPartition = nextKeyPostingPartition;
+    }
+
+    public int getNextKeyPostingPartitionOffset() {
+      return nextKeyPostingPartitionOffset;
+    }
+
+    public void setNextKeyPostingPartitionOffset(int nextKeyPostingPartitionOffset) {
+      this.nextKeyPostingPartitionOffset = nextKeyPostingPartitionOffset;
+    }
+
+    public long getKeyHashCode() {
+      return keyHashCode;
+    }
+
+    public void setKeyHashCode(long keyHashCode) {
+      this.keyHashCode = keyHashCode;
+    }
+
+    public int getValuePostingPartition() {
+      return valuePostingPartition;
+    }
+
+    public void setValuePostingPartition(int valuePostingPartition) {
+      this.valuePostingPartition = valuePostingPartition;
+    }
+
+    public int getValuePostingPartitionOffset() {
+      return valuePostingPartitionOffset;
+    }
+
+    public void setValuePostingPartitionOffset(int valuePostingPartitionOffset) {
+      this.valuePostingPartitionOffset = valuePostingPartitionOffset;
+    }
+
+    public int getBytesLength() {
+      return bytesLength;
+    }
+
+    public void setBytesLength(int bytesLength) {
+      this.bytesLength = bytesLength;
+    }
+
+    public byte[] getBytes() {
+      return bytes;
+    }
+
+    public void setBytes(byte[] bytes) {
+      this.bytes = bytes;
+    }
+  }
+
+  public void readHeader(Header header, RandomAccessFile RAF) throws IOException {
+    header.nextPostingOffset = RAF.readInt();
+    header.bytesLeft = RAF.readInt();
+  }
+
+  public void writeHeader(Header header, RandomAccessFile RAF) throws IOException {
+    RAF.writeInt(header.nextPostingOffset);
+    RAF.writeInt(header.bytesLeft);
+  }
+
+  public void readPosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    posting.flag = RAF.readByte();
+    posting.nextKeyPostingPartition = RAF.readInt();
+    posting.nextKeyPostingPartitionOffset = RAF.readInt();
+    posting.keyHashCode = RAF.readLong();
+    posting.valuePostingPartition = RAF.readInt();
+    posting.valuePostingPartitionOffset = RAF.readInt();
+    posting.bytesLength = RAF.readInt();
+    if (posting.bytesLength > 0) {
+      if (posting.bytes == null || posting.bytes.length != posting.bytesLength) {
+        posting.bytes = new byte[posting.bytesLength];
+      }
+      int read = RAF.read(posting.bytes, 0, posting.bytesLength);
+      if (read != posting.bytesLength) {
+        throw new IOException("Unexcpected EOF");
+      }
+    }
+  }
+
+  public void writePosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    RAF.writeByte(posting.flag);
+    RAF.writeInt(posting.nextKeyPostingPartition);
+    RAF.writeInt(posting.nextKeyPostingPartitionOffset);
+    RAF.writeLong(posting.keyHashCode);
+    RAF.writeInt(posting.valuePostingPartition);
+    RAF.writeInt(posting.valuePostingPartitionOffset);
+    RAF.writeInt(posting.bytesLength);
+    if (posting.bytesLength > 0) {
+      RAF.write(posting.bytes, 0, posting.bytesLength);
+    }
+  }
+
+  public void deletePosting(int startOffset, RandomAccessFile RAF) throws IOException {
+    RAF.seek(startOffset);
+    RAF.writeByte((byte)2);
+  }
+  
+
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Log.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Log.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Log.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Log.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,144 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+import java.io.PrintWriter;
+
+/**
+ * BananaDB has very little debug output.
+ * This class avoids dependencies.
+ *
+ * @author kalle
+ * @since 2009-mar-16 23:32:14
+ */
+public class Log {
+
+  private static enum Level {
+    DEBUG(0), INFO(1), WARNING(2), ERROR(3);
+
+    private int intValue;
+
+    private Level(int intValue) {
+      this.intValue = intValue;
+    }
+
+    public int getIntValue() {
+      return intValue;
+    }
+  }
+
+  private Level level;
+
+  private String name;
+
+  public Log(String name) {
+    this(name, Level.INFO);
+  }
+
+  public Log(Class owner) {
+    this(owner.getName());
+  }
+
+  public Log(Class owner, Level level) {
+    this(owner.getName(), level);
+  }
+
+  public Log(String name, Level level) {
+    this.name = name;
+    this.level = level;
+  }
+
+  public boolean isDebug() {
+    return level.intValue <= Level.DEBUG.intValue;
+  }
+
+  public void debug(String text) {
+    if (isDebug()) {
+      log("DEBUG", text);
+    }
+  }
+
+  public boolean isInfo() {
+    return level.intValue <= Level.INFO.intValue;
+  }
+
+  public void info(String text) {
+    if (isInfo()) {
+      log("INFO", text);
+    }
+  }
+
+  public boolean isWarn() {
+    return level.intValue <= Level.WARNING.intValue;
+  }
+
+  public void warn(String text) {
+    if (isWarn()) {
+      log("WARNING", text);
+    }
+  }
+
+  public void warn(String text, Throwable throwable) {
+    if (isWarn()) {
+      log("WARNING", text, throwable);
+    }
+  }
+
+  public boolean isError() {
+    return level.intValue <= Level.ERROR.intValue;
+  }
+
+  public void error(Throwable throwable) {
+    if (isError()) {
+      log("ERROR", "", throwable);
+    }
+  }
+
+  public void error(String text, Throwable throwable) {
+    if (isError()) {
+      log("ERROR", text, throwable);
+    }
+  }
+
+  private void log(String type, String text) {
+    log(type, text, null);
+  }
+
+  private void log(String type, String text, Throwable throwable) {
+    out.print(String.valueOf(System.currentTimeMillis()));
+    out.print(" ");
+    out.print(name);
+    out.print(" ");
+    out.print(type);
+    out.print(": ");
+    out.print(text);
+    out.print("\n");
+    if (throwable != null) {
+      throwable.printStackTrace(out);
+    }
+    out.flush();
+  }
+
+  private PrintWriter out = new PrintWriter(System.err);
+
+  public void setOut(PrintWriter out) {
+    this.out = out;
+  }
+
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Metadata.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Metadata.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Metadata.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Metadata.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,172 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * @author kalle
+ * @since 2009-mar-16 14:16:39
+ */
+public class Metadata extends FileHandler<Metadata.Header, FileHandler.Posting> {
+
+  public Metadata(File directory, String access) throws IOException {
+    super(directory, 0, "md", access);
+  }
+
+  public static final int HEADER_BYTE_SIZE = 1024;
+  public int getHeaderByteSize() {
+    return HEADER_BYTE_SIZE;
+  }
+
+  public static class Header extends FileHandler.Header {
+    /**
+     * File format version.
+     */
+    private int fileFormatVersion;
+
+    /**
+     * Current hashtable file id. -- will change after rehash.
+     */
+    private int currentHashtableId;
+
+    /** Current hash codes partition used for appending new postings */
+    private int currentHashCodesPartition;
+
+    /** Current keys partition used for appending new postings */
+    private int currentKeysPartition;
+
+    /** Current values partition used for appending new postings */
+    private int currentValuesPartition;
+
+    /**
+     * Total number of value postings.
+     */
+    private long valuePostingsCount;
+
+    /**
+     * Commit version, will increase by one after each modification to the database.
+     */
+    private long commitVersion;
+
+    public int getCurrentHashCodesPartition() {
+      return currentHashCodesPartition;
+    }
+
+    public void setCurrentHashCodesPartition(int currentHashCodesPartition) {
+      this.currentHashCodesPartition = currentHashCodesPartition;
+    }
+
+    public int getCurrentKeysPartition() {
+      return currentKeysPartition;
+    }
+
+    public void setCurrentKeysPartition(int currentKeysPartition) {
+      this.currentKeysPartition = currentKeysPartition;
+    }
+
+    public int getCurrentValuesPartition() {
+      return currentValuesPartition;
+    }
+
+    public void setCurrentValuesPartition(int currentValuesPartition) {
+      this.currentValuesPartition = currentValuesPartition;
+    }
+
+    public int getFileFormatVersion() {
+      return fileFormatVersion;
+    }
+
+    public void setFileFormatVersion(int fileFormatVersion) {
+      this.fileFormatVersion = fileFormatVersion;
+    }
+
+    public int getCurrentHashtableId() {
+      return currentHashtableId;
+    }
+
+    public void setCurrentHashtableId(int currentHashtableId) {
+      this.currentHashtableId = currentHashtableId;
+    }
+
+    public long getValuePostingsCount() {
+      return valuePostingsCount;
+    }
+
+    public void setValuePostingsCount(long valuePostingsCount) {
+      this.valuePostingsCount = valuePostingsCount;
+    }
+
+    public long getCommitVersion() {
+      return commitVersion;
+    }
+
+    public void setCommitVersion(long commitVersion) {
+      this.commitVersion = commitVersion;
+    }
+
+    public long increaseCommitVersion(long value) {
+      return commitVersion += value;
+    }
+
+    public long increaseValuePostingsCount(long value) {
+      return valuePostingsCount += value;
+    }
+
+    public long decreaseValuePostingsCount(long value) {
+      return valuePostingsCount -= value;
+    }
+
+  }
+
+  public void readHeader(Header header, RandomAccessFile RAF) throws IOException {
+    header.fileFormatVersion = RAF.readInt();
+    header.commitVersion = RAF.readLong();
+    header.currentHashtableId = RAF.readInt();
+    header.currentHashCodesPartition = RAF.readInt();
+    header.currentKeysPartition = RAF.readInt();
+    header.currentValuesPartition = RAF.readInt();
+    header.valuePostingsCount = RAF.readLong();
+
+  }
+
+  public void writeHeader(Header header, RandomAccessFile RAF) throws IOException {
+    RAF.writeInt(header.fileFormatVersion);
+    RAF.writeLong(header.commitVersion);
+    RAF.writeInt(header.currentHashtableId);
+    RAF.writeInt(header.currentHashCodesPartition);
+    RAF.writeInt(header.currentKeysPartition);
+    RAF.writeInt(header.currentValuesPartition);
+    RAF.writeLong(header.valuePostingsCount);
+  }
+
+  public void writePosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void readPosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void deletePosting(int startOffset, RandomAccessFile RAF) throws IOException {
+    throw new UnsupportedOperationException();
+  }
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Store.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Store.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Store.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/Store.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,687 @@
+package org.apache.labs.bananadb.store;
+
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import org.apache.labs.bananadb.store.lock.Lock;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * @author kalle
+ * @since 2009-mar-16 15:34:56
+ */
+public class Store {
+
+  private static final Log log = new Log(Store.class);
+
+  private Configuration configuration;
+
+  public Store(Configuration configuration) {
+    this.configuration = configuration;
+  }
+
+
+  public void open() throws IOException {
+    if (!getConfiguration().getDataPath().exists()) {
+      if (log.isInfo()) {
+        log.info("Creating directory " + getConfiguration().getDataPath().getAbsolutePath());
+      }
+      if (!getConfiguration().getDataPath().mkdirs()) {
+        throw new IOException("Could not create directory " + getConfiguration().getDataPath().getAbsolutePath());
+      }
+    }
+  }
+
+
+  private List<Accessor> accessors = new ArrayList<Accessor>();
+
+  List<Accessor> getAccessors() {
+    return accessors;
+  }
+
+  public Accessor createAccessor(boolean readOnly) throws IOException {
+    Accessor accessor = new Accessor(this, readOnly);
+    accessors.add(accessor);
+    return accessor;
+  }
+
+  public void close() throws IOException {
+    log.info("Closing store..");
+    if (accessors.size() > 0) {
+      log.warn("There are " + accessors.size() + " open accessors. They will be closed.");
+    }
+    for (Accessor accessor : new ArrayList<Accessor>(accessors)) {
+      accessor.close();
+    }
+    log.info("Store has been closed.");
+  }
+
+  private void validateKey(byte[] key) {
+    if (key == null || key.length == 0) {
+      throw new IllegalArgumentException("Null key is not allowed");
+    }
+  }
+
+
+  public byte[] get(Accessor accessor, byte[] key, long hashCode) throws IOException {
+
+    validateKey(key);
+
+    Hashtable hashtable = accessor.getHashtable();
+    Hashtable.Posting hashtablePosting = new Hashtable.Posting();
+    hashtable.readPosting(hashtablePosting, hashtable.calculateHashCodePostingOffset(hashCode));
+
+    //
+    // seek to the correct hash code posting
+    //
+    HashCodesPartition hashCodesPartition = accessor.getHashCodesPartition(hashtablePosting.getHashCodePostingPartition());
+    HashCodesPartition.Posting hashCodePosting = new HashCodesPartition.Posting();
+    hashCodesPartition.readPosting(hashCodePosting, hashtablePosting.getHashCodePostingPartitionOffset());
+    if (hashCodePosting.getFlag() != (byte) 1) {
+      return null;
+    }
+    while (hashCode != hashCodePosting.getKeyHashCode()) {
+      if (hashCodePosting.getNextPostingPartition() < 0) {
+        return null;
+      }
+      if (hashCodePosting.getNextPostingPartition() != hashCodesPartition.getPartitionId()) {
+        hashCodesPartition = accessor.getHashCodesPartition(hashCodePosting.getNextPostingPartition());
+      }
+      hashCodesPartition.readPosting(hashCodePosting, hashCodePosting.getNextPostingPartitionOffset());
+    }
+
+    //
+    // seek to the correct key posting
+    //
+
+    KeysPartition.Posting keyPosting = new KeysPartition.Posting();
+
+    KeysPartition keysPartition = accessor.getKeysPartition(hashCodePosting.getFirstKeyPostingPartition());
+    keysPartition.readPosting(keyPosting, hashCodePosting.getFirstKeyPostingPartitionOffset());
+    while (!Arrays.equals(key, keyPosting.getBytes())) {
+      if (keyPosting.getNextKeyPostingPartition() < 0) {
+        return null;
+      }
+      if (keyPosting.getNextKeyPostingPartition() != keysPartition.getPartitionId()) {
+        keysPartition = accessor.getKeysPartition(keyPosting.getNextKeyPostingPartition());
+      }
+      keysPartition.readPosting(keyPosting, keyPosting.getNextKeyPostingPartitionOffset());
+    }
+
+    //
+    // seek to the correct value posting
+    //
+
+    ValuesPartition.Posting valuePosting = new ValuesPartition.Posting();
+
+    ValuesPartition valuesPartition = accessor.getValuesPartition(keyPosting.getValuePostingPartition());
+    valuesPartition.readPosting(valuePosting, keyPosting.getValuePostingPartitionOffset());
+
+    if (valuePosting.getBytesLength() == 0) {
+      return null;
+    }
+
+    return valuePosting.getBytes();
+  }
+
+  /**
+   * Write locking.
+   *
+   * @param accessor
+   * @param key
+   * @param hashCode
+   * @param value
+   * @return
+   * @throws IOException
+   */
+  public byte[] put(final Accessor accessor, final byte[] key, final long hashCode, final byte[] value) throws IOException {
+
+    validateKey(key);
+
+    Lock.With<byte[]> with = new Lock.With<byte[]>(accessor.getWriteLock(), getConfiguration().getLockWaitTimeoutMilliseconds()) {
+      protected byte[] doBody() throws IOException {
+        byte[] ret = doPut(accessor, key, hashCode, value);
+        Metadata metadata = accessor.getMetadata();
+        Metadata.Header mdh = new Metadata.Header();
+        metadata.readHeader(mdh);
+        mdh.setCommitVersion(mdh.getCommitVersion() + 1);
+        metadata.writeHeader(mdh);
+        return ret;
+      }
+    };
+    return with.run();
+  }
+
+  /**
+   * Write locking.
+   *
+   * @param accessor
+   * @param key
+   * @param hashCode
+   * @param value
+   * @return
+   * @throws IOException
+   */
+  private byte[] doPut(final Accessor accessor, final byte[] key, final long hashCode, final byte[] value) throws IOException {
+
+
+    //
+    // create new value posting
+    //
+
+    int newValuePostingPartition;
+    int newValuePostingPartitionOffset;
+
+    ValuesPartition.Posting valuePosting = new ValuesPartition.Posting();
+
+    if (value == null || value.length == 0) {
+      valuePosting.setBytesLength(0);
+      valuePosting.setBytes(null);
+
+      newValuePostingPartition = -1;
+      newValuePostingPartitionOffset = -1;
+
+    } else {
+      valuePosting.setBytesLength(value.length);
+      valuePosting.setBytes(value);
+
+      Accessor.RequestPartitionWriterResponse<ValuesPartition> valueReservation = accessor.requestValueWrite(valuePosting);
+      newValuePostingPartition = valueReservation.getFileHandler().getPartitionId();
+      ValuesPartition valuesPartition = valueReservation.getFileHandler();
+      newValuePostingPartitionOffset = valueReservation.getStartOffset();
+
+      // write new value posting, keep track of partition and offset
+      valuesPartition.writePosting(valuePosting, newValuePostingPartitionOffset);
+
+    }
+
+
+    //
+    // create new key posting
+    //
+
+    KeysPartition.Posting newKeyPosting = new KeysPartition.Posting();
+    newKeyPosting.setFlag((byte) 1);
+    newKeyPosting.setBytes(key);
+    newKeyPosting.setBytesLength(key.length);
+    newKeyPosting.setKeyHashCode(hashCode);
+    newKeyPosting.setNextKeyPostingPartition(-1);
+    newKeyPosting.setNextKeyPostingPartitionOffset(-1);
+    newKeyPosting.setValuePostingPartition(newValuePostingPartition);
+    newKeyPosting.setValuePostingPartitionOffset(newValuePostingPartitionOffset);
+
+    Accessor.RequestPartitionWriterResponse<KeysPartition> keyReservation = accessor.requestValueWrite(newKeyPosting);
+    int newKeyPostingPartition = keyReservation.getFileHandler().getPartitionId();
+    KeysPartition keysPartition = keyReservation.getFileHandler();
+    int newKeyPostingPartitionOffset = keyReservation.getStartOffset();
+
+    keysPartition.writePosting(newKeyPosting, newKeyPostingPartitionOffset);
+
+
+    //
+    // find hashcode posting and hashtable posting for the new key
+    //
+
+    Hashtable hashtable = accessor.getHashtable();
+    Hashtable.Posting hashtablePosting = new Hashtable.Posting();
+    HashCodesPartition.Posting hashCodePosting = new HashCodesPartition.Posting();
+
+    int hashtablePostingOffset = hashtable.calculateHashCodePostingOffset(hashCode);
+    hashtable.readPosting(hashtablePosting, hashtablePostingOffset);
+    if (hashtablePosting.getFlag() != (byte) 1) {
+
+      // this is the first time we create a posting at this hashtable position
+      // that means there is no hash code posting either
+
+      hashCodePosting.setFlag((byte) 1);
+      hashCodePosting.setFirstKeyPostingPartition(newKeyPostingPartition);
+      hashCodePosting.setFirstKeyPostingPartitionOffset(newKeyPostingPartitionOffset);
+      hashCodePosting.setKeyHashCode(hashCode);
+      hashCodePosting.setNextPostingPartition(-1);
+      hashCodePosting.setNextPostingPartitionOffset(-1);
+
+      Accessor.RequestPartitionWriterResponse<HashCodesPartition> hashCodeReservation = accessor.requestValueWrite(hashCodePosting);
+      int newHashCodePostingPatition = hashCodeReservation.getFileHandler().getPartitionId();
+      HashCodesPartition hashCodesPartition = hashCodeReservation.getFileHandler();
+      int newHashCodePostingPatitionOffset = hashCodeReservation.getStartOffset();
+
+      hashCodesPartition.writePosting(hashCodePosting, newHashCodePostingPatitionOffset);
+
+      // update hashtable posting
+
+      hashtablePosting.setFlag((byte) 1);
+      hashtablePosting.setHashCodePostingPartition(newHashCodePostingPatition);
+      hashtablePosting.setHashCodePostingPartitionOffset(newHashCodePostingPatitionOffset);
+      hashtable.writePosting(hashtablePosting, hashtablePostingOffset);
+
+      return null;
+
+    } else {
+
+      // there is a hashtable posting at the position for this hash code
+
+      //
+      // seek to the correct hash code posting
+      //
+
+      HashCodesPartition hashCodesPartition = accessor.getHashCodesPartition(hashtablePosting.getHashCodePostingPartition());
+      int currentHashCodesPotingPartitionOffset = hashtablePosting.getHashCodePostingPartitionOffset();
+      hashCodesPartition.readPosting(hashCodePosting, hashtablePosting.getHashCodePostingPartitionOffset());
+      while (hashCode != hashCodePosting.getKeyHashCode()) {
+        if (hashCodePosting.getNextPostingPartition() < 0) {
+
+          // there is no hash code posting matching this hash code.
+
+          //
+          //  create new hash code posting
+          //
+
+          HashCodesPartition.Posting newHashCodePosting = new HashCodesPartition.Posting();
+          newHashCodePosting.setFlag((byte) 1);
+          newHashCodePosting.setKeyHashCode(hashCode);
+          newHashCodePosting.setNextPostingPartition(-1);
+          newHashCodePosting.setNextPostingPartitionOffset(-1);
+          newHashCodePosting.setFirstKeyPostingPartition(newKeyPostingPartition);
+          newHashCodePosting.setFirstKeyPostingPartitionOffset(newKeyPostingPartitionOffset);
+
+          Accessor.RequestPartitionWriterResponse<HashCodesPartition> hashCodeReservation = accessor.requestValueWrite(newHashCodePosting);
+
+          int newHashCodePostingPartition = hashCodeReservation.getFileHandler().getPartitionId();
+          int newHashCodePostingPartitionOffset = hashCodeReservation.getStartOffset();
+
+          hashCodeReservation.getFileHandler().writePosting(newHashCodePosting, newHashCodePostingPartitionOffset);
+
+
+          //
+          // and update the current posting to point at the new one as next in chain.
+          //
+
+          hashCodePosting.setNextPostingPartition(newHashCodePostingPartition);
+          hashCodePosting.setNextPostingPartitionOffset(newHashCodePostingPartitionOffset);
+          hashCodesPartition.writePosting(hashCodePosting, currentHashCodesPotingPartitionOffset);
+
+          return null;
+
+        }
+        if (hashCodePosting.getNextPostingPartition() != hashCodesPartition.getPartitionId()) {
+          hashCodesPartition = accessor.getHashCodesPartition(hashCodePosting.getNextPostingPartition());
+        }
+        currentHashCodesPotingPartitionOffset = hashCodePosting.getNextPostingPartitionOffset();
+        hashCodesPartition.readPosting(hashCodePosting, hashCodePosting.getNextPostingPartitionOffset());
+      }
+
+      //
+      // seek for the same key
+      //
+
+      keysPartition = accessor.getKeysPartition(hashCodePosting.getFirstKeyPostingPartition());
+
+      KeysPartition.Posting keyPosting = new KeysPartition.Posting();
+
+      int previousKeyPostingPartition = -1;
+      int previousKeyPostingPartitionOffset = -1;
+
+      int currentKeyPostingPartition = keysPartition.getPartitionId();
+      int currentKeyPostingPartitionOffset = hashCodePosting.getFirstKeyPostingPartitionOffset();
+      keysPartition.readPosting(keyPosting, hashCodePosting.getFirstKeyPostingPartitionOffset());
+      while (!Arrays.equals(key, keyPosting.getBytes())) {
+        if (keyPosting.getNextKeyPostingPartition() < 0) {
+
+          // the key did not exist
+          // update the current key posting to point at the new key posting partition and offset as next in chain.
+          keyPosting.setNextKeyPostingPartition(newKeyPostingPartition);
+          keyPosting.setNextKeyPostingPartitionOffset(newKeyPostingPartitionOffset);
+          keysPartition.writePosting(keyPosting, currentKeyPostingPartitionOffset);
+
+          return null;
+        }
+        if (keyPosting.getNextKeyPostingPartition() != keysPartition.getPartitionId()) {
+          keysPartition = accessor.getKeysPartition(keyPosting.getNextKeyPostingPartition());
+        }
+
+        previousKeyPostingPartition = currentKeyPostingPartition;
+        previousKeyPostingPartitionOffset = currentKeyPostingPartitionOffset;
+
+        currentKeyPostingPartitionOffset = keyPosting.getNextKeyPostingPartitionOffset();
+        currentKeyPostingPartition = keysPartition.getPartitionId();
+
+        keysPartition.readPosting(keyPosting, keyPosting.getNextKeyPostingPartitionOffset());
+      }
+
+      //
+      // a posting exists for this key.
+      //
+
+
+      if (hashCodePosting.getFirstKeyPostingPartition() == currentKeyPostingPartition
+          && hashCodePosting.getFirstKeyPostingPartitionOffset() == currentKeyPostingPartitionOffset) {
+        // no previous key
+        // update the hash code posting to point at new key posting as the first in chain
+
+        hashCodePosting.setFirstKeyPostingPartition(newKeyPostingPartition);
+        hashCodePosting.setFirstKeyPostingPartitionOffset(newKeyPostingPartitionOffset);
+        hashCodesPartition.writePosting(hashCodePosting, currentHashCodesPotingPartitionOffset);
+      } else {
+
+        // mark old key as deleted
+        keysPartition.deletePosting(currentKeyPostingPartitionOffset);
+
+        // update previous key posting in chain to point chain at new key posting rather than the deleted
+        if (previousKeyPostingPartition != currentKeyPostingPartition) {
+          keysPartition = accessor.getKeysPartition(previousKeyPostingPartition);
+        }
+        keysPartition.readPosting(keyPosting, previousKeyPostingPartitionOffset);
+        keyPosting.setNextKeyPostingPartition(newKeyPostingPartition);
+        keyPosting.setNextKeyPostingPartitionOffset(newKeyPostingPartitionOffset);
+        keysPartition.writePosting(keyPosting, previousKeyPostingPartitionOffset);
+      }
+
+
+      // read the old value
+      ValuesPartition valuesPartition = accessor.getValuesPartition(keyPosting.getValuePostingPartition());
+      valuesPartition.readPosting(valuePosting, keyPosting.getValuePostingPartitionOffset());
+      byte[] oldValue;
+      if (valuePosting.getBytesLength() > 0) {
+        oldValue = valuePosting.getBytes();
+      } else {
+        oldValue = null;
+      }
+
+      // mark the old value as deleted
+      valuesPartition.deletePosting(keyPosting.getValuePostingPartitionOffset());
+
+      return oldValue;
+
+    }
+
+  }
+
+  /**
+   * Write locking.
+   *
+   * @param accessor
+   * @param key
+   * @param hashCode
+   * @return
+   * @throws IOException
+   */
+  public byte[] remove(final Accessor accessor, final byte[] key, final long hashCode) throws IOException {
+
+    validateKey(key);
+
+    Lock.With<byte[]> with = new Lock.With<byte[]>(accessor.getWriteLock(), getConfiguration().getLockWaitTimeoutMilliseconds()) {
+      protected byte[] doBody() throws IOException {
+
+        byte[] ret = doRemove(accessor, key, hashCode);
+
+        Metadata metadata = accessor.getMetadata();
+        Metadata.Header mdh = new Metadata.Header();
+        metadata.readHeader(mdh);
+        mdh.setCommitVersion(mdh.getCommitVersion() + 1);
+        metadata.writeHeader(mdh);
+
+        return ret;
+
+      }
+    };
+    return with.run();
+  }
+
+  /**
+   * Write locking.
+   *
+   * @param accessor
+   * @param key
+   * @param hashCode
+   * @return
+   * @throws IOException
+   */
+  private byte[] doRemove(final Accessor accessor, final byte[] key, final long hashCode) throws IOException {
+
+    Hashtable.Posting hashtablePosting = new Hashtable.Posting();
+
+    //
+    // find hashtable posting
+    //
+
+    Hashtable hashtable = accessor.getHashtable();
+    int hashtablePostingOffset = hashtable.calculateHashCodePostingOffset(hashCode);
+    accessor.getHashtable().readPosting(hashtablePosting, hashtablePostingOffset);
+    if (hashtablePosting.getHashCodePostingPartition() < 0) {
+      throw new NoSuchElementException(); // todo return null?
+    }
+
+    //
+    // seek to the correct hash code posting
+    //
+
+    HashCodesPartition.Posting hashCodePosting = new HashCodesPartition.Posting();
+
+    HashCodesPartition hashCodesPartition = accessor.getHashCodesPartition(hashtablePosting.getHashCodePostingPartition());
+    int currentHashCodePostingPartitionOffset = hashtablePosting.getHashCodePostingPartitionOffset();
+    int currentHashCodePostingPartition = hashtablePosting.getHashCodePostingPartition();
+    hashCodesPartition.readPosting(hashCodePosting, hashtablePosting.getHashCodePostingPartitionOffset());
+    while (hashCode != hashCodePosting.getKeyHashCode()) {
+      if (hashCodePosting.getNextPostingPartition() < 0) {
+        throw new NoSuchElementException(); // todo return null?
+      }
+
+      if (hashCodePosting.getNextPostingPartition() != hashCodesPartition.getPartitionId()) {
+        hashCodesPartition = accessor.getHashCodesPartition(hashCodePosting.getNextPostingPartition());
+      }
+      currentHashCodePostingPartitionOffset = hashCodePosting.getNextPostingPartitionOffset();
+      currentHashCodePostingPartition = hashCodePosting.getNextPostingPartition();
+      hashCodesPartition.readPosting(hashCodePosting, hashCodePosting.getNextPostingPartitionOffset());
+    }
+
+    //
+    // seek for the same key
+    //
+
+    KeysPartition.Posting keyPosting = new KeysPartition.Posting();
+
+    KeysPartition keysPartition = accessor.getKeysPartition(hashCodePosting.getFirstKeyPostingPartition());
+
+    int previousKeyPostingPartition = -1;
+    int previousKeyPostingPartitionOffset = -1;
+
+    int currentKeyPostingPartition = keysPartition.getPartitionId();
+    int currentKeyPostingPartitionOffset = hashCodePosting.getFirstKeyPostingPartitionOffset();
+    keysPartition.readPosting(keyPosting, hashCodePosting.getFirstKeyPostingPartitionOffset());
+    while (!Arrays.equals(key, keyPosting.getBytes())) {
+      if (keyPosting.getNextKeyPostingPartition() < 0) {
+        // the key did not exist
+        throw new NoSuchElementException(); // todo return null?
+      }
+      if (keyPosting.getNextKeyPostingPartition() != keysPartition.getPartitionId()) {
+        keysPartition = accessor.getKeysPartition(keyPosting.getNextKeyPostingPartition());
+      }
+
+      previousKeyPostingPartition = currentKeyPostingPartition;
+      previousKeyPostingPartitionOffset = currentKeyPostingPartitionOffset;
+
+      currentKeyPostingPartitionOffset = keyPosting.getNextKeyPostingPartitionOffset();
+      currentKeyPostingPartition = keysPartition.getPartitionId();
+
+      keysPartition.readPosting(keyPosting, keyPosting.getNextKeyPostingPartitionOffset());
+    }
+
+    //
+    // a posting exists for this key.
+    //
+
+
+    if (hashCodePosting.getFirstKeyPostingPartition() == currentKeyPostingPartition
+        && hashCodePosting.getFirstKeyPostingPartitionOffset() == currentKeyPostingPartitionOffset) {
+
+      //
+      // no previous key in chain
+      //
+
+      if (hashtablePosting.getHashCodePostingPartition() == currentHashCodePostingPartition
+          && hashtablePosting.getHashCodePostingPartitionOffset() == currentHashCodePostingPartitionOffset) {
+
+        //
+        // no previous hash code posting in chain
+        // i.e. there are now no postings in the hashtable that match the hash code of this key
+        // so delete the hashtable and hash code posting
+        //
+
+        hashCodesPartition.deletePosting(currentHashCodePostingPartitionOffset);
+
+        hashtable.deletePosting(hashtablePostingOffset);
+
+      }
+
+    } else {
+
+      //
+      // there is a previous key in the chain.
+      // update it to point at the next key in chain as defined by the current key
+      //
+
+      if (keysPartition.getPartitionId() != previousKeyPostingPartition) {
+        keysPartition = accessor.getKeysPartition(previousKeyPostingPartition);
+      }
+      KeysPartition.Posting previousKeyPosting = new KeysPartition.Posting();
+      keysPartition.readPosting(previousKeyPosting, previousKeyPostingPartitionOffset);
+      previousKeyPosting.setNextKeyPostingPartition(keyPosting.getNextKeyPostingPartition());
+      previousKeyPosting.setNextKeyPostingPartitionOffset(keyPosting.getNextKeyPostingPartitionOffset());
+      keysPartition.writePosting(previousKeyPosting, previousKeyPostingPartitionOffset);
+
+    }
+
+
+    // mark old key as deleted
+    keysPartition.deletePosting(currentKeyPostingPartitionOffset);
+
+
+    // read the old value
+    ValuesPartition.Posting valuePosting = new ValuesPartition.Posting();
+
+    ValuesPartition oldValuePartition = accessor.getValuesPartition(keyPosting.getValuePostingPartition());
+    oldValuePartition.readPosting(valuePosting, keyPosting.getValuePostingPartitionOffset());
+    byte[] oldValue;
+    if (valuePosting.getBytesLength() > 0) {
+      oldValue = valuePosting.getBytes();
+    } else {
+      oldValue = null;
+    }
+
+    // mark the old value as deleted
+    oldValuePartition.deletePosting(keyPosting.getValuePostingPartitionOffset());
+
+    return oldValue;
+
+  }
+
+  public boolean containsKey(Accessor accessor, byte[] key, long hashCode) throws IOException {
+
+    validateKey(key);
+
+    Hashtable hashtable = accessor.getHashtable();
+    Hashtable.Posting hashtablePosting = new Hashtable.Posting();
+    hashtable.readPosting(hashtablePosting, hashtable.calculateHashCodePostingOffset(hashCode));
+
+    //
+    // seek to the correct hash code posting
+    //
+
+    HashCodesPartition.Posting hashCodePosting = new HashCodesPartition.Posting();
+
+    HashCodesPartition hashCodesPartition = accessor.getHashCodesPartition(hashtablePosting.getHashCodePostingPartition());
+    hashCodesPartition.readPosting(hashCodePosting, hashtablePosting.getHashCodePostingPartitionOffset());
+    if (hashCodePosting.getFlag() != (byte) 1) {
+      return false;
+    }
+    while (hashCode != hashCodePosting.getKeyHashCode()) {
+      if (hashCodePosting.getNextPostingPartition() < 0) {
+        return false;
+      }
+      if (hashCodePosting.getNextPostingPartition() != hashCodesPartition.getPartitionId()) {
+        hashCodesPartition = accessor.getHashCodesPartition(hashCodePosting.getNextPostingPartition());
+      }
+      hashCodesPartition.readPosting(hashCodePosting, hashCodePosting.getNextPostingPartitionOffset());
+    }
+
+    //
+    // seek to the correct key posting
+    //
+
+    KeysPartition.Posting keyPosting = new KeysPartition.Posting();
+
+    KeysPartition keysPartition = accessor.getKeysPartition(hashCodePosting.getFirstKeyPostingPartition());
+    keysPartition.readPosting(keyPosting, hashCodePosting.getFirstKeyPostingPartitionOffset());
+    while (!Arrays.equals(key, keyPosting.getBytes())) {
+      if (keyPosting.getNextKeyPostingPartition() < 0) {
+        return false;
+      }
+      if (keyPosting.getNextKeyPostingPartition() != keysPartition.getPartitionId()) {
+        keysPartition = accessor.getKeysPartition(keyPosting.getNextKeyPostingPartition());
+      }
+      keysPartition.readPosting(keyPosting, keyPosting.getNextKeyPostingPartitionOffset());
+    }
+
+    return true;
+
+  }
+
+  public Cursor values() {
+
+    // todo iterate all value partitions
+    throw new UnsupportedOperationException();
+
+  }
+
+  public Cursor keys() {
+
+    // todo iterate all key partitions
+    throw new UnsupportedOperationException();
+
+  }
+
+  /**
+   * Removes all deleted postings from the store
+   * Write locking.
+   */
+  public void optimize() {
+    throw new UnsupportedOperationException();
+
+  }
+
+  /**
+   * Rehashes the hash table.
+   * Write locking.
+   *
+   * @param resolution number of postings in new hashtable file
+   */
+  public void rehash(int resolution) {
+    throw new UnsupportedOperationException();
+  }
+
+  public Configuration getConfiguration() {
+    return configuration;
+  }
+
+
+}

Added: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/ValuesPartition.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/ValuesPartition.java?rev=755179&view=auto
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/ValuesPartition.java (added)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/ValuesPartition.java Tue Mar 17 10:14:03 2009
@@ -0,0 +1,174 @@
+package org.apache.labs.bananadb.store;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+
+import java.io.RandomAccessFile;
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * Values postings partition file.
+ * <p/>
+ * This file is NOT affected by rehashing.
+ *
+ * @author kalle
+ * @since 2009-mar-16 14:00:13
+ */
+public class ValuesPartition extends FileHandler<ValuesPartition.Header, ValuesPartition.Posting> {
+
+
+  private int partitionId;
+
+  public ValuesPartition(File directory, int partitionId, String access) throws IOException {
+    super(directory, partitionId, "v", access);
+    this.partitionId = partitionId;
+  }
+
+  public static final int HEADER_BYTE_SIZE = 1024;
+
+  public int getHeaderByteSize() {
+    return HEADER_BYTE_SIZE;
+  }
+
+  public int getPartitionId() {
+    return partitionId;
+  }
+
+
+  public static class Header extends FileHandler.Header {
+
+    /**
+     * Offset in this partition for next new posting.
+     */
+    private int nextPostingOffset;
+    /**
+     * Bytes left for use in this partition.
+     */
+    private int bytesLeft;
+
+    public void update(Posting posting) {
+      int postingByteSize = posting.getPostingByteSize();
+      nextPostingOffset += postingByteSize;
+      bytesLeft -= postingByteSize;
+    }
+
+    public int getNextPostingOffset() {
+      return nextPostingOffset;
+    }
+
+    public void setNextPostingOffset(int nextPostingOffset) {
+      this.nextPostingOffset = nextPostingOffset;
+    }
+
+    public int getBytesLeft() {
+      return bytesLeft;
+    }
+
+    public void setBytesLeft(int bytesLeft) {
+      this.bytesLeft = bytesLeft;
+    }
+  }
+
+  public static class Posting extends FileHandler.Posting {
+
+    /**
+     * 0 = never used
+     * 1 = in use
+     * 2 = deleted
+     */
+    private byte flag;
+
+
+    /**
+     * Length in bytes of serializaed value.
+     * 0 == null
+     */
+    private int bytesLength;
+
+    /**
+     * Serialized value.
+     */
+    private byte[] bytes;
+
+
+    public int getPostingByteSize() {
+      return 1 + 4 + bytesLength;
+    }
+
+    public byte getFlag() {
+      return flag;
+    }
+
+    public void setFlag(byte flag) {
+      this.flag = flag;
+    }
+
+    public int getBytesLength() {
+      return bytesLength;
+    }
+
+    public void setBytesLength(int valueBytesLength) {
+      this.bytesLength = valueBytesLength;
+    }
+
+    public byte[] getBytes() {
+      return bytes;
+    }
+
+    public void setBytes(byte[] valueBytes) {
+      this.bytes = valueBytes;
+    }
+  }
+
+  public void readHeader(Header header, RandomAccessFile RAF) throws IOException {
+    header.nextPostingOffset = RAF.readInt();
+    header.bytesLeft = RAF.readInt();
+  }
+
+  public void writeHeader(Header header, RandomAccessFile RAF) throws IOException {
+    RAF.writeInt(header.nextPostingOffset);
+    RAF.writeInt(header.bytesLeft);
+  }
+
+  public void readPosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    posting.flag = RAF.readByte();
+    posting.bytesLength = RAF.readInt();
+    if (posting.bytesLength > 0) {
+      posting.bytes = new byte[posting.bytesLength];
+      int read = RAF.read(posting.bytes, 0, posting.bytesLength);
+      if (read != posting.bytesLength) {
+        throw new IOException("Unexpected EOF");
+      }
+    }
+  }
+
+  public void writePosting(Posting posting, RandomAccessFile RAF) throws IOException {
+    RAF.writeByte(posting.flag);
+    RAF.writeInt(posting.bytesLength);
+    if (posting.bytesLength > 0) {
+      RAF.write(posting.bytes, 0, posting.bytesLength);
+    }
+  }
+
+  public void deletePosting(int startOffset, RandomAccessFile RAF) throws IOException {
+    RAF.seek(startOffset);
+    RAF.writeByte((byte) 2);
+  }
+
+}

Modified: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/Lock.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/Lock.java?rev=755179&r1=754666&r2=755179&view=diff
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/Lock.java (original)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/Lock.java Tue Mar 17 10:14:03 2009
@@ -1,4 +1,4 @@
-package org.apache.labs.bananadb.lock;
+package org.apache.labs.bananadb.store.lock;
 
 /**
  * Licensed to the Apache Software Foundation (ASF) under one or more

Modified: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockFactory.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockFactory.java?rev=755179&r1=754666&r2=755179&view=diff
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockFactory.java (original)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockFactory.java Tue Mar 17 10:14:03 2009
@@ -1,4 +1,4 @@
-package org.apache.labs.bananadb.lock;
+package org.apache.labs.bananadb.store.lock;
 
 /**
  * Licensed to the Apache Software Foundation (ASF) under one or more

Modified: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockObtainFailedException.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockObtainFailedException.java?rev=755179&r1=754666&r2=755179&view=diff
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockObtainFailedException.java (original)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockObtainFailedException.java Tue Mar 17 10:14:03 2009
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.labs.bananadb.lock;
+package org.apache.labs.bananadb.store.lock;
 
 import java.io.IOException;
 

Modified: labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockReleaseFailedException.java
URL: http://svn.apache.org/viewvc/labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockReleaseFailedException.java?rev=755179&r1=754666&r2=755179&view=diff
==============================================================================
--- labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockReleaseFailedException.java (original)
+++ labs/bananadb/trunk/src/main/java/org/apache/labs/bananadb/store/lock/LockReleaseFailedException.java Tue Mar 17 10:14:03 2009
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.labs.bananadb.lock;
+package org.apache.labs.bananadb.store.lock;
 
 import java.io.IOException;
 



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@labs.apache.org
For additional commands, e-mail: commits-help@labs.apache.org