📄 bytestorable.java
字号:
/*
* 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 + -