diff --git a/classlib/src/main/java/org/teavm/classlib/fs/InMemoryVirtualDirectory.java b/classlib/src/main/java/org/teavm/classlib/fs/InMemoryVirtualDirectory.java index 69a76b39a..6e808a180 100644 --- a/classlib/src/main/java/org/teavm/classlib/fs/InMemoryVirtualDirectory.java +++ b/classlib/src/main/java/org/teavm/classlib/fs/InMemoryVirtualDirectory.java @@ -15,8 +15,6 @@ */ package org.teavm.classlib.fs; -import java.io.InputStream; -import java.io.OutputStream; import java.util.LinkedHashMap; import java.util.Map; @@ -48,12 +46,7 @@ public class InMemoryVirtualDirectory extends AbstractInMemoryVirtualFile { } @Override - public InputStream read() { - throw new UnsupportedOperationException(); - } - - @Override - public OutputStream write(boolean append) { + public VirtualFileAccessor createAccessor() { throw new UnsupportedOperationException(); } diff --git a/classlib/src/main/java/org/teavm/classlib/fs/InMemoryVirtualFile.java b/classlib/src/main/java/org/teavm/classlib/fs/InMemoryVirtualFile.java index 666f04554..e29139f69 100644 --- a/classlib/src/main/java/org/teavm/classlib/fs/InMemoryVirtualFile.java +++ b/classlib/src/main/java/org/teavm/classlib/fs/InMemoryVirtualFile.java @@ -15,10 +15,7 @@ */ package org.teavm.classlib.fs; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.Arrays; public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile { @@ -50,23 +47,37 @@ public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile { } @Override - public InputStream read() { + public VirtualFileAccessor createAccessor() { if (parent == null) { return null; } - return new ByteArrayInputStream(data, 0, size); - } - @Override - public OutputStream write(boolean append) { - if (parent == null) { - return null; - } - if (!append) { - data = new byte[0]; - size = 0; - } - return new OutputStreamImpl(data, size); + return new VirtualFileAccessor() { + @Override + public int read(int pos, byte[] buffer, int offset, int limit) throws IOException { + limit = Math.max(0, Math.min(size - pos, limit)); + System.arraycopy(data, pos, buffer, offset, limit); + return limit; + } + + @Override + public void write(int pos, byte[] buffer, int offset, int limit) throws IOException { + expandData(pos + limit); + System.arraycopy(buffer, offset, data, pos, limit); + size = pos + limit; + } + + @Override + public int size() { + return size; + } + + @Override + public void resize(int size) throws IOException { + expandData(size); + InMemoryVirtualFile.this.size = size; + } + }; } @Override @@ -84,73 +95,10 @@ public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile { return size; } - class OutputStreamImpl extends OutputStream { - byte[] data; - int pos; - - OutputStreamImpl(byte[] data, int pos) { - this.data = data; - this.pos = pos; - } - - private void ensureIO() throws IOException { - if (data == null) { - throw new IOException("Stream was closed"); - } - } - - @Override - public void write(int b) throws IOException { - ensureIO(); - expandData(pos + 1); - data[pos++] = (byte) b; - sync(); - } - - private void expandData(int newSize) { - if (newSize > data.length) { - int newCapacity = Math.max(newSize, data.length) * 3 / 2; - boolean actual = data == InMemoryVirtualFile.this.data; - data = Arrays.copyOf(data, newCapacity); - if (actual) { - InMemoryVirtualFile.this.data = data; - } - } - } - - private void sync() { - if (data == InMemoryVirtualFile.this.data) { - size = pos; - modify(); - } - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - ensureIO(); - if (len == 0) { - return; - } - - if (off < 0 || len < 0 || off + len >= b.length) { - throw new IndexOutOfBoundsException(); - } - - expandData(pos + len); - while (len-- > 0) { - data[pos++] = b[off++]; - } - - sync(); - } - - @Override - public void close() throws IOException { - data = null; - } - - @Override - public void flush() throws IOException { + private void expandData(int newSize) { + if (newSize > data.length) { + int newCapacity = Math.max(newSize, data.length) * 3 / 2; + data = Arrays.copyOf(data, newCapacity); } } } diff --git a/classlib/src/main/java/org/teavm/classlib/fs/VirtualFile.java b/classlib/src/main/java/org/teavm/classlib/fs/VirtualFile.java index ce838eee4..6a214c5bc 100644 --- a/classlib/src/main/java/org/teavm/classlib/fs/VirtualFile.java +++ b/classlib/src/main/java/org/teavm/classlib/fs/VirtualFile.java @@ -15,9 +15,6 @@ */ package org.teavm.classlib.fs; -import java.io.InputStream; -import java.io.OutputStream; - public interface VirtualFile { String getName(); @@ -29,9 +26,7 @@ public interface VirtualFile { VirtualFile getChildFile(String fileName); - InputStream read(); - - OutputStream write(boolean append); + VirtualFileAccessor createAccessor(); VirtualFile createFile(String fileName); diff --git a/classlib/src/main/java/org/teavm/classlib/fs/VirtualFileAccessor.java b/classlib/src/main/java/org/teavm/classlib/fs/VirtualFileAccessor.java new file mode 100644 index 000000000..79ab0c2a3 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/fs/VirtualFileAccessor.java @@ -0,0 +1,28 @@ +/* + * Copyright 2017 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.fs; + +import java.io.IOException; + +public interface VirtualFileAccessor { + int read(int pos, byte[] buffer, int offset, int limit) throws IOException; + + void write(int pos, byte[] buffer, int offset, int limit) throws IOException; + + int size(); + + void resize(int size) throws IOException; +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/io/TFile.java b/classlib/src/main/java/org/teavm/classlib/java/io/TFile.java index 7910b020f..92fdb7e1d 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/io/TFile.java +++ b/classlib/src/main/java/org/teavm/classlib/java/io/TFile.java @@ -366,6 +366,11 @@ public class TFile implements Serializable, Comparable { return path.hashCode(); } + @Override + public String toString() { + return path; + } + private static String fixSlashes(String origPath) { int uncIndex = 0; int length = origPath.length(); diff --git a/classlib/src/main/java/org/teavm/classlib/java/io/TFileInputStream.java b/classlib/src/main/java/org/teavm/classlib/java/io/TFileInputStream.java index 62d7ff407..117a1504c 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/io/TFileInputStream.java +++ b/classlib/src/main/java/org/teavm/classlib/java/io/TFileInputStream.java @@ -18,10 +18,14 @@ package org.teavm.classlib.java.io; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.Objects; import org.teavm.classlib.fs.VirtualFile; +import org.teavm.classlib.fs.VirtualFileAccessor; public class TFileInputStream extends InputStream { - private InputStream underlyingStream; + private VirtualFileAccessor accessor; + private int pos; + private boolean eof; public TFileInputStream(TFile file) throws FileNotFoundException { VirtualFile virtualFile = file.findVirtualFile(); @@ -29,39 +33,78 @@ public class TFileInputStream extends InputStream { throw new FileNotFoundException(); } - underlyingStream = virtualFile.read(); - if (underlyingStream == null) { + accessor = virtualFile.createAccessor(); + if (accessor == null) { throw new FileNotFoundException(); } } - @Override - public int read(byte[] b) throws IOException { - return underlyingStream.read(b); - } - @Override public int read(byte[] b, int off, int len) throws IOException { - return underlyingStream.read(b, off, len); + Objects.requireNonNull(b); + if (off < 0 || len < 0 || off + len > b.length) { + throw new IndexOutOfBoundsException(); + } + if (eof) { + return -1; + } + ensureOpened(); + int result = accessor.read(pos, b, off, len); + pos += result; + if (pos == accessor.size()) { + eof = true; + } + return result; } @Override public long skip(long n) throws IOException { - return underlyingStream.skip(n); + ensureOpened(); + if (eof) { + return 0; + } + int newPos = Math.max(pos, Math.min(accessor.size(), pos)); + int result = newPos - pos; + pos = newPos; + if (result == 0) { + accessor = null; + } + return result; } @Override public int available() throws IOException { - return underlyingStream.available(); + ensureOpened(); + if (eof) { + return 0; + } + return Math.max(0, accessor.size() - pos); } @Override public void close() throws IOException { - underlyingStream.close(); + accessor = null; } @Override public int read() throws IOException { - return underlyingStream.read(); + ensureOpened(); + if (eof) { + return -1; + } + byte[] buffer = new byte[1]; + int read = accessor.read(pos, buffer, 0, 1); + if (read == 0) { + eof = true; + } else { + pos++; + } + return !eof ? buffer[0] : -1; + } + + private void ensureOpened() throws IOException { + if (accessor == null) { + throw new IOException("This stream is already closed"); + } } } diff --git a/classlib/src/main/java/org/teavm/classlib/java/io/TFileOutputStream.java b/classlib/src/main/java/org/teavm/classlib/java/io/TFileOutputStream.java index c50650711..13c4f5909 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/io/TFileOutputStream.java +++ b/classlib/src/main/java/org/teavm/classlib/java/io/TFileOutputStream.java @@ -19,10 +19,13 @@ package org.teavm.classlib.java.io; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; +import java.util.Objects; import org.teavm.classlib.fs.VirtualFile; +import org.teavm.classlib.fs.VirtualFileAccessor; public class TFileOutputStream extends OutputStream { - private OutputStream underlyingStream; + private VirtualFileAccessor accessor; + private int pos; public TFileOutputStream(TFile file) throws FileNotFoundException { this(file, false); @@ -40,34 +43,47 @@ public class TFileOutputStream extends OutputStream { throw new FileNotFoundException(); } - underlyingStream = virtualFile.write(append); - if (underlyingStream == null) { + accessor = virtualFile.createAccessor(); + if (accessor == null) { throw new FileNotFoundException(); } + + if (append) { + pos = accessor.size(); + } } - @Override - public void write(byte[] b) throws IOException { - underlyingStream.write(b); - } - @Override public void write(byte[] b, int off, int len) throws IOException { - underlyingStream.write(b, off, len); + Objects.requireNonNull(b); + if (off < 0 || len < 0 || off + len > b.length) { + throw new IndexOutOfBoundsException(); + } + ensureOpened(); + accessor.write(pos, b, off, len); + pos += len; } @Override public void flush() throws IOException { - underlyingStream.flush(); } @Override public void close() throws IOException { - underlyingStream.close(); + accessor = null; } @Override public void write(int b) throws IOException { - underlyingStream.write(b); + ensureOpened(); + byte[] buffer = { (byte) b }; + accessor.write(pos, buffer, 0, 1); + pos++; + } + + private void ensureOpened() throws IOException { + if (accessor == null) { + throw new IOException("This stream is already closed"); + } } }