/* * This file is part of ComputerCraft - http://www.computercraft.info * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ package dan200.computercraft.core.apis.handles; import dan200.computercraft.api.lua.IArguments; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.lua.LuaValues; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SeekableByteChannel; import java.nio.channels.WritableByteChannel; import java.util.Optional; /** * A file handle opened by {@link dan200.computercraft.core.apis.FSAPI#open} using the {@code "wb"} or {@code "ab"} * modes. * * @cc.module fs.BinaryWriteHandle */ public class BinaryWritableHandle extends HandleGeneric { private final WritableByteChannel writer; final SeekableByteChannel seekable; private final ByteBuffer single = ByteBuffer.allocate( 1 ); protected BinaryWritableHandle( WritableByteChannel writer, SeekableByteChannel seekable, Closeable closeable ) { super( closeable ); this.writer = writer; this.seekable = seekable; } public static BinaryWritableHandle of( WritableByteChannel channel, Closeable closeable ) { SeekableByteChannel seekable = asSeekable( channel ); return seekable == null ? new BinaryWritableHandle( channel, null, closeable ) : new Seekable( seekable, closeable ); } public static BinaryWritableHandle of( WritableByteChannel channel ) { return of( channel, channel ); } /** * Write a string or byte to the file. * * @param arguments The value to write. * @throws LuaException If the file has been closed. * @cc.tparam [1] number The byte to write. * @cc.tparam [2] string The string to write. */ @LuaFunction public final void write( IArguments arguments ) throws LuaException { checkOpen(); try { Object arg = arguments.get( 0 ); if( arg instanceof Number ) { int number = ((Number) arg).intValue(); single.clear(); single.put( (byte) number ); single.flip(); writer.write( single ); } else if( arg instanceof String ) { writer.write( arguments.getBytes( 0 ) ); } else { throw LuaValues.badArgumentOf( 0, "string or number", arg ); } } catch( IOException e ) { throw new LuaException( e.getMessage() ); } } /** * Save the current file without closing it. * * @throws LuaException If the file has been closed. */ @LuaFunction public final void flush() throws LuaException { checkOpen(); try { // Technically this is not needed if( writer instanceof FileChannel ) ((FileChannel) writer).force( false ); } catch( IOException ignored ) { } } public static class Seekable extends BinaryWritableHandle { public Seekable( SeekableByteChannel seekable, Closeable 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}: * * - {@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. * * 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. */ @LuaFunction public final Object[] seek( Optional whence, Optional offset ) throws LuaException { checkOpen(); return handleSeek( seekable, whence, offset ); } } }