Improve virtual FS API

This commit is contained in:
Alexey Andreev 2017-11-13 23:22:54 +03:00
parent 99812d1bfa
commit 9ca09093a6
7 changed files with 150 additions and 122 deletions

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -366,6 +366,11 @@ public class TFile implements Serializable, Comparable<TFile> {
return path.hashCode();
}
@Override
public String toString() {
return path;
}
private static String fixSlashes(String origPath) {
int uncIndex = 0;
int length = origPath.length();

View File

@ -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");
}
}
}

View File

@ -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");
}
}
}