/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2000-2006
* Oracle Corporation. All rights reserved.
*
* $Id: TupleOutput.java,v 12.4 2006/08/31 18:14:06 bostic Exp $
*/
package com.sleepycat.bind.tuple;
import com.sleepycat.util.FastOutputStream;
import com.sleepycat.util.PackedInteger;
import com.sleepycat.util.UtfOps;
/**
* An OutputStream with DataOutput-like methods for
* writing tuple fields. It is used by TupleBinding.
*
*
This class has many methods that have the same signatures as methods in * the {@link java.io.DataOutput} interface. The reason this class does not * implement {@link java.io.DataOutput} is because it would break the interface * contract for those methods because of data format differences.
* *Signed numbers are stored in the buffer in MSB (most significant byte * first) order with their sign bit (high-order bit) inverted to cause negative * numbers to be sorted first when comparing values as unsigned byte arrays, * as done in a database. Unsigned numbers, including characters, are stored * in MSB order with no change to their sign bit.
* *Strings and character arrays are stored either as a fixed length array of * unicode characters, where the length must be known by the application, or as * a null-terminated UTF byte array.
*Floats and doubles are stored using two different representations: sorted * representation and integer-bit (IEEE 754) representation. If you use * negative floating point numbers in a key, you should use sorted * representation; alternatively you may use integer-bit representation but you * will need to implement and configure a custom comparator to get correct * numeric ordering for negative numbers.
* *To use sorted representation use this set of methods:
*To use integer-bit representation use this set of methods:
*Float.floatToIntBits is used to convert the signed float
* value.
*
* Note: This method produces byte array values that by default * (without a custom comparator) do not sort correctly for * negative values. Only non-negative values are sorted correctly by * default. To sort all values correctly by default, use {@link * #writeSortedFloat}.
* * @param val is the value to write to the buffer. * * @return this tuple output object. */ public final TupleOutput writeFloat(float val) { writeUnsignedInt(Float.floatToIntBits(val)); return this; } /** * Writes an signed double (eight byte) value to the buffer. * Writes values that can be read using {@link TupleInput#readDouble}. *Double.doubleToLongBits is used to convert the signed
* double value.
*
* Note: This method produces byte array values that by default * (without a custom comparator) do not sort correctly for * negative values. Only non-negative values are sorted correctly by * default. To sort all values correctly by default, use {@link * #writeSortedDouble}.
* * @param val is the value to write to the buffer. * * @return this tuple output object. */ public final TupleOutput writeDouble(double val) { writeUnsignedLong(Double.doubleToLongBits(val)); return this; } /** * Writes a signed float (four byte) value to the buffer, with support for * correct default sorting of all values. * Writes values that can be read using {@link TupleInput#readSortedFloat}. * *Float.floatToIntBits and the following bit manipulations
* are used to convert the signed float value to a representation that is
* sorted correctly by default.
* int intVal = Float.floatToIntBits(val);
* intVal ^= (intVal < 0) ? 0xffffffff : 0x80000000;
*
*
* @param val is the value to write to the buffer.
*
* @return this tuple output object.
*/
public final TupleOutput writeSortedFloat(float val) {
int intVal = Float.floatToIntBits(val);
intVal ^= (intVal < 0) ? 0xffffffff : 0x80000000;
writeUnsignedInt(intVal);
return this;
}
/**
* Writes a signed double (eight byte) value to the buffer, with support
* for correct default sorting of all values.
* Writes values that can be read using {@link TupleInput#readSortedDouble}.
*
* Float.doubleToLongBits and the following bit
* manipulations are used to convert the signed double value to a
* representation that is sorted correctly by default.
* long longVal = Double.doubleToLongBits(val);
* longVal ^= (longVal < 0) ? 0xffffffffffffffffL : 0x8000000000000000L;
*
*
* @param val is the value to write to the buffer.
*
* @return this tuple output object.
*/
public final TupleOutput writeSortedDouble(double val) {
long longVal = Double.doubleToLongBits(val);
longVal ^= (longVal < 0) ? 0xffffffffffffffffL : 0x8000000000000000L;
writeUnsignedLong(longVal);
return this;
}
// --- end DataOutput compatible methods ---
/**
* Writes the specified bytes to the buffer, converting each character to
* an unsigned byte value.
* Writes values that can be read using {@link TupleInput#readBytes}.
* Only characters with values below 0x100 may be written using this
* method, since the high-order 8 bits of all characters are discarded.
*
* @param chars is the array of values to be written.
*
* @return this tuple output object.
*
* @throws NullPointerException if the chars parameter is null.
*/
public final TupleOutput writeBytes(char[] chars) {
for (int i = 0; i < chars.length; i++) {
writeFast((byte) chars[i]);
}
return this;
}
/**
* Writes the specified characters to the buffer, converting each character
* to a two byte unsigned value.
* Writes values that can be read using {@link TupleInput#readChars}.
*
* @param chars is the array of characters to be written.
*
* @return this tuple output object.
*
* @throws NullPointerException if the chars parameter is null.
*/
public final TupleOutput writeChars(char[] chars) {
for (int i = 0; i < chars.length; i++) {
writeFast((byte) (chars[i] >>> 8));
writeFast((byte) chars[i]);
}
return this;
}
/**
* Writes the specified characters to the buffer, converting each character
* to UTF format.
* Note that zero (0x0000) character values are encoded as non-zero values.
* Writes values that can be read using {@link TupleInput#readString(int)}
* or {@link TupleInput#readString(char[])}.
*
* @param chars is the array of characters to be written.
*
* @return this tuple output object.
*
* @throws NullPointerException if the chars parameter is null.
*/
public final TupleOutput writeString(char[] chars) {
if (chars.length == 0) return this;
int utfLength = UtfOps.getByteLength(chars);
makeSpace(utfLength);
UtfOps.charsToBytes(chars, 0, getBufferBytes(), getBufferLength(),
chars.length);
addSize(utfLength);
return this;
}
/**
* Writes an unsigned byte (one byte) value to the buffer.
* Writes values that can be read using {@link
* TupleInput#readUnsignedByte}.
*
* @param val is the value to write to the buffer.
*
* @return this tuple output object.
*/
public final TupleOutput writeUnsignedByte(int val) {
writeFast(val);
return this;
}
/**
* Writes an unsigned short (two byte) value to the buffer.
* Writes values that can be read using {@link
* TupleInput#readUnsignedShort}.
*
* @param val is the value to write to the buffer.
*
* @return this tuple output object.
*/
public final TupleOutput writeUnsignedShort(int val) {
writeFast((byte) (val >>> 8));
writeFast((byte) val);
return this;
}
/**
* Writes an unsigned int (four byte) value to the buffer.
* Writes values that can be read using {@link
* TupleInput#readUnsignedInt}.
*
* @param val is the value to write to the buffer.
*
* @return this tuple output object.
*/
public final TupleOutput writeUnsignedInt(long val) {
writeFast((byte) (val >>> 24));
writeFast((byte) (val >>> 16));
writeFast((byte) (val >>> 8));
writeFast((byte) val);
return this;
}
/**
* This method is private since an unsigned long cannot be treated as
* such in Java, nor converted to a BigInteger of the same value.
*/
private final TupleOutput writeUnsignedLong(long val) {
writeFast((byte) (val >>> 56));
writeFast((byte) (val >>> 48));
writeFast((byte) (val >>> 40));
writeFast((byte) (val >>> 32));
writeFast((byte) (val >>> 24));
writeFast((byte) (val >>> 16));
writeFast((byte) (val >>> 8));
writeFast((byte) val);
return this;
}
/**
* Writes a packed integer. Note that packed integers are not appropriate
* for sorted values (keys) unless a custom comparator is used.
*
* @see PackedInteger
*/
public void writePackedInt(int val) {
makeSpace(PackedInteger.MAX_LENGTH);
int oldLen = getBufferLength();
int newLen = PackedInteger.writeInt(getBufferBytes(), oldLen, val);
addSize(newLen - oldLen);
}
}