OutputStream in a portable way.  Data written to
 * a stream using this class can be read back in using the
 * DataInputStream class on any platform.
 *
 * @see DataInputStream
 *
 * @author Aaron M. Renn DataOutputStream to
   * write its data to the specified underlying OutputStream
   *
   * @param out The subordinate OutputStream to which this 
   * object will write
   */
  public DataOutputStream (OutputStream out)
  {
    super (out);
    written = 0;
  }
  /**
   * This method flushes any unwritten bytes to the underlying stream.
   *
   * @exception IOException If an error occurs.
   */
  public void flush () throws IOException
  {
    out.flush();
  }
  /**
   * This method returns the total number of bytes that have been written to
   * the underlying output stream so far.  This is the value of the
   * written instance variable
   *
   * @return The number of bytes written to the stream.
   */
  public final int size ()
  {
    return written;
  }
  /**
   * This method writes the specified byte (passed as an int)
   * to the underlying output stream.
   *
   * @param value The byte to write, passed as an int.
   *
   * @exception IOException If an error occurs.
   */
  public synchronized void write (int value) throws IOException
  {
    out.write (value);
    ++written;
  }
  /**
   * This method writes len bytes from the specified byte array
   * buf starting at position offset into the
   * buffer to the underlying output stream.
   *
   * @param buf The byte array to write from.
   * @param offset The index into the byte array to start writing from.
   * @param len The number of bytes to write.
   *
   * @exception IOException If an error occurs.
   */
  public synchronized void write (byte[] buf, int offset, int len) 
     throws IOException
  {
    out.write(buf, offset, len);
    written += len;
  }
  /**
   * This method writes a Java boolean value to an output stream.  If
   * value is true, a byte with the value of
   * 1 will be written, otherwise a byte with the value of 0 will be
   * written.
   *
   * The value written can be read using the readBoolean
   * method in DataInput.
   *
   * @param value The boolean value to write to the stream
   *
   * @exception IOException If an error occurs
   *
   * @see DataInput#readBoolean
   */
  public final void writeBoolean (boolean value) throws IOException
  {
    write (value ? 1 : 0);
  }
  /**
   * This method writes a Java byte value to an output stream.  The
   * byte to be written will be in the lowest 8 bits of the
   * int value passed.
   *
   * The value written can be read using the readByte or
   * readUnsignedByte methods in DataInput.
   *
   * @param value The byte to write to the stream, passed as 
   * the low eight bits of an int.
   *
   * @exception IOException If an error occurs
   *
   * @see DataInput#readByte
   * @see DataInput#readUnsignedByte
   */
  public final void writeByte (int value) throws IOException
  {
    write (value & 0xff);
  }
  /**
   * This method writes a Java short value to an output stream.  The
   * char to be written will be in the lowest 16 bits of the int
   * value passed.  These bytes will be written "big endian".  That is,
   * with the high byte written first in the following manner:
   * 
   * byte0 = (byte)((value & 0xFF00) >> 8);
   * 
   * byte1 = (byte)(value & 0x00FF);
   *
   * The value written can be read using the readShort and
   * readUnsignedShort methods in DataInput.
   *
   * @param value The short value to write to the stream,
   * passed as an int.
   *
   * @exception IOException If an error occurs
   *
   * @see DataInput#readShort
   * @see DataInput#readUnsignedShort
   */
  public final synchronized void writeShort (int value) throws IOException
  {
    write ((byte) (0xff & (value >> 8)));
    write ((byte) (0xff & value));
  }
  /**
   * This method writes a Java char value to an output stream.  The
   * char to be written will be in the lowest 16 bits of the int
   * value passed.  These bytes will be written "big endian".  That is,
   * with the high byte written first in the following manner:
   * 
   * byte0 = (byte)((value & 0xFF00) >> 8);
   * 
   * byte1 = (byte)(value & 0x00FF);
   *
   * The value written can be read using the readChar
   * method in DataInput.
   *
   * @param value The char value to write, 
   * passed as an int.
   *
   * @exception IOException If an error occurs
   *
   * @see DataInput#readChar
   */
  public final synchronized void writeChar (int value) throws IOException
  {
    write ((byte) (0xff & (value >> 8)));
    write ((byte) (0xff & value));
  }
  /**
   * This method writes a Java int value to an output stream.  The 4 bytes
   * of the passed value will be written "big endian".  That is, with
   * the high byte written first in the following manner:
   * 
   * byte0 = (byte)((value & 0xFF000000) >> 24);
   * 
   * byte1 = (byte)((value & 0x00FF0000) >> 16);
   * byte2 = (byte)((value & 0x0000FF00) >> 8);
   * byte3 = (byte)(value & 0x000000FF);
   *
   * The value written can be read using the readInt
   * method in DataInput.
   *
   * @param value The int value to write to the stream
   *
   * @exception IOException If an error occurs
   *
   * @see DataInput#readInt
   */
  public final synchronized void writeInt (int value) throws IOException
  {
    write ((byte) (0xff & (value >> 24)));
    write ((byte) (0xff & (value >> 16)));
    write ((byte) (0xff & (value >>  8)));
    write ((byte) (0xff & value));
  }
  /**
   * This method writes a Java long value to an output stream.  The 8 bytes
   * of the passed value will be written "big endian".  That is, with
   * the high byte written first in the following manner:
   * 
   * byte0 = (byte)((value & 0xFF00000000000000L) >> 56);
   * 
   * byte1 = (byte)((value & 0x00FF000000000000L) >> 48);
   * byte2 = (byte)((value & 0x0000FF0000000000L) >> 40);
   * byte3 = (byte)((value & 0x000000FF00000000L) >> 32);
   * byte4 = (byte)((value & 0x00000000FF000000L) >> 24);
   * byte5 = (byte)((value & 0x0000000000FF0000L) >> 16);
   * byte6 = (byte)((value & 0x000000000000FF00L) >> 8);
   * byte7 = (byte)(value & 0x00000000000000FFL);
   *
   * The value written can be read using the readLong
   * method in DataInput.
   *
   * @param value The long value to write to the stream
   *
   * @exception IOException If an error occurs
   *
   * @see DataInput#readLong
   */
  public final synchronized void writeLong (long value) throws IOException
  {
    write ((byte) (0xff & (value >> 56)));
    write ((byte) (0xff & (value>> 48)));
    write ((byte) (0xff & (value>> 40)));
    write ((byte) (0xff & (value>> 32)));
    write ((byte) (0xff & (value>> 24)));
    write ((byte) (0xff & (value>> 16)));
    write ((byte) (0xff & (value>>  8)));
    write ((byte) (0xff & value));
  }
  /**
   * This method writes a Java float value to the stream.  This
   * value is written by first calling the method
   * Float.floatToIntBits
   * to retrieve an int representing the floating point number,
   * then writing this int value to the stream exactly the same
   * as the writeInt() method does.
   *
   * The value written can be read using the readFloat
   * method in DataInput.
   *
   * @param value The float value to write to the stream
   *
   * @exception IOException If an error occurs
   *
   * @see writeInt
   * @see DataInput#readFloat
   * @see Float#floatToIntBits
   */
  public final void writeFloat (float value) throws IOException
  {
    writeInt (Float.floatToIntBits (value));
  }
  /**
   * This method writes a Java double value to the stream.  This
   * value is written by first calling the method
   * Double.doubleToLongBits
   * to retrieve an long representing the floating point number,
   * then writing this long value to the stream exactly the same
   * as the writeLong() method does.
   *
   * The value written can be read using the readDouble
   * method in DataInput.
   *
   * @param value The double value to write to the stream
   *
   * @exception IOException If an error occurs
   *
   * @see writeLong
   * @see DataInput#readDouble
   * @see Double#doubleToLongBits
   */
  public final void writeDouble (double value) throws IOException
  {
    writeLong (Double.doubleToLongBits (value));
  }
  /**
   * This method writes all the bytes in a String out to the
   * stream.  One byte is written for each character in the
   * String.
   * The high eight bits of each character are discarded, thus this
   * method is inappropriate for completely representing Unicode characters.
   *
   * @param value The String to write to the stream
   *
   * @exception IOException If an error occurs
   */
  public final void writeBytes (String value) throws IOException
  {
    int len = value.length();
    for (int i = 0; i < len; ++i)
      writeByte (value.charAt(i));
  }
  /**
   * This method writes all the characters of a String to an
   * output stream as an array of char's. Each character
   * is written using the method specified in the writeChar
   * method.
   *
   * @param value The String to write to the stream
   *
   * @exception IOException If an error occurs
   *
   * @see writeChar
   */
  public final void writeChars (String value) throws IOException
  {
    int len = value.length();
    for (int i = 0; i < len; ++i)
      writeChar (value.charAt(i));
  }
  /**
   * This method writes a Java String to the stream in a modified
   * UTF-8 format.  First, two bytes are written to the stream indicating the
   * number of bytes to follow.  Note that this is the number of bytes in the
   * encoded String not the String length.  Next
   * come the encoded characters.  Each character in the String
   * is encoded as either one, two or three bytes.  For characters in the
   * range of \u0001 to <\u007F>, one byte is used.  The character
   * value goes into bits 0-7 and bit eight is 0.  For characters in the range
   * of \u0080 to \u007FF, two bytes are used.  Bits
   * 6-10 of the character value are encoded bits 0-4 of the first byte, with
   * the high bytes having a value of "110".  Bits 0-5 of the character value
   * are stored in bits 0-5 of the second byte, with the high bits set to
   * "10".  This type of encoding is also done for the null character
   * \u0000.  This eliminates any C style NUL character values
   * in the output.  All remaining characters are stored as three bytes.
   * Bits 12-15 of the character value are stored in bits 0-3 of the first
   * byte.  The high bits of the first bytes are set to "1110".  Bits 6-11
   * of the character value are stored in bits 0-5 of the second byte.  The
   * high bits of the second byte are set to "10".  And bits 0-5 of the
   * character value are stored in bits 0-5 of byte three, with the high bits
   * of that byte set to "10".
   *
   * The value written can be read using the readUTF
   * method in DataInput.
   *
   * @param value The String to write to the output in UTF format
   *
   * @exception IOException If an error occurs
   *
   * @see DataInput#readUTF
   */
  public synchronized final void writeUTF (String value) throws IOException
  {
    int len = value.length();
    int sum = 0;
    for (int i = 0; i < len && sum <= 65535; ++i)
      {
	char c = value.charAt(i);
	if (c >= '\u0001' && c <= '\u007f')
	  sum += 1;
	else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff'))
	  sum += 2;
	else
	  sum += 3;
      }
    if (sum > 65535)
      throw new UTFDataFormatException ();
    writeShort (sum);
    for (int i = 0; i < len; ++i)
      {
	char c = value.charAt(i);
	if (c >= '\u0001' && c <= '\u007f')
	  write (c);
	else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff'))
	  {
	    write (0xc0 | (0x1f & (c >> 6)));
	    write (0x80 | (0x3f & c));
	  }
	else
	  {
	    // JSL says the first byte should be or'd with 0xc0, but
	    // that is a typo.  Unicode says 0xe0, and that is what is
	    // consistent with DataInputStream.
	    write (0xe0 | (0x0f & (c >> 12)));
	    write (0x80 | (0x3f & (c >> 6)));
	    write (0x80 | (0x3f & c));
	  }
      }
  }
} // class DataOutputStream