Package uk.ac.open.kmi.smartproducts.sesame.sail.btree

Source Code of uk.ac.open.kmi.smartproducts.sesame.sail.btree.AllocatedNodesList

/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 2008-2010.
*
* Licensed under the Aduna BSD-style license.
*/
package uk.ac.open.kmi.smartproducts.sesame.sail.btree;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.BitSet;

import info.aduna.io.ByteArrayUtil;
import info.aduna.io.NioFile;

/**
* List of allocated BTree nodes, persisted to a file on disk.
*
* @author Arjohn Kampman
*/
class AllocatedNodesList {

  /*-----------*
   * Constants *
   *-----------*/

  /**
   * Magic number "Allocated Nodes File" to detect whether the file is actually
   * an allocated nodes file. The first three bytes of the file should be equal
   * to this magic number.
   */
  private static final byte[] MAGIC_NUMBER = new byte[] { 'a', 'n', 'f' };

  /**
   * The file format version number, stored as the fourth byte in allocated
   * nodes files.
   */
  private static final byte FILE_FORMAT_VERSION = 1;

  private static final int HEADER_LENGTH = MAGIC_NUMBER.length + 1;

  /*-----------*
   * Variables *
   *-----------*/

  /**
   * The BTree associated with this allocated nodes list.
   */
  private final BTree btree;

  /**
   * The allocated nodes file.
   */
  private final NioFile nioFile;

  /**
   * Bit set recording which nodes have been allocated, using node IDs as
   * index.
   */
  private BitSet allocatedNodes;

  /**
   * Flag indicating whether the set of allocated nodes has changed and needs
   * to be written to file.
   */
  private boolean needsSync = false;

  /*--------------*
   * Constructors *
   *--------------*/

  /**
   * Creates a new AllocatedNodelist for the specified BTree.
   */
  public AllocatedNodesList(File allocNodesFile, BTree btree)
    throws IOException
  {
    if (allocNodesFile == null) {
      throw new IllegalArgumentException("allocNodesFile must not be null");
    }
    if (btree == null) {
      throw new IllegalArgumentException("btree muts not be null");
    }

    this.nioFile = new NioFile(allocNodesFile);
    this.btree = btree;
  }

  /*---------*
   * Methods *
   *---------*/

  /**
   * Gets the allocated nodes file.
   */
  public File getFile() {
    return nioFile.getFile();
  }

  public synchronized void close()
    throws IOException
  {
    close(true);
  }

  /**
   * Deletes the allocated nodes file.
   *
   * @return <tt>true</tt> if the file was deleted.
   */
  public synchronized boolean delete()
    throws IOException
  {
    close(false);
    return nioFile.delete();
  }

  public synchronized void close(boolean syncChanges)
    throws IOException
  {
    if (syncChanges) {
      sync();
    }
    allocatedNodes = null;
    needsSync = false;
    nioFile.close();
  }

  /**
   * Writes any changes that are cached in memory to disk.
   *
   * @throws IOException
   */
  public synchronized void sync()
    throws IOException
  {
    if (needsSync) {
      // Trim bit set
      BitSet bitSet = allocatedNodes;
      int bitSetLength = allocatedNodes.length();
      if (bitSetLength < allocatedNodes.size()) {
        bitSet = allocatedNodes.get(0, bitSetLength);
      }

      byte[] data = ByteArrayUtil.toByteArray(bitSet);

      // Write bit set to file
      nioFile.truncate(HEADER_LENGTH + data.length);
      nioFile.writeBytes(MAGIC_NUMBER, 0);
      nioFile.writeByte(FILE_FORMAT_VERSION, MAGIC_NUMBER.length);
      nioFile.writeBytes(data, HEADER_LENGTH);

      needsSync = false;
    }
  }

  private void scheduleSync()
    throws IOException
  {
    if (needsSync == false) {
      nioFile.truncate(0);
      needsSync = true;
    }
  }

  /**
   * Clears the allocated nodes list.
   *
   * @throws IOException
   *         If an I/O error occurred.
   */
  public synchronized void clear()
    throws IOException
  {
    if (allocatedNodes != null) {
      allocatedNodes.clear();
    }
    else {
      // bit set has not yet been initialized
      allocatedNodes = new BitSet();
    }

    scheduleSync();
  }

  public synchronized int allocateNode()
    throws IOException
  {
    initAllocatedNodes();

    int newNodeID = allocatedNodes.nextClearBit(1);
    allocatedNodes.set(newNodeID);

    scheduleSync();

    return newNodeID;
  }

  public synchronized void freeNode(int nodeID)
    throws IOException
  {
    initAllocatedNodes();
    allocatedNodes.clear(nodeID);
    scheduleSync();
  }

  /**
   * Returns the highest allocated node ID.
   */
  public synchronized int getMaxNodeID()
    throws IOException
  {
    initAllocatedNodes();
    return Math.max(0, allocatedNodes.length() - 1);
  }

  /**
   * Returns the number of allocated nodes.
   */
  public synchronized int getNodeCount()
    throws IOException
  {
    initAllocatedNodes();
    return allocatedNodes.cardinality();
  }

  private void initAllocatedNodes()
    throws IOException
  {
    if (allocatedNodes == null) {
      if (nioFile.size() > 0L) {
        loadAllocatedNodesInfo();
      }
      else {
        crawlAllocatedNodes();
      }
    }
  }

  private void loadAllocatedNodesInfo()
    throws IOException
  {
    byte[] data;

    if (nioFile.size() >= HEADER_LENGTH
        && Arrays.equals(MAGIC_NUMBER, nioFile.readBytes(0, MAGIC_NUMBER.length)))
    {
      byte version = nioFile.readByte(MAGIC_NUMBER.length);
      if (version > FILE_FORMAT_VERSION) {
        throw new IOException("Unable to read allocated nodes file; it uses a newer file format");
      }
      else if (version != FILE_FORMAT_VERSION) {
        throw new IOException("Unable to read allocated nodes file; invalid file format version: "
            + version);
      }

      data = nioFile.readBytes(HEADER_LENGTH, (int)(nioFile.size() - HEADER_LENGTH));
    }
    else {
      // assume header is missing (old file format)
      data = nioFile.readBytes(0, (int)nioFile.size());
      scheduleSync();
    }

    allocatedNodes = ByteArrayUtil.toBitSet(data);
  }

  private void crawlAllocatedNodes()
    throws IOException
  {
    allocatedNodes = new BitSet();

    BTree.Node rootNode = btree.readRootNode();
    if (rootNode != null) {
      crawlAllocatedNodes(rootNode);
    }

    scheduleSync();
  }

  private void crawlAllocatedNodes(BTree.Node node)
    throws IOException
  {
    try {
      allocatedNodes.set(node.getID());

      if (!node.isLeaf()) {
        for (int i = 0; i < node.getValueCount() + 1; i++) {
          crawlAllocatedNodes(node.getChildNode(i));
        }
      }

    }
    finally {
      node.release();
    }
  }
}
TOP

Related Classes of uk.ac.open.kmi.smartproducts.sesame.sail.btree.AllocatedNodesList

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.