/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 2003 Rob Nielsen *
* Copyright (C) 2003-2012 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine is distributed in the hope that it will *
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross.io;
import totalcross.sys.Convert;
import totalcross.sys.Vm;
/**
* DataStream can be attached to any Stream such as a
* PortConnector, PDBFile, or ByteArrayStream, which lets you read and write
* standard Java data types like int, double, and String in a simple
* manner. Here's an example:
*
* <pre>
* PortConnector port = new PortConnector(9600, 0);
* DataStream ds = new DataStream(port);
* ds.writeString("Hello");
* int status = ds.readUnsignedByte();
* if (status == 1)
* {
* ds.writeString("Pi");
* ds.writeDouble(3.14);
* }
* port.close();
* </pre>
*
* <br>
* <b>Important!</b>: All methods
* read and write numeric data in the big endian format (Java format).
* For more information, see <a target=_blank
* href='http://www.webopedia.com/TERM/b/big_endian.html'>this</a>.
* @see DataStreamLE
*/
public class DataStream extends Stream
{
/** The underlying stream, from where bytes are read and written. */
protected Stream stream;
/** a four byte array for reading and writing numbers */
protected byte[] buffer = new byte[256]; // starts with 256 bytes since readSmallString uses it
static protected byte zeros[] = new byte[128]; // tc100
/** Message thrown when an end of stream is reached when reading. */
public static final String EOSMessage = "End of stream";
private final boolean ensureWrite;
/**
* Constructs a new DataStream which sits upon the given stream using big
* endian notation for multibyte values.
*
* @param stream the base stream, from where bytes are read and written.
*/
public DataStream(Stream stream)
{
this(stream, false);
}
/**
* Constructs a new DataStream which sits upon the given stream using big endian notation for multibyte values.
*
* @param stream
* the base stream, from where bytes are read and written.
* @param ensureWrite
* if true, write operations are blocked until all the requested data is written to the underlying stream.
*/
public DataStream(Stream stream, boolean ensureWrite)
{
if (stream == null)
throw new NullPointerException("Argument 'stream' cannot be null");
this.stream = stream;
this.ensureWrite = ensureWrite;
}
/**
* Closes the stream. This just calls the close method of the attached stream,
* thus closing it. Usually, this method may never be called. Remember that closing
* a stream twice might throw an IOException, so if you call this close method, don't
* call the base Stream's.
*
* @throws totalcross.io.IOException
* @throws IOException
*/
public void close() throws totalcross.io.IOException
{
if (stream != null)
stream.close();
}
/** Pads the stream writing 0 the given number of times.
* @param n The number of zeros to be written
* @throws totalcross.io.IOException */
final public int pad(int n) throws totalcross.io.IOException
{
if (zeros.length < n)
zeros = new byte[n + 16]; // guich@200 grows the buffer if necessary - corrected by mike roberts
return writeBytesInternal(zeros, 0, n);
}
/**
* Reads a byte from the stream as a boolean. True is returned if the byte is
* not zero, false if it is.
*
* @return the boolean value read.
* @throws EOFException
* @throws totalcross.io.IOException
*/
final public boolean readBoolean() throws EOFException, totalcross.io.IOException
{
readBytesInternal(buffer, 0, 1, true);
return buffer[0] != 0;
}
/**
* Reads a single byte from the stream. The returned value will range from
* -128 to 127.
*
* @return the read byte
* @throws EOFException
* @throws totalcross.io.IOException
*/
final public byte readByte() throws EOFException, totalcross.io.IOException
{
readBytesInternal(buffer, 0, 1, true);
return buffer[0];
}
final public int readBytes(byte buf[], int start, int count) throws totalcross.io.IOException
{
return readBytesInternal(buf, start, count, false);
}
/**
* Reads bytes from the stream. Returns the number of bytes actually read.
*
* @param buf the byte array to read data into
* @throws totalcross.io.IOException If an error prevented the read operation from occurring.
*/
final public int readBytes(byte buf[]) throws totalcross.io.IOException
{
readBytesInternal(buf, 0, buf.length, false);
return buf.length;
}
/**
* Reads a float value from the stream as four bytes in IEEE 754 format.
*
* @return the float value as a double.
* @throws EOFException
* @throws totalcross.io.IOException
*/
public double readFloat() throws EOFException, totalcross.io.IOException
{
return Convert.intBitsToDouble(readInt());
}
/**
* Reads an integer from the stream as four bytes. The returned value will
* range from -2147483648 to 2147483647.
*
* @return the integer value
* @throws EOFException
* @throws totalcross.io.IOException
*/
public int readInt() throws EOFException, totalcross.io.IOException
{
byte[] b = buffer;
readBytesInternal(b, 0, 4, true);
return (((b[0] & 0xFF) << 24) | ((b[1] & 0xFF) << 16) | ((b[2] & 0xFF) << 8) | (b[3] & 0xFF));
}
/**
* Reads a short from the stream as two bytes. The returned value will range
* from -32768 to 32767.
*
* @return the short value
* @throws EOFException
* @throws totalcross.io.IOException
*/
public short readShort() throws EOFException, totalcross.io.IOException
{
byte[] b = buffer;
readBytesInternal(b, 0, 2, true);
return (short)(((b[0] & 0xFF) << 8) | (b[1] & 0xFF));
}
/**
* Reads a double.
*
* @return the double value
* @throws EOFException
* @throws totalcross.io.IOException
* @since SuperWaba 2.0
*/
public double readDouble() throws EOFException, totalcross.io.IOException
{
return Convert.longBitsToDouble(readLong());
}
/**
* Reads a long.
*
* @return the long value
* @throws EOFException
* @throws totalcross.io.IOException
* @since SuperWaba 2.0
*/
public long readLong() throws EOFException, totalcross.io.IOException
{
long l1 = (long) readInt() & 0xFFFFFFFFL;
long l2 = (long) readInt() & 0xFFFFFFFFL;
return (l1 << 32) | l2;
}
/**
* Reads a single unsigned byte from the stream. The returned value will
* range from 0 to 255. Use writeByte to write the unsigned byte.
*
* @return the read byte.
* @throws EOFException
* @throws totalcross.io.IOException
* @see #writeByte(int)
*/
final public int readUnsignedByte() throws EOFException, totalcross.io.IOException
{
readBytesInternal(buffer, 0, 1, true);
return buffer[0] & 0xFF;
}
/**
* Reads an unsigned short from the stream as two bytes. The returned value
* will range from 0 to 65535. Use writeShort to write the unsigned short.
*
* @return the short
* @throws EOFException
* @throws totalcross.io.IOException
* @see #writeShort(int)
*/
public int readUnsignedShort() throws EOFException, totalcross.io.IOException
{
byte[] b = buffer;
readBytesInternal(b, 0, 2, true);
return (((b[0] & 0xFF) << 8) | (b[1] & 0xFF));
}
/**
* Reads an unsigned integer from the stream as four bytes.<br>
* The returned value will range from 0 to 4294967295.
*
* @return the integer value stored in a long
* @throws EOFException
* @throws totalcross.io.IOException
* @since TotalCross 1.27
*/
public long readUnsignedInt() throws EOFException, totalcross.io.IOException
{
byte[] b = buffer;
readBytesInternal(b, 0, 4, true);
return ((((long)(b[0] & 0xFF)) << 24) | (((long)(b[1] & 0xFF)) << 16) | ((b[2] & 0xFF) << 8) | (b[3] & 0xFF));
}
/**
* Writes a boolean to the stream as a byte. True values are written as 1 and
* false values as 0.
*
* @param bool the boolean to write
* @return the number of bytes written: 1
* @throws totalcross.io.IOException
*/
final public int writeBoolean(boolean bool) throws totalcross.io.IOException
{
buffer[0] = (bool ? (byte) 1 : (byte) 0);
return writeBytesInternal(buffer, 0, 1);
}
/**
* Writes a single byte to the stream.
*
* @param by
* the byte to write
* @return the number of bytes written: 1
* @throws totalcross.io.IOException
*/
final public int writeByte(byte by) throws totalcross.io.IOException
{
buffer[0] = by;
return writeBytesInternal(buffer, 0, 1);
}
/**
* Writes a single byte to the stream.
*
* @param by
* the byte to write (only least significant byte is written)
* @return the number of bytes written: 1
* @throws totalcross.io.IOException
*/
final public int writeByte(int by) throws totalcross.io.IOException
{
buffer[0] = (byte)by;
return writeBytesInternal(buffer, 0, 1);
}
final public int writeBytes(byte buf[], int start, int count) throws totalcross.io.IOException
{
return writeBytesInternal(buf, start, count);
}
/**
* Writes bytes to the stream.
*
* @param buf the byte array to write data from
* @return the number of bytes written: buf.length
* @throws totalcross.io.IOException
*/
final public int writeBytes(byte buf[]) throws totalcross.io.IOException
{
return writeBytesInternal(buf, 0, buf.length);
}
/**
* Writes a float value to the stream as four bytes in IEEE 754 format
*
* @param f the float to write
* @return the number of bytes written: 4
* @throws totalcross.io.IOException
*/
public int writeFloat(double f) throws totalcross.io.IOException
{
return writeInt(Convert.doubleToIntBits(f));
}
/**
* Writes an integer to the stream as four bytes.
*
* @param i
* the integer to write
* @return the number of bytes written: 4
* @throws totalcross.io.IOException
*/
public int writeInt(int i) throws totalcross.io.IOException
{
byte[] b = buffer;
b[3] = (byte)i;
i >>= 8; // guich@300_40
b[2] = (byte)i;
i >>= 8;
b[1] = (byte)i;
i >>= 8;
b[0] = (byte)i;
return writeBytesInternal(b, 0, 4);
}
/**
* Writes a short to the stream as two bytes.
*
* @param i
* the short to write
* @return the number of bytes written: 2
* @throws totalcross.io.IOException
*/
public int writeShort(int i) throws totalcross.io.IOException
{
byte[] b = buffer;
b[1] = (byte)i;
i >>= 8; // guich@300_40
b[0] = (byte)i;
return writeBytesInternal(b, 0, 2);
}
/**
* Reads a string, converting from Pascal (the length is placed before the
* string) to Java format.
* <p>The String size is limited to 65535 characters.
*
* @return a zero or more length string. null is never returned.
* @throws EOFException
* @throws totalcross.io.IOException
*/
public String readString() throws EOFException, totalcross.io.IOException
{
int len = readUnsignedShort(); // guich@341_2
if (len == 0)
return "";
if (buffer.length < len)
buffer = new byte[len + 16]; // guich@200 grows the buffer if necessary - corrected by mike mcroberts
readBytesInternal(buffer, 0, len, true);
return new String(totalcross.sys.Convert.charConverter.bytes2chars(buffer, 0, len)); // eisvogel@450_17: make sure that desktop and device use the same convertion algorithm
}
/**
* Reads a big string, converting from Pascal (the length is placed before the
* string) to Java format.
* <p>The String size is limited to 2,147,483,647 characters.
*
* @return a zero or more length string. null is never returned.
* @throws EOFException
* @throws totalcross.io.IOException
* @since TotalCross 1.0
*/
public String readBigString() throws EOFException, totalcross.io.IOException
{
int len = readInt(); // guich@341_2
if (len == 0)
return "";
if (buffer.length < len)
buffer = new byte[len + 16]; // guich@200 grows the buffer if necessary - corrected by mike mcroberts
readBytesInternal(buffer, 0, len, true);
return new String(totalcross.sys.Convert.charConverter.bytes2chars(buffer, 0, len)); // eisvogel@450_17: make sure that desktop and device use the same convertion algorithm
}
/**
* Reads an array of Strings.
* <p>The array length is limited to 65535 elements.
*
* @return a zero or more length array. null is never returned.
* @throws EOFException
* @throws totalcross.io.IOException
*/
public String[] readStringArray() throws EOFException, totalcross.io.IOException
{
int size = (int) readUnsignedShort(),i=0;
String[] a = new String[size];
while (--size >= 0)
a[i++] = readString();
return a;
}
/**
* Reads an array of Strings.
*
* @param size The of the string array.
* @return a zero or more length array. null is never returned.
* @throws EOFException
* @throws totalcross.io.IOException
*/
public String[] readStringArray(int size) throws EOFException, totalcross.io.IOException
{
int i = 0;
String[] a = new String[size];
while (--size >= 0)
a[i++] = readString();
return a;
}
/**
* Writes the string into the stream, converting it from Java format
* to Pascal format (the length is placed before the string).
* <p>The String size is limited to 65535 characters.
*
* @return the number of bytes written.
* @throws totalcross.io.IOException
*/
public int writeString(String s) throws totalcross.io.IOException
{
int n = 0,l;
if (s != null && (l=s.length()) > 0) // eisvogel@421_70: don't let write more than the pdb can handle
{
char[] ac = s.toCharArray();
if (l > 65535)
throw new IOException("String size "+l+" is too big to use with writeString!");
byte[] c = totalcross.sys.Convert.charConverter.chars2bytes(ac, 0, ac.length); // eisvogel@450_17
n += writeShort(c.length);
n += writeBytesInternal(c, 0, c.length);
}
else
n += writeShort(0);
return n;
}
/**
* Writes the string into the stream, converting it from Java format
* to Pascal format (the length is placed before the string).
* <p>The String size is limited to 2,147,483,647 characters.
*
* @return the number of bytes written.
* @throws totalcross.io.IOException
* @since TotalCross 1.0
*/
public int writeBigString(String s) throws totalcross.io.IOException
{
int n = 0;
if (s != null && s.length() > 0) // eisvogel@421_70: don't let write more than the pdb can handle
{
char[] ac = s.toCharArray();
byte[] c = totalcross.sys.Convert.charConverter.chars2bytes(ac, 0, ac.length); // eisvogel@450_17
n += writeInt(c.length);
n += writeBytesInternal(c, 0, c.length);
}
else
n += writeInt(0);
return n;
}
/**
* writes the string array into the stream
*
* @return the number of bytes written.
* @throws totalcross.io.IOException
*/
public int writeStringArray(String[] v) throws totalcross.io.IOException
{
int n = 0;
if (v == null || v.length == 0)
n += writeShort(0);
else
{
n += writeShort(v.length);
for (int i = 0; i < v.length; i++)
n += writeString(v[i]);
}
return n;
}
/**
* Writes a double.
*
* @return the number of bytes written: 8
* @throws totalcross.io.IOException
* @since SuperWaba 2.0
*/
public int writeDouble(double d) throws totalcross.io.IOException
{
return writeLong(Convert.doubleToLongBits(d));
}
/**
* Writes a long.
* @throws totalcross.io.IOException
*
* @since SuperWaba 2.0
*/
public int writeLong(long l) throws totalcross.io.IOException
{
return writeInt((int)(l >> 32)) + writeInt((int)l);
}
/**
* Reads a C-style string from the stream. This is a NUL (0) terminated
* series of characters. This format is commonly used by other
* applications. Note that if you're creating your own stream, choose
* readString instead of readCString, because readCString is *much* slower.
* Also, this method does not handle correctly unicode characters.
*
* @return the loaded String
* @throws EOFException
* @throws totalcross.io.IOException
*/
public String readCString() throws EOFException, totalcross.io.IOException
{
// we have to read one character at a time.
byte b[] = buffer, c;
byte[] buf = new byte[1];
int size = 0;
while (true)
{
readBytesInternal(buf, 0, 1, true);
c = buf[0];
if (c == 0)
break;
if (size == b.length) // grow the array if necessary
{
byte[] temp = new byte[b.length * 3 / 2];
Vm.arrayCopy(b, 0, temp, 0, size);
buffer = b = temp;
}
b[size++] = c;
}
return new String(totalcross.sys.Convert.charConverter.bytes2chars(b, 0, size)); // eisvogel@450_17
}
/**
* Writes a C-style string to the stream. This means that all the characters
* of the string are written out, followed by a NUL (0) character. This
* format is commonly used by other applications.
*
* @param s the string to write
* @return the number of bytes written.
* @throws totalcross.io.IOException
*/
final public int writeCString(String s) throws totalcross.io.IOException
{
if (s == null)
return writeByte(0);
int n = 0;
char[] ac = s.toCharArray();
byte[] ab = totalcross.sys.Convert.charConverter.chars2bytes(ac, 0, ac.length); // eisvogel@450_17
n += writeBytesInternal(ab, 0, ab.length);
n += writeByte(0);
return n;
}
/**
* Reads a two-byte character.
*
* @throws EOFException
* @throws totalcross.io.IOException
* @since SuperWaba 4.21
*/
public char readChar() throws EOFException, totalcross.io.IOException // guich@421_31
{
byte[] b = buffer;
readBytesInternal(b, 0, 2, true);
return (char)(((b[0] & 0xFF) << 8) | (b[1] & 0xFF));
}
/**
* Writes a two-byte character.
*
* @param c the character to be written.
* @return the number of bytes written: 2
* @throws totalcross.io.IOException
* @since SuperWaba 4.21
*/
public int writeChar(char c) throws totalcross.io.IOException // guich@421_31
{
byte[] b = buffer;
b[1] = (byte)c;
c >>= 8;
b[0] = (byte)c;
return writeBytesInternal(b, 0, 2);
}
/**
* Reads an array of chars. The length is stored in the first two bytes
* as an unsigned short.
* <p>The char array size is limited to 65535 characters.
*
* @throws EOFException
* @throws totalcross.io.IOException
* @since SuperWaba 3.5
*/
public char[] readChars() throws EOFException, totalcross.io.IOException
{
return readChars(readUnsignedShort());
}
/**
* Reads a char array with the given length.
*
* @param chars An already created chars array.
* @param len The array length.
* @throws EOFException
* @throws totalcross.io.IOException
* @since TotalCross 1.01
*/
public void readChars(char[] chars, int len) throws EOFException, totalcross.io.IOException
{
byte[] bytes = buffer;
int buflen = bytes.length / 2, start = 0;
while (len > 0)
{
int avail = (len > buflen ? buflen : len) * 2;
readBytesInternal(bytes, 0, avail, true);
for (int i =0; i < avail; i += 2)
chars[start++] = (char) (((bytes[i] & 0xFF) << 8) | (bytes[i+1] & 0xFF));
len -= avail / 2;
}
}
/**
* Reads 'count' chars from the stream to the given char array, starting from index 'start'.
*
* @param chars
* a char array
* @param start
* starting position on the char array
* @param count
* number of chars to be read from the stream
*
* @throws EOFException
* @throws totalcross.io.IOException
* @since TotalCross 1.15
*/
public int readChars(char[] chars, int start, int count) throws EOFException, totalcross.io.IOException
{
byte[] bytes = buffer;
int buflen = bytes.length / 2;
int current = start;
while (count > 0)
{
int avail = (count > buflen ? buflen : count) * 2;
readBytesInternal(bytes, 0, avail, true);
for (int i =0; i < avail; i += 2)
chars[current++] = (char) (((bytes[i] & 0xFF) << 8) | (bytes[i+1] & 0xFF));
count -= avail / 2;
}
return current - start;
}
/**
* Reads an array of chars, where its length is stored in the first four bytes
* as an int.
* <p>The char array size is limited to 2,147,483,647 characters.
*
* @throws EOFException
* @throws totalcross.io.IOException
* @since TotalCross 1.0 beta3
*/
public char[] readBigChars() throws EOFException, totalcross.io.IOException
{
return readChars(readInt());
}
/**
* Reads a char array with the given length.
*
* @param len
* @return
* @throws EOFException
* @throws totalcross.io.IOException
* @since TotalCross 1.0 beta3
*/
protected char[] readChars(int len) throws EOFException, totalcross.io.IOException
{
char[] chars = new char[len];
byte[] bytes = buffer;
int buflen = bytes.length / 2, start = 0;
while (len > 0)
{
int avail = (len > buflen ? buflen : len) * 2;
readBytesInternal(bytes, 0, avail, true);
for (int i =0; i < avail; i += 2)
chars[start++] = (char) (((bytes[i] & 0xFF) << 8) | (bytes[i+1] & 0xFF));
len -= avail / 2;
}
return chars;
}
/**
* Writes an array of chars, placing its length in the first two bytes, as an
* unsigned short.
* <p>The char array size is limited to 65535 characters.
*
* @param len the length to be written or -1 if it is to write the whole char array
* @param chars the char array to be written.
* @param start the starting index (in most cases: 0).
* @return the number of bytes written.
* @throws totalcross.io.IOException
* @since SuperWaba 3.5
*/
public int writeChars(char[] chars, int start, int len) throws totalcross.io.IOException
{
return writeChars(chars, start, len, 2);
}
/**
* Writes an array of chars, placing its length in the first four bytes, as an
* int.
* <p>The char array size is limited to 2,147,483,647 characters.
*
* @param len the length to be written or -1 if it is to write the whole char array
* @param chars the char array to be written.
* @param start the starting index (in most cases: 0).
* @return the number of bytes written.
* @throws totalcross.io.IOException
* @since TotalCross 1.0 beta3
*/
public int writeBigChars(char[] chars, int start, int len) throws totalcross.io.IOException
{
return writeChars(chars, start, len, 4);
}
/** Writes the given char array, writting the length as an int or as a short or as a byte or don't
* writting the length, depending on the number of bytes given (4,2,1,0). */
public int writeChars(char[] chars, int start, int len, int lenSize) throws totalcross.io.IOException
{
int n=0,c;
if (len < 0)
len = chars == null ? 0 : chars.length;
len -= start;
if (len < 0)
len = 0; // eisvogel@421_70
if (lenSize == 2)
{
if (len > 65535)
throw new IOException("String size "+len+" is too big to use with writeChars!");
n = writeShort(len);
}
else
if (lenSize == 4)
n = writeInt(len);
else
if (lenSize == 1)
{
if (len > 255)
throw new IOException("String size "+len+" is too big to use with writeChars!");
n = writeByte(len);
}
byte[] bytes = buffer;
int buflen = bytes.length / 2;
while (len > 0)
{
int avail = (len > buflen ? buflen : len) * 2;
for (int i =0; i < avail; i += 2)
{
c = chars[start++];
bytes[i+1] = (byte)c;
bytes[i] = (byte)(c>>8);
}
n += writeBytesInternal(bytes, 0, avail);
len -= avail / 2;
}
return n;
}
/** Writes the String as a char array. The chars are read using the charAt method from the String class.
* This method is faster than the other writeChars method on blackberry, but slower on other devices.
* <p>The char array size is limited to 65535 characters.
* @param s The String to be written. Must not be null!
* @param len The maximum number of chars to be written. Must be less than the String's length.
* @since TotalCross 1.0 beta 4
*/
public int writeChars(String s, int len) throws totalcross.io.IOException
{
if (len > 65535)
throw new IOException("String size "+len+" is too big to use with writeChars!");
int c;
int start = 0;
int n = writeShort(len);
byte[] bytes = buffer;
int buflen = bytes.length / 2;
while (len > 0)
{
int avail = (len > buflen ? buflen : len) * 2;
for (int i =0; i < avail; i += 2)
{
c = s.charAt(start++);
bytes[i+1] = (byte)c;
bytes[i] = (byte)(c>>8);
}
n += writeBytesInternal(bytes, 0, avail);
len -= avail / 2;
}
return n;
}
/** Returns the base stream attached to this stream. */
public Stream getStream()
{
return stream;
}
/**
* Reads a fixed length string from the stream. The given number of
* characters are read and converted to a String.
*
* @param length the number of characters to read
* @return the loaded string
* @throws EOFException
* @throws totalcross.io.IOException
*/
public String readFixedString(int length) throws EOFException, totalcross.io.IOException
{
if (buffer.length < length)
buffer = new byte[length];
readBytesInternal(buffer, 0, length, true);
return new String(totalcross.sys.Convert.charConverter.bytes2chars(buffer, 0, length)); // guich@tc100b5_25: use the byte array instead
}
/**
* Writes a fixed length string to the stream. If the given string is longer
* than the given length, it will be truncated and if it is shorter, it will
* be padded with spaces.
*
* @param s the string to write
* @param length the length of the fixed string
* @throws totalcross.io.IOException
*/
public void writeFixedString(String s, int length) throws totalcross.io.IOException
{
writeFixedString(s, length, ' ');
}
/**
* Writes a fixed length string to the stream. If the given string is longer
* than the given length, it will be truncated and if it is shorter, it will
* be padded the given pad character.
*
* @param s the string to write
* @param length the length of the fixed string
* @param pad the character to pad if the string is shorter than the length
* @throws totalcross.io.IOException
*/
public void writeFixedString(String s, int length, char pad) throws totalcross.io.IOException
{
if (length == 0)
return;
if (buffer.length < length)
buffer = new byte[length];
byte[] b = buffer;
int slen = 0;
if (s != null)
{
char[] c = s.toCharArray();
slen = c.length;
for (int i = 0; i < slen && i < length; i++)
b[i] = (byte) c[i];
}
Convert.fill(b, slen, length, pad); // pad the rest
writeBytesInternal(b, 0, length);
}
/** Write the given Object using the Storable class.
* @see Storable
*/
public void writeObject(Storable s) throws totalcross.io.IOException
{
writeString(s.getClass().getName());
s.saveState(this);
}
/**
* Read a Storable object.
*
* @throws ClassNotFoundException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws EOFException
* @throws totalcross.io.IOException
* @see Storable
*/
public Object readObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException, EOFException,
totalcross.io.IOException
{
String className = readString();
Class<?> c = Class.forName(className);
Object o = c.newInstance();
((Storable) o).loadState(this);
return o;
}
/**
* Read a small String. If the length is 0, an
* empty String is returned.
* <p>
* The String size is limited to 255 characters.
*
* @throws EOFException
* @throws totalcross.io.IOException
* @since TotalCross 1.0
*/
public String readSmallString() throws EOFException, totalcross.io.IOException
{
int len = readUnsignedByte();
if (len == 0)
return "";
readBytesInternal(buffer, 0, len, true);
return new String(totalcross.sys.Convert.charConverter.bytes2chars(buffer, 0, len));
}
/** Write a small String comprised of only ASCII chars (each char is casted to byte).
* The length is written as a single byte before the byte array.
* <p>The String size is limited to 255 characters.
* @since TotalCross 1.0
*/
public int writeSmallString(String s) throws totalcross.io.IOException
{
int len = s == null ? 0 : s.length();
if (len > 255) throw new IOException("String size "+s.length()+" is too big to use with writeSmallString!");
writeByte(len);
int ret = len+1;
if (len > 0)
for (int i = 0; len-- > 0; i++)
writeByte(s.charAt(i));
return ret;
}
/** Write a small String taking each char as a byte. To read it, use readString.
* The maximum allowed length is 65536.
* @since TotalCross 1.52
*/
public int writeSmallString8(String s) throws IOException
{
int len = s == null ? 0 : s.length();
if (len > 65536) throw new IOException("String size "+s.length()+" is too big to use with writeSmallString8!");
writeShort(len);
int ret = len+2;
if (len > 0)
for (int i = 0; len-- > 0; i++)
writeByte(s.charAt(i));
return ret;
}
protected int writeBytesInternal(byte[] buf, int start, int count) throws totalcross.io.IOException
{
int written = stream.writeBytes(buf, start, count);
if (ensureWrite && written < count)
{
do
{
written += stream.writeBytes(buf, start + written, count - written);
} while (written < count);
}
return written;
}
/**
* Blocks until the requested number of bytes is read from the underlying stream, only returning less than count if
* the end of the stream is reached.
*
* @param buf
* @param start
* @param count
* @return The number of bytes read, which will usually be equal to count. It may return a positive value smaller
* than count if the end of the stream is reached during the read operation, or -1 if the end of stream and
* nothing was read.
* @throws EOFException
* @throws totalcross.io.IOException
*/
protected int readBytesInternal(byte[] buf, int start, int count, boolean throwEOF) throws EOFException, totalcross.io.IOException
{
if (count == 0) // just return
return 0;
int r = stream.readBytes(buf, start, count);
if (r == count) // quick check before loop
return r;
int bytesRead = r;
if (r != -1)
{
while (bytesRead < count)
{
r = stream.readBytes(buf, start + bytesRead, count - bytesRead); // stop after we finish reading or at eos
if (r == -1)
break;
bytesRead += r;
}
}
if (r == -1 && throwEOF)
throw new EOFException(EOSMessage);
return bytesRead;
}
/** @deprecated Use skipBytes instead. */
public int skip(int n) throws IOException
{
return skipBytes(n);
}
}