1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-14 12:10:30 +00:00

Make Mount.openForRead always return a SeekableByteChannel

I want to make some further changes to Mount here, but this helps
simplify BinaryReadableHandle a little.
This commit is contained in:
Jonathan Coates 2022-12-03 18:20:50 +00:00
parent c96172e78d
commit fc5f296eeb
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
8 changed files with 53 additions and 68 deletions

View File

@ -8,7 +8,7 @@ package dan200.computercraft.api.filesystem;
import dan200.computercraft.api.peripheral.IComputerAccess;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
@ -60,15 +60,13 @@ public interface Mount {
long getSize(String path) throws IOException;
/**
* Opens a file with a given path, and returns an {@link ReadableByteChannel} representing its contents.
* Opens a file with a given path, and returns a {@link SeekableByteChannel} representing its contents.
*
* @param path A file path in normalised format, relative to the mount location. ie: "programs/myprogram".
* @return A channel representing the contents of the file. If the channel implements
* {@link java.nio.channels.SeekableByteChannel}, one will be able to seek to arbitrary positions when using binary
* mode.
* @return A channel representing the contents of the file.
* @throws IOException If the file does not exist, or could not be opened.
*/
ReadableByteChannel openForRead(String path) throws IOException;
SeekableByteChannel openForRead(String path) throws IOException;
/**
* Get attributes about the given file.

View File

@ -8,13 +8,11 @@ package dan200.computercraft.core.apis.handles;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.core.filesystem.TrackingCloseable;
import dan200.computercraft.core.util.Nullability;
import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.util.ArrayList;
import java.util.List;
@ -29,22 +27,19 @@ import java.util.Optional;
public class BinaryReadableHandle extends HandleGeneric {
private static final int BUFFER_SIZE = 8192;
private final ReadableByteChannel reader;
final @Nullable SeekableByteChannel seekable;
private final SeekableByteChannel channel;
private final ByteBuffer single = ByteBuffer.allocate(1);
BinaryReadableHandle(ReadableByteChannel reader, @Nullable SeekableByteChannel seekable, TrackingCloseable closeable) {
BinaryReadableHandle(SeekableByteChannel channel, TrackingCloseable closeable) {
super(closeable);
this.reader = reader;
this.seekable = seekable;
this.channel = channel;
}
public static BinaryReadableHandle of(ReadableByteChannel channel, TrackingCloseable closeable) {
var seekable = asSeekable(channel);
return seekable == null ? new BinaryReadableHandle(channel, null, closeable) : new Seekable(seekable, closeable);
public static BinaryReadableHandle of(SeekableByteChannel channel, TrackingCloseable closeable) {
return new BinaryReadableHandle(channel, closeable);
}
public static BinaryReadableHandle of(ReadableByteChannel channel) {
public static BinaryReadableHandle of(SeekableByteChannel channel) {
return of(channel, new TrackingCloseable.Impl(channel));
}
@ -69,21 +64,19 @@ public class BinaryReadableHandle extends HandleGeneric {
if (countArg.isPresent()) {
int count = countArg.get();
if (count < 0) throw new LuaException("Cannot read a negative number of bytes");
if (count == 0 && seekable != null) {
return seekable.position() >= seekable.size() ? null : new Object[]{ "" };
}
if (count == 0) return channel.position() >= channel.size() ? null : new Object[]{ "" };
if (count <= BUFFER_SIZE) {
var buffer = ByteBuffer.allocate(count);
var read = reader.read(buffer);
var read = channel.read(buffer);
if (read < 0) return null;
buffer.flip();
return new Object[]{ buffer };
} else {
// Read the initial set of characters, failing if none are read.
var buffer = ByteBuffer.allocate(BUFFER_SIZE);
var read = reader.read(buffer);
var read = channel.read(buffer);
if (read < 0) return null;
// If we failed to read "enough" here, let's just abort
@ -99,7 +92,7 @@ public class BinaryReadableHandle extends HandleGeneric {
parts.add(buffer);
while (read >= BUFFER_SIZE && totalRead < count) {
buffer = ByteBuffer.allocate(Math.min(BUFFER_SIZE, count - totalRead));
read = reader.read(buffer);
read = channel.read(buffer);
if (read < 0) break;
totalRead += read;
@ -117,7 +110,7 @@ public class BinaryReadableHandle extends HandleGeneric {
}
} else {
single.clear();
var b = reader.read(single);
var b = channel.read(single);
return b == -1 ? null : new Object[]{ single.get(0) & 0xFF };
}
} catch (IOException e) {
@ -139,14 +132,14 @@ public class BinaryReadableHandle extends HandleGeneric {
checkOpen();
try {
var expected = 32;
if (seekable != null) expected = Math.max(expected, (int) (seekable.size() - seekable.position()));
expected = Math.max(expected, (int) (channel.size() - channel.position()));
var stream = new ByteArrayOutputStream(expected);
var buf = ByteBuffer.allocate(8192);
var readAnything = false;
while (true) {
buf.clear();
var r = reader.read(buf);
var r = channel.read(buf);
if (r == -1) break;
readAnything = true;
@ -179,7 +172,7 @@ public class BinaryReadableHandle extends HandleGeneric {
boolean readAnything = false, readRc = false;
while (true) {
single.clear();
var read = reader.read(single);
var read = channel.read(single);
if (read <= 0) {
// Nothing else to read, and we saw no \n. Return the array. If we saw a \r, then add it
// back.
@ -211,35 +204,29 @@ public class BinaryReadableHandle extends HandleGeneric {
}
}
public static class Seekable extends BinaryReadableHandle {
Seekable(SeekableByteChannel seekable, TrackingCloseable closeable) {
super(seekable, seekable, closeable);
}
/**
* Seek to a new position within the file, changing where bytes are written to. The new position is an offset
* given by {@code offset}, relative to a start position determined by {@code whence}:
* <p>
* - {@code "set"}: {@code offset} is relative to the beginning of the file.
* - {@code "cur"}: Relative to the current position. This is the default.
* - {@code "end"}: Relative to the end of the file.
* <p>
* In case of success, {@code seek} returns the new file position from the beginning of the file.
*
* @param whence Where the offset is relative to.
* @param offset The offset to seek to.
* @return The new position.
* @throws LuaException If the file has been closed.
* @cc.treturn [1] number The new position.
* @cc.treturn [2] nil If seeking failed.
* @cc.treturn string The reason seeking failed.
* @cc.since 1.80pr1.9
*/
@Nullable
@LuaFunction
public final Object[] seek(Optional<String> whence, Optional<Long> offset) throws LuaException {
checkOpen();
return handleSeek(Nullability.assertNonNull(seekable), whence, offset);
}
/**
* Seek to a new position within the file, changing where bytes are written to. The new position is an offset
* given by {@code offset}, relative to a start position determined by {@code whence}:
* <p>
* - {@code "set"}: {@code offset} is relative to the beginning of the file.
* - {@code "cur"}: Relative to the current position. This is the default.
* - {@code "end"}: Relative to the end of the file.
* <p>
* In case of success, {@code seek} returns the new file position from the beginning of the file.
*
* @param whence Where the offset is relative to.
* @param offset The offset to seek to.
* @return The new position.
* @throws LuaException If the file has been closed.
* @cc.treturn [1] number The new position.
* @cc.treturn [2] nil If seeking failed.
* @cc.treturn string The reason seeking failed.
* @cc.since 1.80pr1.9
*/
@Nullable
@LuaFunction
public final Object[] seek(Optional<String> whence, Optional<Long> offset) throws LuaException {
checkOpen();
return handleSeek(channel, whence, offset);
}
}

