/*
Derby - Class org.apache.derby.iapi.services.io.CompressedNumber
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.DataInput;
import java.io.DataOutput;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Static methods to write and read compressed forms of numbers to DataOut and
* DataIn interfaces. Format written is platform independent like the Data*
* interfaces and must remain fixed once a product is shipped. If a different
* format is required then write a new set of methods, e.g. writeInt2. The
* formats defined by stored format identifiers are implicitly dependent on
* these formats not changing.
*/
public abstract class CompressedNumber {
// the maximum number of bytes written out for an int
public static final int MAX_INT_STORED_SIZE = 4;
// the maximum number of bytes written out for a long
public static final int MAX_LONG_STORED_SIZE = 8;
// largest int stored compressed in 1 byte
public static final int MAX_COMPRESSED_INT_ONE_BYTE = 0x3f;
// largest int stored compressed in 2 bytes
public static final int MAX_COMPRESSED_INT_TWO_BYTES = 0x3fff;
/**
* Write a compressed integer only supporting signed values. Formats are (with
* x representing value bits):
*
*
*
* 1 Byte - 00xxxxxx Represents the value <= 63 (0x3f) 2 Byte - 01xxxxxx
* xxxxxxxx Represents the value > 63 && <= 16383 (0x3fff) 4 byte - 1xxxxxxx
* xxxxxxxx xxxxxxxx xxxxxxxx Represents the value > 16383 && <= MAX_INT
*
*
*
*
* @exception IOException
* value is negative or an exception was thrown by a method on
* out.
*/
public static final int writeInt(DataOutput out, int value) throws IOException {
if (value < 0)
throw new IOException();
if (value <= 0x3f) {
out.writeByte(value);
return 1;
}
if (value <= 0x3fff) {
out.writeByte(0x40 | (value >>> 8));
out.writeByte(value & 0xff);
return 2;
}
out.writeByte(((value >>> 24) | 0x80) & 0xff);
out.writeByte((value >>> 16) & 0xff);
out.writeByte((value >>> 8) & 0xff);
out.writeByte((value) & 0xff);
return 4;
}
/**
* Write a compressed integer directly to an OutputStream.
*
* @exception IOException
* an exception was thrown by a method on in.
*/
public static final int writeInt(OutputStream out, int value) throws IOException {
if (value < 0)
throw new IOException();
if (value <= 0x3f) {
out.write(value);
return 1;
}
if (value <= 0x3fff) {
out.write(0x40 | (value >>> 8));
out.write(value & 0xff);
return 2;
}
out.write(((value >>> 24) | 0x80) & 0xff);
out.write((value >>> 16) & 0xff);
out.write((value >>> 8) & 0xff);
out.write((value) & 0xff);
return 4;
}
/**
* Read an integer previously written by writeInt().
*
* @exception IOException
* an exception was thrown by a method on in.
*/
public static final int readInt(DataInput in) throws IOException {
int value = in.readUnsignedByte();
if ((value & ~0x3f) == 0) {
// length is stored in this byte, we also know that the 0x80 bit
// was not set, so no need to mask off the sign extension from
// the byte to int conversion.
// account for 1 byte stored length of field + 1 for all returns
return (value);
} else if ((value & 0x80) == 0) {
// length is stored in 2 bytes. only use low 6 bits from 1st byte.
// top 8 bits of 2 byte length is stored in this byte, we also
// know that the 0x80 bit was not set, so no need to mask off the
// sign extension from the 1st byte to int conversion. Need to
// mask the byte in data[offset + 1] to account for possible sign
// extension.
return (((value & 0x3f) << 8) | in.readUnsignedByte());
} else {
// top 8 bits of 4 byte length is stored in this byte, we also
// know that the 0x80 bit was set, so need to mask off the
// sign extension from the 1st byte to int conversion. Need to
// mask the bytes from the next 3 bytes data[offset + 1,2,3] to
// account for possible sign extension.
//
return (((value & 0x7f) << 24) | (in.readUnsignedByte() << 16) | (in.readUnsignedByte() << 8) | (in
.readUnsignedByte()));
}
}
/**
* Read an integer previously written by writeInt().
*
* @exception IOException
* an exception was thrown by a method on in.
*/
public static final int readInt(InputStream in) throws IOException {
int value = InputStreamUtil.readUnsignedByte(in);
if ((value & ~0x3f) == 0) {
return (value);
} else if ((value & 0x80) == 0) {
return (((value & 0x3f) << 8) | InputStreamUtil.readUnsignedByte(in));
} else {
return (((value & 0x7f) << 24) | (InputStreamUtil.readUnsignedByte(in) << 16)
| (InputStreamUtil.readUnsignedByte(in) << 8) | (InputStreamUtil.readUnsignedByte(in)));
}
}
public static final int readInt(byte[] data, int offset) {
int value = data[offset++];
if ((value & ~0x3f) == 0) {
// length is stored in this byte, we also know that the 0x80 bit
// was not set, so no need to mask off the sign extension from
// the byte to int conversion.
return (value);
} else if ((value & 0x80) == 0) {
// length is stored in 2 bytes. only use low 6 bits from 1st byte.
// top 8 bits of 2 byte length is stored in this byte, we also
// know that the 0x80 bit was not set, so no need to mask off the
// sign extension from the 1st byte to int conversion. Need to
// mask the byte in data[offset + 1] to account for possible sign
// extension.
return (((value & 0x3f) << 8) | (data[offset] & 0xff));
} else {
// top 8 bits of 4 byte length is stored in this byte, we also
// know that the 0x80 bit was set, so need to mask off the
// sign extension from the 1st byte to int conversion. Need to
// mask the bytes from the next 3 bytes data[offset + 1,2,3] to
// account for possible sign extension.
//
return (((value & 0x7f) << 24) | ((data[offset++] & 0xff) << 16)
| ((data[offset++] & 0xff) << 8) | ((data[offset] & 0xff)));
}
}
/**
* Skip an integer previously written by writeInt().
*
* @exception IOException
* an exception was thrown by a method on in.
*/
/**
* Return the number of bytes that would be written by a writeInt call
*/
public static final int sizeInt(int value) {
if (value <= 0x3f) {
return 1;
}
if (value <= 0x3fff) {
return 2;
}
return 4;
}
/**
* Write a compressed long only supporting signed values.
*
* Formats are (with x representing value bits):
*
*
*
* 2 byte - 00xxxxxx xxxxxxxx Represents the value <= 16383 (0x3fff) 4 byte -
* 01xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx Represents the value > 16383 && <=
* 0x3fffffff 8 byte - 1xxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
* xxxxxxxx xxxxxxxx Represents the value > 0x3fffffff && <= MAX_LONG
*
*
*
*
* @exception IOException
* value is negative or an exception was thrown by a method on
* out.
*/
public static final int writeLong(DataOutput out, long value) throws IOException {
if (value < 0)
throw new IOException();
if (value <= 0x3fff) {
out.writeByte((int) ((value >>> 8) & 0xff));
out.writeByte((int) ((value) & 0xff));
return 2;
}
if (value <= 0x3fffffff) {
out.writeByte((int) (((value >>> 24) | 0x40) & 0xff));
out.writeByte((int) ((value >>> 16) & 0xff));
out.writeByte((int) ((value >>> 8) & 0xff));
out.writeByte((int) ((value) & 0xff));
return 4;
}
out.writeByte((int) (((value >>> 56) | 0x80) & 0xff));
out.writeByte((int) ((value >>> 48) & 0xff));
out.writeByte((int) ((value >>> 40) & 0xff));
out.writeByte((int) ((value >>> 32) & 0xff));
out.writeByte((int) ((value >>> 24) & 0xff));
out.writeByte((int) ((value >>> 16) & 0xff));
out.writeByte((int) ((value >>> 8) & 0xff));
out.writeByte((int) ((value) & 0xff));
return 8;
}
/**
* Write a compressed integer only supporting signed values.
*
* @exception IOException
* value is negative or an exception was thrown by a method on
* out.
*/
public static final int writeLong(OutputStream out, long value) throws IOException {
if (value < 0)
throw new IOException();
if (value <= 0x3fff) {
out.write((int) ((value >>> 8) & 0xff));
out.write((int) ((value) & 0xff));
return 2;
}
if (value <= 0x3fffffff) {
out.write((int) (((value >>> 24) | 0x40) & 0xff));
out.write((int) ((value >>> 16) & 0xff));
out.write((int) ((value >>> 8) & 0xff));
out.write((int) ((value) & 0xff));
return 4;
}
out.write((int) (((value >>> 56) | 0x80) & 0xff));
out.write((int) ((value >>> 48) & 0xff));
out.write((int) ((value >>> 40) & 0xff));
out.write((int) ((value >>> 32) & 0xff));
out.write((int) ((value >>> 24) & 0xff));
out.write((int) ((value >>> 16) & 0xff));
out.write((int) ((value >>> 8) & 0xff));
out.write((int) ((value) & 0xff));
return 8;
}
/**
* Read a long previously written by writeLong().
*
* @exception IOException
* an exception was thrown by a method on in.
*/
public static final long readLong(DataInput in) throws IOException {
int int_value = in.readUnsignedByte();
if ((int_value & ~0x3f) == 0) {
// test for small case first - assuming this is usual case.
// this is stored in 2 bytes.
return ((int_value << 8) | in.readUnsignedByte());
} else if ((int_value & 0x80) == 0) {
// value is stored in 4 bytes. only use low 6 bits from 1st byte.
return (((int_value & 0x3f) << 24) | (in.readUnsignedByte() << 16)
| (in.readUnsignedByte() << 8) | (in.readUnsignedByte()));
} else {
// value is stored in 8 bytes. only use low 7 bits from 1st byte.
return ((((long) (int_value & 0x7f)) << 56) | (((long) in.readUnsignedByte()) << 48)
| (((long) in.readUnsignedByte()) << 40) | (((long) in.readUnsignedByte()) << 32)
| (((long) in.readUnsignedByte()) << 24) | (((long) in.readUnsignedByte()) << 16)
| (((long) in.readUnsignedByte()) << 8) | (((long) in.readUnsignedByte())));
}
}
/**
* Read a long previously written by writeLong().
*
* @exception IOException
* an exception was thrown by a method on in.
*/
public static final long readLong(InputStream in) throws IOException {
int int_value = InputStreamUtil.readUnsignedByte(in);
if ((int_value & ~0x3f) == 0) {
// test for small case first - assuming this is usual case.
// this is stored in 2 bytes.
return ((int_value << 8) | InputStreamUtil.readUnsignedByte(in));
} else if ((int_value & 0x80) == 0) {
// value is stored in 4 bytes. only use low 6 bits from 1st byte.
return (((int_value & 0x3f) << 24) | (InputStreamUtil.readUnsignedByte(in) << 16)
| (InputStreamUtil.readUnsignedByte(in) << 8) | (InputStreamUtil.readUnsignedByte(in)));
} else {
// value is stored in 8 bytes. only use low 7 bits from 1st byte.
long value = int_value;
return ((((long) (value & 0x7f)) << 56)
| (((long) InputStreamUtil.readUnsignedByte(in)) << 48)
| (((long) InputStreamUtil.readUnsignedByte(in)) << 40)
| (((long) InputStreamUtil.readUnsignedByte(in)) << 32)
| (((long) InputStreamUtil.readUnsignedByte(in)) << 24)
| (((long) InputStreamUtil.readUnsignedByte(in)) << 16)
| (((long) InputStreamUtil.readUnsignedByte(in)) << 8) | (((long) InputStreamUtil
.readUnsignedByte(in))));
}
}
public static final long readLong(byte[] data, int offset) {
int int_value = data[offset++];
if ((int_value & ~0x3f) == 0) {
// test for small case first - assuming this is usual case.
// this is stored in 2 bytes.
return ((int_value << 8) | (data[offset] & 0xff));
} else if ((int_value & 0x80) == 0) {
// value is stored in 4 bytes. only use low 6 bits from 1st byte.
return (((int_value & 0x3f) << 24) | ((data[offset++] & 0xff) << 16)
| ((data[offset++] & 0xff) << 8) | ((data[offset] & 0xff)));
} else {
// value is stored in 8 bytes. only use low 6 bits from 1st byte.
return ((((long) (int_value & 0x7f)) << 56) | (((long) (data[offset++] & 0xff)) << 48)
| (((long) (data[offset++] & 0xff)) << 40) | (((long) (data[offset++] & 0xff)) << 32)
| (((long) (data[offset++] & 0xff)) << 24) | (((long) (data[offset++] & 0xff)) << 16)
| (((long) (data[offset++] & 0xff)) << 8) | (((long) (data[offset] & 0xff))));
}
}
public static final int sizeLong(long value) {
if (value <= 0x3fff) {
return 2;
}
if (value <= 0x3fffffff) {
return 4;
}
return 8;
}
}
/*
*
* Derby - Class org.apache.derby.iapi.services.io.InputStreamUtil
*
* 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.
*
*/
/**
* Utility methods for InputStream that are stand-ins for a small subset of
* DataInput methods. This avoids pushing a DataInputStream just to get this
* functionality.
*/
final class InputStreamUtil {
private static final int SKIP_FRAGMENT_SIZE = Integer.MAX_VALUE;
/**
* Read an unsigned byte from an InputStream, throwing an EOFException if the
* end of the input is reached.
*
* @exception IOException
* if an I/O error occurs.
* @exception EOFException
* if the end of the stream is reached
*
* @see DataInput#readUnsignedByte
*
*/
public static int readUnsignedByte(InputStream in) throws IOException {
int b = in.read();
if (b < 0)
throw new EOFException();
return b;
}
/**
* Read a number of bytes into an array.
*
* @exception IOException
* if an I/O error occurs.
* @exception EOFException
* if the end of the stream is reached
*
* @see DataInput#readFully
*
*/
public static void readFully(InputStream in, byte b[], int offset, int len) throws IOException {
do {
int bytesRead = in.read(b, offset, len);
if (bytesRead < 0)
throw new EOFException();
len -= bytesRead;
offset += bytesRead;
} while (len != 0);
}
/**
* Read a number of bytes into an array. Keep reading in a loop until len
* bytes are read or EOF is reached or an exception is thrown. Return the
* number of bytes read. (InputStream.read(byte[],int,int) does not guarantee
* to read len bytes even if it can do so without reaching EOF or raising an
* exception.)
*
* @exception IOException
* if an I/O error occurs.
*/
public static int readLoop(InputStream in, byte b[], int offset, int len) throws IOException {
int firstOffset = offset;
do {
int bytesRead = in.read(b, offset, len);
if (bytesRead <= 0)
break;
len -= bytesRead;
offset += bytesRead;
} while (len != 0);
return offset - firstOffset;
}
/**
* Skips until EOF, returns number of bytes skipped.
*
* @param is
* InputStream to be skipped.
* @return number of bytes skipped in fact.
* @throws IOException
* if IOException occurs. It doesn't contain EOFException.
* @throws NullPointerException
* if the param 'is' equals null.
*/
public static long skipUntilEOF(InputStream is) throws IOException {
if (is == null)
throw new NullPointerException();
long bytes = 0;
while (true) {
long r = skipPersistent(is, SKIP_FRAGMENT_SIZE);
bytes += r;
if (r < SKIP_FRAGMENT_SIZE)
return bytes;
}
}
/**
* Skips requested number of bytes, throws EOFException if there is too few
* bytes in the stream.
*
* @param is
* InputStream to be skipped.
* @param skippedBytes
* number of bytes to skip. if skippedBytes <= zero, do nothing.
* @throws EOFException
* if EOF meets before requested number of bytes are skipped.
* @throws IOException
* if IOException occurs. It doesn't contain EOFException.
* @throws NullPointerException
* if the param 'is' equals null.
*/
public static void skipFully(InputStream is, long skippedBytes) throws IOException {
if (is == null)
throw new NullPointerException();
if (skippedBytes <= 0)
return;
long bytes = skipPersistent(is, skippedBytes);
if (bytes < skippedBytes)
throw new EOFException();
}
/**
* Tries harder to skip the requested number of bytes.
*
* Note that even if the method fails to skip the requested number of bytes,
* it will not throw an exception. If this happens, the caller can be sure
* that end-of-stream has been reached.
*
* @param in
* byte stream
* @param bytesToSkip
* the number of bytes to skip
* @return The number of bytes skipped.
* @throws IOException
* if reading from the stream fails
*/
public static final long skipPersistent(InputStream in, long bytesToSkip) throws IOException {
long skipped = 0;
while (skipped < bytesToSkip) {
long skippedNow = in.skip(bytesToSkip - skipped);
if (skippedNow == 0) {
if (in.read() == -1) {
// EOF, return what we have and leave it up to caller to
// decide what to do about it.
break;
} else {
skippedNow = 1; // Added to count below.
}
}
skipped += skippedNow;
}
return skipped;
}
}