⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bytestorable.java

📁 实现数据库的storage manager 功能
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 * Copyright (c) 2000-2004, Rickard C鰏ter, Martin Svensson
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer. 
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution. 
 * Neither the name of SICS nor the names of its contributors 
 * may be used to endorse or promote products derived from this software 
 * without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 */

package com.mellowtech.disc;

import java.nio.*;
import java.nio.channels.*;
import java.io.*;

/**
 * The disc API rely to a large extent of the ability for Objects to
 * represent themselves as bytes to ensure fast and compact transfer
 * to and from streams, files, and channels. Objects that wish to
 * access the functionality within the disc api needs to subclass the
 * ByteStorable.
 * <p>
 * If subclasses intend to use the methods that handle byte arrays and
 * streams they are strongly encouraged to overwrite these methods since
 * they (in the default implementation) only converts the byte arrays
 * to ByteBuffers and calls the appropriate methods.
 * </p>
 * <p>
 * <b></b> 
 * </p>
 * <p>
 * The ByteStorable should not be confused withe the Serializable interface
 * found in the java.io package. However, if serializabiliy are to be
 * used in any subclass of ByteStorable it is probably a good idea to
 * implement</p>
 *  <PRE>
 * private void writeObject(java.io.ObjectOutputStream out)
 *    throws IOException
 * private void readObject(java.io.ObjectInputStream in)
 *    throws IOException, ClassNotFoundException;
 * </PRE>
 * <p>
 * since the conversion to and from bytes is already implemented.</p>
 * @author Martin Svensson
 * @version 1.0
 */
public abstract class ByteStorable implements Comparable{

  /**
   * This default implementation just returns an exception. Subclasses
   * has to overwrite this method to be "comparable".
   * @param o an <code>Object</code> to compare with
   * @exception UnsupportedOperationException if an error occurs
   */
  public int compareTo(Object o) throws UnsupportedOperationException{
    throw new UnsupportedOperationException();
  }

  /**
   * Creates a new ByteStorable object from a buffer of bytes. Observe
   * that this method should create a fresh instance and not
   * fill the current instance with the bytes from the buffer.
   *
   * @param bb The current buffer.
   * @return a new ByteStorable object.
   */
  public abstract ByteStorable fromBytes(ByteBuffer bb);

  /**
   * Creates a new ByteStorable object from a buffer of bytes. The
   * default implementation converts the array into a ByteBuffer
   * and calls fromBytes, i.e<br>
   * <pre>
   *   ByteBuffer bb = ByteBuffer.wrap(b);
   *   bb.position(offset);
   *   return fromBytes(bb);
   * </pre></p>
   * <p>Subclasses are encouraged to overwrite this method if it is
   * heavily used.</p>
   * @param b[] a buffer to read from
   * @param offset an offset within the buffer
   * @return a new ByteStorable instance
   */
  public ByteStorable fromBytes(byte b[], int offset){
    ByteBuffer bb = ByteBuffer.wrap(b);
    bb.position(offset);
    return fromBytes(bb);
  }

  /**
   * Optional operation. Creates a new ByteStorable from an input stream. 
   * The reason this is not implemented is because it is difficult to 
   * find out how many bytes should be read (and read just those) without 
   * using "push back" functionality.
   * @param is stream to read from
   * @return a new ByteStorable instance
   * @exception UnsupportedOperationException if an error occurs
   */
  public ByteStorable fromBytes(InputStream is) throws UnsupportedOperationException{
    throw new UnsupportedOperationException();
  }

  /**
   * Optional operation. Creates a new ByteStorable from a channel. The reason
   * this is not implemented is because it is difficult to find out how many
   * bytes should be read (and read just those) without using "push back" 
   * functionality.
   * @param rbc channel to read from
   * @return a new ByteStorable instance
   * @exception UnsupportedOperationException if an error occurs
   */
  public ByteStorable fromBytes(ReadableByteChannel rbc) throws UnsupportedOperationException{
    throw new UnsupportedOperationException();
  }

  /**
   * Writes this ByteStorable to a ByteBuffer. Observe that calculating 
   * the byteSize from a buffer should only require a maximum of four 
   * bytes of a ByteStorable that has been transferrd to bytes.
   *
   * @param bb a buffer to store the byte representation
   * @see #byteSize(ByteBuffer)
   */
  public abstract void toBytes(ByteBuffer bb);

  /**
   * Writes this ByteStorable to a ByteBuffer. The ByteBuffer is 
   * allocated to byteSize() bytes.
   *
   * @return the byte buffer
   * @see #byteSize(ByteBuffer)
   */
  public ByteBuffer toBytes() {
    ByteBuffer bb = ByteBuffer.allocate(byteSize());
    toBytes(bb);
    return bb;
  }

  
  /**
   * Writes this ByteStorable to a ByteBuffer. If this method is to used 
   * frequently subclasses are encouraged to overwrite it since the default 
   * implementation only wraps the array in a ByteBuffer and calls 
   * toBytes(ByteBuffer).
   * <p>
   * <pre>
   *   ByteBuffer bb = ByteBuffer.wrap(b);
   *   bb.position(offset);
   *   toBytes(bb);
   *   return bb.position() - offset;
   * </pre>
   * </p>
   * @param b a buffer
   * @param offset offset where to start write
   * @return the number of bytes written
   * @see #toBytes(ByteBuffer)
   */
  public int toBytes(byte[] b, int offset){
    ByteBuffer bb = ByteBuffer.wrap(b);
    bb.position(offset);
    toBytes(bb);
    return bb.position() - offset;
  }