View File

@ -14,7 +14,7 @@ import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.Map;
@ -110,7 +110,7 @@ public abstract class ArchiveMount<T extends ArchiveMount.FileEntry<T>> implemen
protected abstract long getSize(T file) throws IOException;
@Override
public ReadableByteChannel openForRead(String path) throws IOException {
public SeekableByteChannel openForRead(String path) throws IOException {
var file = get(path);
if (file == null || file.isDirectory()) throw new FileOperationException(path, NO_SUCH_FILE);

View File

@ -182,7 +182,7 @@ public class FileMount implements WritableMount {
}
@Override
public ReadableByteChannel openForRead(String path) throws IOException {
public SeekableByteChannel openForRead(String path) throws IOException {
if (created()) {
var file = getRealPath(path);
if (file.exists() && !file.isDirectory()) return FileChannel.open(file.toPath(), READ_OPTIONS);

View File

@ -18,7 +18,7 @@ import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.nio.channels.Channel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.AccessDeniedException;
import java.nio.file.attribute.BasicFileAttributes;
@ -346,7 +346,7 @@ public class FileSystem {
}
}
public synchronized <T extends Closeable> FileSystemWrapper<T> openForRead(String path, Function<ReadableByteChannel, T> open) throws FileSystemException {
public synchronized <T extends Closeable> FileSystemWrapper<T> openForRead(String path, Function<SeekableByteChannel, T> open) throws FileSystemException {
cleanup();
path = sanitizePath(path);

View File

@ -11,7 +11,7 @@ import dan200.computercraft.api.filesystem.WritableMount;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.AccessDeniedException;
import java.nio.file.attribute.BasicFileAttributes;
@ -120,7 +120,7 @@ class MountWrapper {
}
}
public ReadableByteChannel openForRead(String path) throws FileSystemException {
public SeekableByteChannel openForRead(String path) throws FileSystemException {
path = toLocal(path);
try {
if (mount.exists(path) && !mount.isDirectory(path)) {

View File

@ -8,7 +8,7 @@ package dan200.computercraft.core.filesystem;
import dan200.computercraft.api.filesystem.Mount;
import java.io.IOException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
@ -42,7 +42,7 @@ public class SubMount implements Mount {
}
@Override
public ReadableByteChannel openForRead(String path) throws IOException {
public SeekableByteChannel openForRead(String path) throws IOException {
return parent.openForRead(getFullPath(path));
}

View File

@ -13,7 +13,7 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.*;
@ -111,7 +111,7 @@ public class MemoryMount implements WritableMount {
}
@Override
public ReadableByteChannel openForRead(String path) throws FileOperationException {
public SeekableByteChannel openForRead(String path) throws FileOperationException {
var file = files.get(path);
if (file == null) throw new FileOperationException(path, "File not found");
return new ArrayByteChannel(file);