package com.sleepycat.bind.tuple;
import com.sleepycat.util.FastOutputStream;
import com.sleepycat.util.UtfOps;
import de.ovgu.cide.jakutil.*;
/** 
 * An <code>OutputStream</code> with <code>DataOutput</code>-like methods
 * for writing tuple fields. It is used by <code>TupleBinding</code>.
 * <p>
 * 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.
 * </p>
 * <p>
 * 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.
 * </p>
 * <p>
 * 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.
 * </p>
 * <ul>
 * <li>Null strings are UTF encoded as { 0xFF }, which is not allowed in a
 * standard UTF encoding. This allows null strings, as distinct from empty or
 * zero length strings, to be represented in a tuple. Using the default
 * comparator, null strings will be ordered last.</li>
 * <li>Zero (0x0000) character values are UTF encoded as non-zero values, and
 * therefore embedded zeros in the string are supported. The sequence { 0xC0,
 * 0x80 } is used to encode a zero character. This UTF encoding is the same one
 * used by native Java UTF libraries. However, this encoding of zero does impact
 * the lexicographical ordering, and zeros will not be sorted first (the natural
 * order) or last. For all character values other than zero, the default UTF
 * byte ordering is the same as the Unicode lexicographical character ordering.</li>
 * </ul>
 * <p>
 * Floats and doubles are stored in standard Java integer-bit representation
 * (IEEE 754). Non-negative numbers are correctly ordered by numeric value.
 * However, negative numbers are not correctly ordered; therefore, if you use
 * negative floating point numbers in a key, you'll need to implement and
 * configure a custom comparator to get correct numeric ordering.
 * </p>
 * @author Mark Hayes
 */
public class TupleOutput extends FastOutputStream {
  /** 
 * We represent a null string as a single FF UTF character, which cannot
 * occur in a UTF encoded string.
 */
  static final int NULL_STRING_UTF_VALUE=((byte)0xFF);
  /** 
 * Creates a tuple output object for writing a byte array of tuple data.
 */
  public TupleOutput(){
    super();
  }
  /** 
 * Creates a tuple output object for writing a byte array of tuple data,
 * using a given buffer. A new buffer will be allocated only if the number
 * of bytes needed is greater than the length of this buffer. A reference to
 * the byte array will be kept by this object and therefore the byte array
 * should not be modified while this object is in use.
 * @param bufferis the byte array to use as the buffer.
 */
  public TupleOutput(  byte[] buffer){
    super(buffer);
  }
  /** 
 * 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 valis the string containing the values to be written.
 * @return this tuple output object.
 * @throws NullPointerExceptionif the val parameter is null.
 */
  public final TupleOutput writeBytes(  String val){
    writeBytes(val.toCharArray());
    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 valis the string containing the characters to be written.
 * @return this tuple output object.
 * @throws NullPointerExceptionif the val parameter is null.
 */
  public final TupleOutput writeChars(  String val){
    writeChars(val.toCharArray());
    return this;
  }
  /** 
 * Writes the specified characters to the buffer, converting each character
 * to UTF format, and adding a null terminator byte. Note that zero (0x0000)
 * character values are encoded as non-zero values and a null String
 * parameter is encoded as 0xFF. Writes values that can be read using{@link TupleInput#readString()}.
 * @param valis the string containing the characters to be written.
 * @return this tuple output object.
 */
  public final TupleOutput writeString(  String val){
    if (val != null) {
      writeString(val.toCharArray());
    }
 else {
      writeFast(NULL_STRING_UTF_VALUE);
    }
    writeFast(0);
    return this;
  }
  /** 
 * Writes a char (two byte) unsigned value to the buffer. Writes values that
 * can be read using {@link TupleInput#readChar}.
 * @param valis the value to write to the buffer.
 * @return this tuple output object.
 */
  public final TupleOutput writeChar(  int val){
    writeFast((byte)(val >>> 8));
    writeFast((byte)val);
    return this;
  }
  /** 
 * Writes a boolean (one byte) unsigned value to the buffer, writing one if
 * the value is true and zero if it is false. Writes values that can be read
 * using {@link TupleInput#readBoolean}.
 * @param valis the value to write to the buffer.
 * @return this tuple output object.
 */
  public final TupleOutput writeBoolean(  boolean val){
    writeFast(val ? (byte)1 : (byte)0);
    return this;
  }
  /** 
 * Writes an signed byte (one byte) value to the buffer. Writes values that
 * can be read using {@link TupleInput#readByte}.
 * @param valis the value to write to the buffer.
 * @return this tuple output object.
 */
  public final TupleOutput writeByte(  int val){
    writeUnsignedByte(val ^ 0x80);
    return this;
  }
  /** 
 * Writes an signed short (two byte) value to the buffer. Writes values that
 * can be read using {@link TupleInput#readShort}.
 * @param valis the value to write to the buffer.
 * @return this tuple output object.
 */
  public final TupleOutput writeShort(  int val){
    writeUnsignedShort(val ^ 0x8000);
    return this;
  }
  /** 
 * Writes an signed int (four byte) value to the buffer. Writes values that
 * can be read using {@link TupleInput#readInt}.
 * @param valis the value to write to the buffer.
 * @return this tuple output object.
 */
  public final TupleOutput writeInt(  int val){
    writeUnsignedInt(val ^ 0x80000000);
    return this;
  }
  /** 
 * Writes an signed long (eight byte) value to the buffer. Writes values
 * that can be read using {@link TupleInput#readLong}.
 * @param valis the value to write to the buffer.
 * @return this tuple output object.
 */
  public final TupleOutput writeLong(  long val){
    writeUnsignedLong(val ^ 0x8000000000000000L);
    return this;
  }
  /** 
 * Writes an signed float (four byte) value to the buffer. Writes values
 * that can be read using {@link TupleInput#readFloat}.
 * <code>Float.floatToIntBits</code> is used to convert the signed float
 * value.
 * @param valis 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}.
 * <code>Double.doubleToLongBits</code> is used to convert the signed
 * double value.
 * @param valis 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 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 charsis the array of values to be written.
 * @return this tuple output object.
 * @throws NullPointerExceptionif 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 charsis the array of characters to be written.
 * @return this tuple output object.
 * @throws NullPointerExceptionif 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 charsis the array of characters to be written.
 * @return this tuple output object.
 * @throws NullPointerExceptionif 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 valis 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 valis 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 valis 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;
  }
}
