using System.Text; using Lua.Internal; namespace Lua.Standard.Internal; internal static class IOHelper { public static int Open(LuaState state, string fileName, string mode, Memory buffer, bool throwError) { var fileMode = mode switch { "r" or "rb" or "r+" or "r+b" => FileMode.Open, "w" or "wb" or "w+" or "w+b" => FileMode.Create, "a" or "ab" or "a+" or "a+b" => FileMode.Append, _ => throw new LuaRuntimeException(state.GetTraceback(), "bad argument #2 to 'open' (invalid mode)"), }; var fileAccess = mode switch { "r" or "rb" => FileAccess.Read, "w" or "wb" or "a" or "ab" => FileAccess.Write, _ => FileAccess.ReadWrite, }; try { var stream = File.Open(fileName, fileMode, fileAccess); buffer.Span[0] = new LuaValue(new FileHandle(stream)); return 1; } catch (IOException ex) { if (throwError) { throw; } buffer.Span[0] = LuaValue.Nil; buffer.Span[1] = ex.Message; buffer.Span[2] = ex.HResult; return 3; } } // TODO: optimize (use IBuffertWrite, async) public static int Write(FileHandle file, string name, LuaFunctionExecutionContext context, Memory buffer) { try { for (int i = 1; i < context.ArgumentCount; i++) { var arg = context.Arguments[i]; if (arg.TryRead(out var str)) { file.Write(str); } else if (arg.TryRead(out var d)) { using var fileBuffer = new PooledArray(64); var span = fileBuffer.AsSpan(); d.TryFormat(span, out var charsWritten); file.Write(span[..charsWritten]); } else { LuaRuntimeException.BadArgument(context.State.GetTraceback(), i + 1, name); } } } catch (IOException ex) { buffer.Span[0] = LuaValue.Nil; buffer.Span[1] = ex.Message; buffer.Span[2] = ex.HResult; return 3; } buffer.Span[0] = new(file); return 1; } static readonly LuaValue[] defaultReadFormat = ["*l"]; public static int Read(LuaState state, FileHandle file, string name, int startArgumentIndex, ReadOnlySpan formats, Memory buffer, bool throwError) { if (formats.Length == 0) { formats = defaultReadFormat; } try { for (int i = 0; i < formats.Length; i++) { var format = formats[i]; if (format.TryRead(out var str)) { switch (str) { case "*n": case "*number": // TODO: support number format throw new NotImplementedException(); case "*a": case "*all": buffer.Span[i] = file.ReadToEnd(); break; case "*l": case "*line": buffer.Span[i] = file.ReadLine() ?? LuaValue.Nil; break; case "L": case "*L": var text = file.ReadLine(); buffer.Span[i] = text == null ? LuaValue.Nil : text + Environment.NewLine; break; } } else if (format.TryRead(out var count)) { using var byteBuffer = new PooledArray(count); for (int j = 0; j < count; j++) { var b = file.ReadByte(); if (b == -1) { buffer.Span[0] = LuaValue.Nil; return 1; } byteBuffer[j] = (byte)b; } buffer.Span[i] = Encoding.UTF8.GetString(byteBuffer.AsSpan()); } else { LuaRuntimeException.BadArgument(state.GetTraceback(), i + 1, name); } } return formats.Length; } catch (IOException ex) { if (throwError) { throw; } buffer.Span[0] = LuaValue.Nil; buffer.Span[1] = ex.Message; buffer.Span[2] = ex.HResult; return 3; } } }