  /**
   * Writes this ByteStorable to an output stream. If this method is to 
   * used frequently subclasses are encouraged to overwrite it since the 
   * default implementation only wraps the array in a ByteBuffer and 
   * calls toBytes(ByteBuffer).
   * <p>
   * <pre>
   *   byte[] b = new byte[byteSize()];
   *   toBytes(b, 0);
   *   os.write(b);
   * </pre>
   * </p>
   * @param os an <code>OutputStream</code> value
   * @return the number of bytes written
   * @exception IOException if an error occurs
   * @see #toBytes(ByteBuffer)
   */
  public int toBytes(OutputStream os) throws IOException{
    int byteSize = byteSize();
    byte[] b = new byte[byteSize];
    toBytes(b, 0);
    os.write(b);
    return byteSize;
  }

  /**
   * Writes this ByteStorable to an output stream. If this method is 
   * to be used frequently subclasses are encouraged to overwrite it since 
   * the default implementation only wraps the array in a ByteBuffer and 
   * calls toBytes(ByteBuffer).
   * <p>
   * <pre>
   *   ByteBuffer bb = ByteBuffer.allocate(byteSize());
   *   toBytes(bb);
   *   bb.flip();
   *   wbc.write(bb);
   * </pre>
   * </p>
   * @param wbc a <code>WritableByteChannel</code> value
   * @return the number of bytes written
   * @exception IOException if an error occurs
   * @see #toBytes(ByteBuffer)
   */
  public int toBytes(WritableByteChannel wbc) throws IOException{
    int byteSize = byteSize();
    ByteBuffer bb = ByteBuffer.allocate(byteSize);
    toBytes(bb);
    bb.flip();
    wbc.write(bb);
    return byteSize;
  }

  /**
   * Calculates the current instance byte size, i.e the number of bytes it will occupy.
   * @return the number of bytes
   */
  public abstract int byteSize();
  
  /**
   * From a buffer calculate how many bytes the written ByteStorable
   * will occupy. No more than four bytes can be used for this.
   * @param bb a <code>ByteBuffer</code> value
   * @return the number of bytes
   */
  public abstract int byteSize(ByteBuffer bb);

  /**
   * Same as above. Implementors are cncourage to overwrite this
   * method if it is used frequently, since the default implementation
   * wraps the array into a ByteBuffer.
   * <p>
   * <pre>
   *  ByteBuffer bb = ByteBuffer.wrap(b);
   *   bb.position(offset);
   *   return byteSize(bb);
   * </pre>
   * </p>
   * @param b a <code>byte[]</code> value
   * @param offset an <code>int</code> value
   * @return an <code>int</code> value
   */
  public int byteSize(byte[] b, int offset){
    ByteBuffer bb = ByteBuffer.wrap(b);
    bb.position(offset);
    return byteSize(bb);
  }

  /**
   * Returns the smallest separator between two ByteStorables. See CBString for
   * an implementation. By default separate returns a copy of the larger value.
   * @param first a <code>ByteStorable</code> value
   * @param second a <code>ByteStorable</code> value
   * @return a <code>ByteStorable</code> value
   */
  public ByteStorable separate(ByteStorable first, ByteStorable second){
    ByteStorable toRet = first.compareTo(second) > 0 ? first : second;
    //now copy:
    ByteBuffer bb = ByteBuffer.allocate(toRet.byteSize());
    toRet.toBytes(bb);
    bb.flip();
    return toRet.fromBytes(bb);
  }

  /**
   * Starting at the current position copy a number
   * bytes to the beginning of the buffer and set the new postion to
   * just after the copied bytes. 
   * @param bb buffer
   * @param numBytes number of bytes to copy
   */
  public final static void copyToBeginning(ByteBuffer bb, int numBytes){
    if(numBytes == 0){
      bb.clear();
      return;
    }
    byte[] b = new byte[numBytes];
    bb.get(b);
    bb.clear();
    bb.put(b);
  }

  /**
   * Calculates the number of bytes that the next ByteStorable will
   * need to be fully read. If the buffer does not fully contain
   * the next ByteStorable it will return the number of bytes that
   * are left in this buffer as a negative value.
   *
   * @param bb a buffer
   * @param template a ByteStorable template to be used for calculating 
   * the size
   * @return the next ByteStorable's size or -(bytes left in buffer)
   */
  public final static int slackOrSize(ByteBuffer bb, ByteStorable template){
    int left = bb.remaining();
    if(bb.remaining() < 4)
      return -left;
    bb.mark();
    int bSize = template.byteSize(bb);
    bb.reset();
    if(bSize > left)
      return -left;
    return bSize;
  }
  

  /**
   * Calculate the number of bytes this num can be
   * encoded in. That is, small number will required less bytes
   * with variable encoding. Useful if you want to store a
   * size indicator.
   *
   * @param num the number to encode
   * @return the number of bytes needed to encode the number
   * @see #putSize
   * @see #getSize
   */
  public static final int sizeBytesNeeded(int num){
    int count = 1;
    num = (num >> 7);
    while(num > 0){
      count++;
      num = num >> 7;
    }
    return count;
  }

  /**
   * Encodes a number to a buffer. Uses less bytes for smaller numbers.
   * @param size the number to encode
   * @param bb a buffer to store the encoded value
   * @return the number of bytes written
   * @see #getSize
   */
  public static final int putSize(int size, ByteBuffer bb){
    int c, count = 1;
    c = (size & 0x7F);
    size = (size >> 7);
    while (size > 0) {
      bb.put((byte) (c & 0xFF));
      c = (size & 0x7F);
      size = size >> 7;
      count++;
    }
    c = (c | 0x80);
    bb.put((byte) (c & 0xFF));
    return count;
  }

  /**
   * Encodes a number to a buffer. Uses less bytes for smaller numbers.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -