Package org.openrdf.sail.nativerdf.datastore

Source Code of org.openrdf.sail.nativerdf.datastore.DataFile$DataIterator

/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.sail.nativerdf.datastore;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.NoSuchElementException;

/**
* Class supplying access to a data file. A data file stores data sequentially.
* Each entry starts with the entry's length (4 bytes), followed by the data
* itself. File offsets are used to identify entries.
*
* @author Arjohn Kampman
*/
public class DataFile {

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

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

  /**
   * File format version, stored as the fourth byte in data files.
   */
  private static final byte FILE_FORMAT_VERSION = 1;

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

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

  private final File file;

  private final RandomAccessFile raf;

  private final FileChannel fileChannel;

  private final boolean forceSync;

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

  public DataFile(File file)
    throws IOException
  {
    this(file, false);
  }

  public DataFile(File file, boolean forceSync)
    throws IOException
  {
    this.file = file;
    this.forceSync = forceSync;

    if (!file.exists()) {
      boolean created = file.createNewFile();
      if (!created) {
        throw new IOException("Failed to create file: " + file);
      }
    }

    // Open a read/write channel to the file
    raf = new RandomAccessFile(file, "rw");
    fileChannel = raf.getChannel();

    if (fileChannel.size() == 0L) {
      // Empty file, write header
      ByteBuffer buf = ByteBuffer.allocate((int)HEADER_LENGTH);
      buf.put(MAGIC_NUMBER);
      buf.put(FILE_FORMAT_VERSION);
      buf.rewind();

      fileChannel.write(buf, 0L);

      sync();
    }
    else {
      // Verify file header
      ByteBuffer buf = ByteBuffer.allocate((int)HEADER_LENGTH);
      fileChannel.read(buf, 0L);
      buf.rewind();

      if (buf.remaining() < HEADER_LENGTH) {
        throw new IOException("File too short to be a compatible data file");
      }

      byte[] magicNumber = new byte[MAGIC_NUMBER.length];
      buf.get(magicNumber);
      byte version = buf.get();

      if (!Arrays.equals(MAGIC_NUMBER, magicNumber)) {
        throw new IOException("File doesn't contain compatible data records");
      }

      if (version > FILE_FORMAT_VERSION) {
        throw new IOException("Unable to read data file; it uses a newer file format");
      }
      else if (version != FILE_FORMAT_VERSION) {
        throw new IOException("Unable to read data file; invalid file format version: " + version);
      }
    }
  }

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

  public File getFile() {
    return file;
  }

  /**
   * Stores the specified data and returns the byte-offset at which it has been
   * stored.
   *
   * @param data
   *        The data to store, must not be <tt>null</tt>.
   * @return The byte-offset in the file at which the data was stored.
   */
  public long storeData(byte[] data)
    throws IOException
  {
    assert data != null : "data must not be null";

    long offset = fileChannel.size();

    ByteBuffer buf = ByteBuffer.allocate(data.length + 4);
    buf.putInt(data.length);
    buf.put(data);
    buf.rewind();

    fileChannel.write(buf, offset);

    return offset;
  }

  /**
   * Gets the data that is stored at the specified offset.
   *
   * @param offset
   *        An offset in the data file, must be larger than 0.
   * @return The data that was found on the specified offset.
   * @exception IOException
   *            If an I/O error occurred.
   */
  public byte[] getData(long offset)
    throws IOException
  {
    assert offset > 0 : "offset must be larger than 0, is: " + offset;

    // TODO: maybe get more data in one go is more efficient?
    ByteBuffer buf = ByteBuffer.allocate(4);
    fileChannel.read(buf, offset);
    int dataLength = buf.getInt(0);

    byte[] data = new byte[dataLength];
    buf = ByteBuffer.wrap(data);
    fileChannel.read(buf, offset + 4L);

    return data;
  }

  /**
   * Discards all stored data.
   *
   * @throws IOException
   *         If an I/O error occurred.
   */
  public void clear()
    throws IOException
  {
    fileChannel.truncate(HEADER_LENGTH);
  }

  /**
   * Syncs any unstored data to the hash file.
   */
  public void sync()
    throws IOException
  {
    if (forceSync) {
      fileChannel.force(false);
    }
  }

  /**
   * Closes the data file, releasing any file locks that it might have.
   *
   * @throws IOException
   */
  public void close()
    throws IOException
  {
    raf.close();
  }

  /**
   * Gets an iterator that can be used to iterate over all stored data.
   *
   * @return a DataIterator.
   */
  public DataIterator iterator() {
    return new DataIterator();
  }

  /**
   * An iterator that iterates over the data that is stored in a data file.
   */
  public class DataIterator {

    private long position = HEADER_LENGTH;

    public boolean hasNext()
      throws IOException
    {
      return position < fileChannel.size();
    }

    public byte[] next()
      throws IOException
    {
      if (!hasNext()) {
        throw new NoSuchElementException();
      }

      byte[] data = getData(position);
      position += (4 + data.length);
      return data;
    }
  }
}
TOP

Related Classes of org.openrdf.sail.nativerdf.datastore.DataFile$DataIterator

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.