Add PushbackInputStream and DataInputStream

This commit is contained in:
konsoletyper 2014-11-04 21:11:27 +03:00
parent 470bbc6fc1
commit d5e602b38e
5 changed files with 733 additions and 0 deletions

View File

@ -0,0 +1,54 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed 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.
*/
package org.teavm.classlib.java.io;
import org.teavm.classlib.java.lang.TString;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface TDataInput {
void readFully(byte[] b) throws TIOException;
void readFully(byte[] b, int off, int len) throws TIOException;
int skipBytes(int n) throws TIOException;
boolean readBoolean() throws TIOException;
byte readByte() throws TIOException;
int readUnsignedByte() throws TIOException;
short readShort() throws TIOException;
int readUnsignedShort() throws TIOException;
char readChar() throws TIOException;
int readInt() throws TIOException;
long readLong() throws TIOException;
float readFloat() throws TIOException;
double readDouble() throws TIOException;
TString readLine() throws TIOException;
TString readUTF() throws TIOException;
}

View File

@ -0,0 +1,270 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed 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.
*/
package org.teavm.classlib.java.io;
import org.teavm.classlib.java.lang.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class TDataInputStream extends TFilterInputStream implements TDataInput {
byte[] buff;
public TDataInputStream(TInputStream in) {
super(in);
buff = new byte[8];
}
@Override
public final int read(byte[] buffer) throws TIOException {
return in.read(buffer, 0, buffer.length);
}
@Override
public final int read(byte[] buffer, int offset, int length) throws TIOException {
return in.read(buffer, offset, length);
}
@Override
public final boolean readBoolean() throws TIOException {
int temp = in.read();
if (temp < 0) {
throw new TEOFException();
}
return temp != 0;
}
@Override
public final byte readByte() throws TIOException {
int temp = in.read();
if (temp < 0) {
throw new TEOFException();
}
return (byte)temp;
}
private int readToBuff(int count) throws TIOException {
int offset = 0;
while(offset < count) {
int bytesRead = in.read(buff, offset, count - offset);
if(bytesRead == -1) return bytesRead;
offset += bytesRead;
}
return offset;
}
@Override
public final char readChar() throws TIOException {
if (readToBuff(2) < 0){
throw new TEOFException();
}
return (char) (((buff[0] & 0xff) << 8) | (buff[1] & 0xff));
}
@Override
public final double readDouble() throws TIOException {
return TDouble.longBitsToDouble(readLong());
}
@Override
public final float readFloat() throws TIOException {
return TFloat.intBitsToFloat(readInt());
}
@Override
public final void readFully(byte[] buffer) throws TIOException {
readFully(buffer, 0, buffer.length);
}
@Override
public final void readFully(byte[] buffer, int offset, int length) throws TIOException {
if (length < 0) {
throw new TIndexOutOfBoundsException();
}
if (length == 0) {
return;
}
if (in == null) {
throw new TNullPointerException();
}
if (buffer == null) {
throw new TNullPointerException();
}
if (offset < 0 || offset > buffer.length - length) {
throw new TIndexOutOfBoundsException();
}
while (length > 0) {
int result = in.read(buffer, offset, length);
if (result < 0) {
throw new TEOFException();
}
offset += result;
length -= result;
}
}
@Override
public final int readInt() throws TIOException {
if (readToBuff(4) < 0){
throw new TEOFException();
}
return ((buff[0] & 0xff) << 24) | ((buff[1] & 0xff) << 16) | ((buff[2] & 0xff) << 8) | (buff[3] & 0xff);
}
@Override
@Deprecated
public final TString readLine() throws TIOException {
TStringBuilder line = new TStringBuilder(80);
boolean foundTerminator = false;
while (true) {
int nextByte = in.read();
switch (nextByte) {
case -1:
if (line.length() == 0 && !foundTerminator) {
return null;
}
return TString.wrap(line.toString());
case (byte) '\r':
if (foundTerminator) {
((TPushbackInputStream)in).unread(nextByte);
return TString.wrap(line.toString());
}
foundTerminator = true;
/* Have to be able to peek ahead one byte */
if (!(in.getClass() == TPushbackInputStream.class)) {
in = new TPushbackInputStream(in);
}
break;
case (byte) '\n':
return TString.wrap(line.toString());
default:
if (foundTerminator) {
((TPushbackInputStream)in).unread(nextByte);
return TString.wrap(line.toString());
}
line.append((char) nextByte);
}
}
}
@Override
public final long readLong() throws TIOException {
if (readToBuff(8) < 0){
throw new TEOFException();
}
int i1 = ((buff[0] & 0xff) << 24) | ((buff[1] & 0xff) << 16) |
((buff[2] & 0xff) << 8) | (buff[3] & 0xff);
int i2 = ((buff[4] & 0xff) << 24) | ((buff[5] & 0xff) << 16) |
((buff[6] & 0xff) << 8) | (buff[7] & 0xff);
return ((i1 & 0xffffffffL) << 32) | (i2 & 0xffffffffL);
}
@Override
public final short readShort() throws TIOException {
if (readToBuff(2) < 0){
throw new TEOFException();
}
return (short) (((buff[0] & 0xff) << 8) | (buff[1] & 0xff));
}
@Override
public final int readUnsignedByte() throws TIOException {
int temp = in.read();
if (temp < 0) {
throw new TEOFException();
}
return temp;
}
@Override
public final int readUnsignedShort() throws TIOException {
if (readToBuff(2) < 0){
throw new TEOFException();
}
return (char) (((buff[0] & 0xff) << 8) | (buff[1] & 0xff));
}
@Override
public final TString readUTF() throws TIOException {
return decodeUTF(readUnsignedShort());
}
TString decodeUTF(int utfSize) throws TIOException {
return decodeUTF(utfSize, this);
}
private static TString decodeUTF(int utfSize, TDataInput in) throws TIOException {
byte[] buf = new byte[utfSize];
char[] out = new char[utfSize];
in.readFully(buf, 0, utfSize);
return convertUTF8WithBuf(buf, out, 0, utfSize);
}
public static final TString readUTF(TDataInput in) throws TIOException {
return decodeUTF(in.readUnsignedShort(), in);
}
@Override
public final int skipBytes(int count) throws TIOException {
int skipped = 0;
long skip;
while (skipped < count && (skip = in.skip(count - skipped)) != 0) {
skipped += skip;
}
if (skipped < 0) {
throw new TEOFException();
}
return skipped;
}
private static TString convertUTF8WithBuf(byte[] buf, char[] out, int offset, int utfSize)
throws TUTFDataFormatException {
int count = 0;
int s = 0;
int a;
while (count < utfSize) {
char ch = (char)buf[offset + count++];
if (ch < '\u0080') {
out[s++] = ch;
} else if (((a = out[s]) & 0xe0) == 0xc0) {
if (count >= utfSize) {
throw new TUTFDataFormatException(TString.wrap("End of stream reached"));
}
int b = buf[count++];
if ((b & 0xC0) != 0x80) {
throw new TUTFDataFormatException(TString.wrap("Malformed UTF-8 sequence"));
}
out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
} else if ((a & 0xf0) == 0xe0) {
if (count + 1 >= utfSize) {
throw new TUTFDataFormatException(TString.wrap("Malformed UTF-8 sequence"));
}
int b = buf[count++];
int c = buf[count++];
if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
throw new TUTFDataFormatException(TString.wrap("Malformed UTF-8 sequence"));
}
out[s++] = (char)(((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
} else {
throw new TUTFDataFormatException(TString.wrap("Malformed UTF-8 sequence"));
}
}
return new TString(out, 0, s);
}
}

View File

@ -0,0 +1,176 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed 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.
*/
package org.teavm.classlib.java.io;
import org.teavm.classlib.java.lang.TArrayIndexOutOfBoundsException;
import org.teavm.classlib.java.lang.TString;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class TPushbackInputStream extends TFilterInputStream {
protected byte[] buf;
protected int pos;
public TPushbackInputStream(TInputStream in) {
super(in);
buf = (in == null) ? null : new byte[1];
pos = 1;
}
public TPushbackInputStream(TInputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Size must be positive");
}
buf = in == null ? null : new byte[size];
pos = size;
}
@Override
public int available() throws TIOException {
if (buf == null) {
throw new TIOException();
}
return buf.length - pos + in.available();
}
@Override
public void close() throws TIOException {
if (in != null) {
in.close();
in = null;
buf = null;
}
}
@Override
public boolean markSupported() {
return false;
}
@Override
public int read() throws TIOException {
if (buf == null) {
throw new TIOException();
}
// Is there a pushback byte available?
if (pos < buf.length) {
return (buf[pos++] & 0xFF);
}
// Assume read() in the InputStream will return low-order byte or -1
// if end of stream.
return in.read();
}
@Override
public int read(byte[] buffer, int offset, int length) throws TIOException {
if (buf == null) {
throw new TIOException(TString.wrap("Stream is closed"));
}
// Force buffer null check first!
if (offset > buffer.length || offset < 0) {
throw new TArrayIndexOutOfBoundsException(TString.wrap("Offset out of bounds: " + offset));
}
if (length < 0 || length > buffer.length - offset) {
throw new TArrayIndexOutOfBoundsException(TString.wrap("Length out of bounds: " + length));
}
int copiedBytes = 0, copyLength = 0, newOffset = offset;
// Are there pushback bytes available?
if (pos < buf.length) {
copyLength = (buf.length - pos >= length) ? length : buf.length - pos;
System.arraycopy(buf, pos, buffer, newOffset, copyLength);
newOffset += copyLength;
copiedBytes += copyLength;
// Use up the bytes in the local buffer
pos += copyLength;
}
// Have we copied enough?
if (copyLength == length) {
return length;
}
int inCopied = in.read(buffer, newOffset, length - copiedBytes);
if (inCopied > 0) {
return inCopied + copiedBytes;
}
if (copiedBytes == 0) {
return inCopied;
}
return copiedBytes;
}
@Override
public long skip(long count) throws TIOException {
if (in == null) {
throw new TIOException();
}
if (count <= 0) {
return 0;
}
int numSkipped = 0;
if (pos < buf.length) {
numSkipped += (count < buf.length - pos) ? count : buf.length - pos;
pos += numSkipped;
}
if (numSkipped < count) {
numSkipped += in.skip(count - numSkipped);
}
return numSkipped;
}
public void unread(byte[] buffer) throws TIOException {
unread(buffer, 0, buffer.length);
}
public void unread(byte[] buffer, int offset, int length) throws TIOException {
if (length > pos) {
throw new TIOException(TString.wrap("Pushback buffer full"));
}
if (offset > buffer.length || offset < 0) {
throw new TArrayIndexOutOfBoundsException(TString.wrap("Offset out of bounds: " + offset));
}
if (length < 0 || length > buffer.length - offset) {
throw new TArrayIndexOutOfBoundsException(TString.wrap("Length out of bounds: " + length));
}
if (buf == null) {
throw new TIOException(TString.wrap("Stream is closed"));
}
System.arraycopy(buffer, offset, buf, pos - length, length);
pos = pos - length;
}
public void unread(int oneByte) throws TIOException {
if (buf == null) {
throw new TIOException();
}
if (pos == 0) {
throw new TIOException();
}
buf[--pos] = (byte) oneByte;
}
@Override
public void mark(int readlimit) {
return;
}
@Override
public void reset() throws TIOException {
throw new TIOException();
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed 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.
*/
package org.teavm.classlib.java.io;
import org.teavm.classlib.java.lang.TString;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class TUTFDataFormatException extends TIOException {
private static final long serialVersionUID = -6383472574962319733L;
public TUTFDataFormatException() {
super();
}
public TUTFDataFormatException(TString message) {
super(message);
}
}

View File

@ -0,0 +1,199 @@
/*
* 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.
*/
package org.teavm.classlib.java.io;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;
import org.junit.Test;
@SuppressWarnings("resource")
public class PushbackInputStreamTest {
PushbackInputStream pis;
public PushbackInputStreamTest() {
byte[] array = new byte[1000];
for (int i = 0; i < array.length; ++i) {
array[i] = (byte)i;
}
pis = new PushbackInputStream(new ByteArrayInputStream(array), array.length);
}
@Test
public void test_reset() {
PushbackInputStream pb = new PushbackInputStream(new ByteArrayInputStream(new byte[] { 0 }), 2);
try {
pb.reset();
fail("Should throw IOException");
} catch (IOException e) {
// expected
}
}
@Test
public void test_mark() {
PushbackInputStream pb = new PushbackInputStream(new ByteArrayInputStream(new byte[] { 0 }), 2);
pb.mark(Integer.MAX_VALUE);
pb.mark(0);
pb.mark(-1);
pb.mark(Integer.MIN_VALUE);
}
@Test
public void test_ConstructorLjava_io_InputStream() {
try {
PushbackInputStream str = new PushbackInputStream(null);
str.read();
fail("Expected IOException");
} catch (IOException e) {
// Expected
}
try {
pis = new PushbackInputStream(new ByteArrayInputStream("Hello".getBytes()));
pis.unread("He".getBytes());
fail("Failed to throw exception on unread when buffer full");
} catch (IOException e) {
// Expected
}
}
@Test
public void test_ConstructorLjava_io_InputStreamI() {
try {
pis = new PushbackInputStream(new ByteArrayInputStream("Hello".getBytes()), 5);
pis.unread("Hellos".getBytes());
} catch (IOException e) {
// Correct
// Pushback buffer should be full
return;
}
fail("Failed to throw exception on unread when buffer full");
}
@Test
public void test_ConstructorLjava_io_InputStreamL() {
try {
PushbackInputStream str = new PushbackInputStream(null, 1);
str.read();
fail("Expected IOException");
} catch (IOException e) {
// Expected
}
}
@Test
public void test_available() {
try {
assertEquals("Available returned incorrect number of bytes", 1000, pis.available());
} catch (IOException e) {
fail("Exception during available test: " + e.toString());
}
}
@Test
public void test_markSupported() {
assertTrue("markSupported returned true", !pis.markSupported());
}
@Test
public void test_read() {
try {
assertEquals("Incorrect byte read", 0, pis.read());
} catch (IOException e) {
fail("Exception during read test : " + e.getMessage());
}
}
@Test
public void test_read$BII() {
try {
byte[] buf = new byte[100];
pis.read(buf, 0, buf.length);
assertEquals(0, buf[0]);
assertEquals(99, buf[99]);
} catch (IOException e) {
fail("Exception during read test : " + e.getMessage());
}
}
@Test
public void test_skipJ() throws Exception {
byte[] buf = new byte[50];
pis.skip(50);
pis.read(buf, 0, buf.length);
assertEquals(50, buf[0]);
assertEquals(99, buf[49]);
pis.unread(buf);
pis.skip(25);
byte[] buf2 = new byte[25];
pis.read(buf2, 0, buf2.length);
assertEquals(75, buf2[0]);
assertEquals(99, buf2[24]);
}
@Test
public void test_unread$B() {
try {
byte[] buf = new byte[100];
pis.read(buf, 0, buf.length);
assertEquals(0, buf[0]);
assertEquals(99, buf[99]);
pis.unread(buf);
pis.read(buf, 0, 50);
assertEquals(0, buf[0]);
assertEquals(49, buf[49]);
} catch (IOException e) {
fail("IOException during unread test : " + e.getMessage());
}
}
@Test
public void test_unread$BII() throws IOException {
byte[] buf = new byte[100];
pis.read(buf, 0, buf.length);
assertEquals(0, buf[0]);
assertEquals(99, buf[99]);
pis.unread(buf, 50, 50);
pis.read(buf, 0, 50);
assertEquals(50, buf[0]);
assertEquals(99, buf[49]);
// Regression for HARMONY-49
try {
PushbackInputStream pb = new PushbackInputStream(new ByteArrayInputStream(new byte[] { 0 }), 2);
pb.unread(new byte[1], 0, 5);
fail("Assert 0: should throw IOE");
} catch (IOException e) {
// expected
}
}
@Test
public void test_unreadI() {
try {
int x = pis.read();
assertEquals("Incorrect byte read", 0, x);
pis.unread(x);
assertEquals("Failed to unread", x, pis.read());
} catch (IOException e) {
fail("IOException during read test : " + e.getMessage());
}
}
}