From b034fa24bc127f7ba5f8b3782fe0247e212f452c Mon Sep 17 00:00:00 2001 From: Alessandro Proto Date: Fri, 13 Jan 2023 21:31:51 +0100 Subject: [PATCH] Stream handle objects (#2) --- .../LuaRuntime/Handlers/BinaryReadHandle.cs | 128 ++++++++++ .../LuaRuntime/Handlers/BinaryWriteHandle.cs | 127 ++++++++++ Capy64/LuaRuntime/Handlers/ReadHandle.cs | 143 +++++++++++ Capy64/LuaRuntime/Handlers/WriteHandle.cs | 126 ++++++++++ Capy64/LuaRuntime/Libraries/FileSystem.cs | 222 +----------------- Capy64/LuaRuntime/Libraries/HTTP.cs | 20 +- 6 files changed, 542 insertions(+), 224 deletions(-) create mode 100644 Capy64/LuaRuntime/Handlers/BinaryReadHandle.cs create mode 100644 Capy64/LuaRuntime/Handlers/BinaryWriteHandle.cs create mode 100644 Capy64/LuaRuntime/Handlers/ReadHandle.cs create mode 100644 Capy64/LuaRuntime/Handlers/WriteHandle.cs diff --git a/Capy64/LuaRuntime/Handlers/BinaryReadHandle.cs b/Capy64/LuaRuntime/Handlers/BinaryReadHandle.cs new file mode 100644 index 0000000..f87f5ce --- /dev/null +++ b/Capy64/LuaRuntime/Handlers/BinaryReadHandle.cs @@ -0,0 +1,128 @@ +using KeraLua; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Capy64.LuaRuntime.Handlers; + +[Obsolete("Work in progress")] +public class BinaryReadHandle +{ + private readonly BinaryReader _stream; + private bool isClosed = false; + private bool ended => _stream.BaseStream.Position == _stream.BaseStream.Length; + public BinaryReadHandle(Stream stream) + { + _stream = new BinaryReader(stream); + } + + public BinaryReadHandle(BinaryReader stream) + { + _stream = stream; + } + + /*public void Push(Lua L) + { + L.NewTable(); + + L.PushString("readAll"); + L.PushCFunction(L_ReadAll); + L.SetTable(-3); + + L.PushString("readLine"); + L.PushCFunction(L_ReadLine); + L.SetTable(-3); + + L.PushString("read"); + L.PushCFunction(L_Read); + L.SetTable(-3); + + L.PushString("close"); + L.PushCFunction(L_Close); + L.SetTable(-3); + } + + private int L_ReadAll(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + if (isClosed) + L.Error("handle is closed"); + + if (ended) + { + L.PushNil(); + return 1; + } + + //var content = _stream.read; + L.PushString(content); + + return 1; + } + + private int L_ReadLine(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + if (isClosed) + L.Error("handle is closed"); + + if (ended) + { + L.PushNil(); + return 1; + } + + var line = _stream.ReadLine(); + + if (line is null) + L.PushNil(); + else + L.PushString(line); + + return 1; + } + + private int L_Read(IntPtr state) + { + var L = Lua.FromIntPtr(state); + var count = (int)L.OptNumber(1, 1); + + L.ArgumentCheck(count < 1, 1, "count must be a positive integer"); + + if (ended) + L.Error("handle is closed"); + + if (_stream.EndOfStream) + { + L.PushNil(); + return 1; + } + + var chunk = new char[count]; + + _stream.Read(chunk, 0, count); + + L.PushString(new string(chunk)); + + return 1; + } + + private int L_Close(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + if (isClosed) + return 0; + + _stream.Close(); + + isClosed = true; + + return 0; + }*/ +} diff --git a/Capy64/LuaRuntime/Handlers/BinaryWriteHandle.cs b/Capy64/LuaRuntime/Handlers/BinaryWriteHandle.cs new file mode 100644 index 0000000..6a6e032 --- /dev/null +++ b/Capy64/LuaRuntime/Handlers/BinaryWriteHandle.cs @@ -0,0 +1,127 @@ +using KeraLua; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Capy64.LuaRuntime.Handlers; + +[Obsolete("Work in progress")] +public class BinaryWriteHandle +{ + private readonly BinaryWriter _stream; + private bool isClosed = false; + public BinaryWriteHandle(Stream stream) + { + _stream = new BinaryWriter(stream); + } + + public BinaryWriteHandle(BinaryWriter stream) + { + _stream = stream; + } + + /*public void Push(Lua L) + { + L.NewTable(); + + L.PushString("readAll"); + L.PushCFunction(L_ReadAll); + L.SetTable(-3); + + L.PushString("readLine"); + L.PushCFunction(L_ReadLine); + L.SetTable(-3); + + L.PushString("read"); + L.PushCFunction(L_Read); + L.SetTable(-3); + + L.PushString("close"); + L.PushCFunction(L_Close); + L.SetTable(-3); + } + + private int L_ReadAll(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + if (isClosed) + L.Error("handle is closed"); + + if (ended) + { + L.PushNil(); + return 1; + } + + //var content = _stream.read; + L.PushString(content); + + return 1; + } + + private int L_ReadLine(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + if (isClosed) + L.Error("handle is closed"); + + if (ended) + { + L.PushNil(); + return 1; + } + + var line = _stream.ReadLine(); + + if (line is null) + L.PushNil(); + else + L.PushString(line); + + return 1; + } + + private int L_Read(IntPtr state) + { + var L = Lua.FromIntPtr(state); + var count = (int)L.OptNumber(1, 1); + + L.ArgumentCheck(count < 1, 1, "count must be a positive integer"); + + if (ended) + L.Error("handle is closed"); + + if (_stream.EndOfStream) + { + L.PushNil(); + return 1; + } + + var chunk = new char[count]; + + _stream.Read(chunk, 0, count); + + L.PushString(new string(chunk)); + + return 1; + } + + private int L_Close(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + if (isClosed) + return 0; + + _stream.Close(); + + isClosed = true; + + return 0; + }*/ +} diff --git a/Capy64/LuaRuntime/Handlers/ReadHandle.cs b/Capy64/LuaRuntime/Handlers/ReadHandle.cs new file mode 100644 index 0000000..50e77dd --- /dev/null +++ b/Capy64/LuaRuntime/Handlers/ReadHandle.cs @@ -0,0 +1,143 @@ +using KeraLua; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace Capy64.LuaRuntime.Handlers; + +public class ReadHandle +{ + public readonly StreamReader Stream; + public bool IsClosed = false; + public ReadHandle(Stream stream) + { + Stream = new StreamReader(stream); + } + + public void Push(Lua L, bool newTable = true) + { + if (newTable) + L.NewTable(); + + L.PushString("readAll"); + L.PushCFunction(L_ReadAll); + L.SetTable(-3); + + L.PushString("readLine"); + L.PushCFunction(L_ReadLine); + L.SetTable(-3); + + L.PushString("read"); + L.PushCFunction(L_Read); + L.SetTable(-3); + + L.PushString("close"); + L.PushCFunction(L_Close); + L.SetTable(-3); + + L.PushString("_handle"); + L.PushObject(this); + L.SetTable(-3); + } + + private static int L_ReadAll(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Table); + L.PushString("_handle"); + L.GetTable(1); + var h = L.ToObject(-1, false); + + if (h is null || h.IsClosed) + L.Error("handle is closed"); + + if (h.Stream.EndOfStream) + { + L.PushNil(); + return 1; + } + + var content = h.Stream.ReadToEnd(); + L.PushString(content); + + return 1; + } + + private static int L_ReadLine(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Table); + L.PushString("_handle"); + L.GetTable(1); + var h = L.ToObject(-1, false); + + if (h is null || h.IsClosed) + L.Error("handle is closed"); + + var line = h.Stream.ReadLine(); + + if (line is null) + L.PushNil(); + else + L.PushString(line); + + return 1; + } + + private static int L_Read(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Table); + var count = (int)L.OptNumber(2, 1); + + L.PushString("_handle"); + L.GetTable(1); + var h = L.ToObject(-1, false); + + + L.ArgumentCheck(count >= 1, 2, "count must be a positive integer"); + + if (h is null || h.IsClosed) + L.Error("handle is closed"); + + if (h.Stream.EndOfStream) + { + L.PushNil(); + return 1; + } + + var chunk = new char[count]; + + h.Stream.Read(chunk, 0, count); + + L.PushString(new string(chunk)); + + return 1; + } + + private static int L_Close(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Table); + L.PushString("_handle"); + L.GetTable(1); + var h = L.ToObject(-1, true); + + if (h is null || h.IsClosed) + return 0; + + h.Stream.Close(); + + h.IsClosed = true; + + return 0; + } +} diff --git a/Capy64/LuaRuntime/Handlers/WriteHandle.cs b/Capy64/LuaRuntime/Handlers/WriteHandle.cs new file mode 100644 index 0000000..38eb398 --- /dev/null +++ b/Capy64/LuaRuntime/Handlers/WriteHandle.cs @@ -0,0 +1,126 @@ +using KeraLua; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Capy64.LuaRuntime.Handlers; + +public class WriteHandle +{ + public readonly StreamWriter Stream; + public bool IsClosed = false; + public WriteHandle(Stream stream) + { + Stream = new StreamWriter(stream); + } + + public WriteHandle(StreamWriter stream) + { + Stream = stream; + } + + public void Push(Lua L, bool newTable = true) + { + if (newTable) + L.NewTable(); + + L.PushString("write"); + L.PushCFunction(L_Write); + L.SetTable(-3); + + L.PushString("writeLine"); + L.PushCFunction(L_WriteLine); + L.SetTable(-3); + + L.PushString("flush"); + L.PushCFunction(L_Flush); + L.SetTable(-3); + + L.PushString("close"); + L.PushCFunction(L_Close); + L.SetTable(-3); + + L.PushString("_handle"); + L.PushObject(this); + L.SetTable(-3); + } + + private static int L_Write(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Table); + var content = L.CheckString(2); + + L.PushString("_handle"); + L.GetTable(1); + var h = L.ToObject(-1, false); + + if (h is null || h.IsClosed) + L.Error("handle is closed"); + + + h.Stream.Write(content); + + return 0; + } + + private static int L_WriteLine(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Table); + var content = L.CheckString(2); + + L.PushString("_handle"); + L.GetTable(1); + var h = L.ToObject(-1, false); + + if (h is null || h.IsClosed) + L.Error("handle is closed"); + + + h.Stream.WriteLine(content); + + return 0; + } + + private static int L_Flush(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Table); + L.PushString("_handle"); + L.GetTable(1); + var h = L.ToObject(-1, false); + + if (h is null || h.IsClosed) + L.Error("handle is closed"); + + h.Stream.Flush(); + + return 0; + } + + private static int L_Close(IntPtr state) + { + var L = Lua.FromIntPtr(state); + + L.CheckType(1, LuaType.Table); + L.PushString("_handle"); + L.GetTable(1); + var h = L.ToObject(-1, true); + + if (h is null || h.IsClosed) + return 0; + + h.Stream.Close(); + + h.IsClosed = true; + + return 0; + } +} diff --git a/Capy64/LuaRuntime/Libraries/FileSystem.cs b/Capy64/LuaRuntime/Libraries/FileSystem.cs index 84adf3c..ca9f8f0 100644 --- a/Capy64/LuaRuntime/Libraries/FileSystem.cs +++ b/Capy64/LuaRuntime/Libraries/FileSystem.cs @@ -1,5 +1,6 @@ using Capy64.API; using Capy64.LuaRuntime.Extensions; +using Capy64.LuaRuntime.Handlers; using KeraLua; using System; using System.Collections.Generic; @@ -512,225 +513,18 @@ public class FileSystem : IPlugin } } - var handle = File.Open(path, fileMode, fileAccess, FileShare.ReadWrite); - bool isClosed = false; + var fileStream = File.Open(path, fileMode, fileAccess, FileShare.ReadWrite); - - var functions = new Dictionary>(); + // todo: add binary mode if (fileAccess == FileAccess.Read) { - var reader = new StreamReader(handle); - functions["read"] = (IntPtr state) => - { - var L = Lua.FromIntPtr(state); - - var count = (int)L.OptNumber(1, 1); - - if (isClosed) - { - L.Error("file handle is closed"); - return 0; - } - - if (reader.EndOfStream) - { - L.PushNil(); - return 1; - } - - if (binaryMode) - { - var data = new byte[count]; - handle.Read(data, 0, count); - L.PushBuffer(data); - } - else - { - var data = new char[count]; - reader.ReadBlock(data, 0, count); - L.PushString(new string(data)); - } - - return 1; - }; - - functions["readLine"] = (IntPtr state) => - { - var L = Lua.FromIntPtr(state); - - var count = (int)L.OptNumber(1, 1); - - if (isClosed) - { - L.Error("file handle is closed"); - return 0; - } - - if (reader.EndOfStream) - { - L.PushNil(); - return 1; - } - - if (binaryMode) - { - var line = reader.ReadLine(); - L.PushBuffer(Encoding.UTF8.GetBytes(line)); - } - else - { - var line = reader.ReadLine(); - L.PushString(line); - } - - return 1; - }; - - functions["readAll"] = (IntPtr state) => - { - var L = Lua.FromIntPtr(state); - - var count = (int)L.OptNumber(1, 1); - - if (isClosed) - { - L.Error("file handle is closed"); - return 0; - } - - if (reader.EndOfStream) - { - L.PushNil(); - return 1; - } - - if (binaryMode) - { - var content = reader.ReadToEnd(); - L.PushBuffer(Encoding.UTF8.GetBytes(content)); - } - else - { - var content = reader.ReadToEnd(); - L.PushString(content); - } - - return 1; - }; - + var handle = new ReadHandle(fileStream); + handle.Push(L); } - else + else if (fileAccess == FileAccess.Write) { - var writer = new StreamWriter(handle); - functions["write"] = (IntPtr state) => - { - var L = Lua.FromIntPtr(state); - - L.ArgumentCheck(L.IsStringOrNumber(1), 1, "string or number value expected"); - - if (isClosed) - { - L.Error("file handle is closed"); - return 0; - } - - if (L.IsString(1)) - { - if (binaryMode) - { - handle.Write(L.ToBuffer(1)); - } - else - { - writer.Write(L.ToString(1)); - } - } - else - { - handle.WriteByte((byte)L.ToInteger(1)); - } - - return 0; - }; - - functions["writeLine"] = (IntPtr state) => - { - var L = Lua.FromIntPtr(state); - - L.ArgumentCheck(L.IsStringOrNumber(1), 1, "string or number value expected"); - - if (isClosed) - { - L.Error("file handle is closed"); - return 0; - } - - if (L.IsString(1)) - { - if (binaryMode) - { - handle.Write(L.ToBuffer(1)); - handle.WriteByte((byte)'\n'); - } - else - { - writer.WriteLine(L.ToString(1)); - } - } - else - { - handle.WriteByte((byte)L.ToInteger(1)); - handle.WriteByte((byte)'\n'); - } - - return 0; - }; - - functions["flush"] = (IntPtr state) => - { - var L = Lua.FromIntPtr(state); - - if (isClosed) - { - L.Error("file handle is closed"); - return 0; - } - - handle.Flush(); - - return 0; - }; - } - - - functions["close"] = (IntPtr state) => - { - var L = Lua.FromIntPtr(state); - - if (isClosed) - { - L.Error("file handle is closed"); - return 0; - } - - if (fileAccess != FileAccess.Read) - { - handle.Flush(); - } - handle.Dispose(); - - isClosed = true; - - return 0; - }; - - - L.NewTable(); - foreach (var pair in functions) - { - L.PushString(pair.Key); - L.PushCFunction(new LuaFunction(pair.Value)); - L.SetTable(-3); + var handle = new WriteHandle(fileStream); + handle.Push(L); } return 1; diff --git a/Capy64/LuaRuntime/Libraries/HTTP.cs b/Capy64/LuaRuntime/Libraries/HTTP.cs index efb6e19..d92aac6 100644 --- a/Capy64/LuaRuntime/Libraries/HTTP.cs +++ b/Capy64/LuaRuntime/Libraries/HTTP.cs @@ -1,8 +1,10 @@ using Capy64.API; using Capy64.LuaRuntime.Extensions; +using Capy64.LuaRuntime.Handlers; using KeraLua; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net.Http; @@ -13,6 +15,7 @@ public class HTTP : IPlugin private static IGame _game; private static HttpClient _client; private static long RequestId; + private static List ReadHandles = new(); private readonly LuaRegister[] HttpLib = new LuaRegister[] { @@ -151,12 +154,14 @@ public class HTTP : IPlugin reqTask.ContinueWith(async (task) => { var response = await task; - object content; + /*object content; if ((bool)options["binary"]) content = await response.Content.ReadAsByteArrayAsync(); else - content = await response.Content.ReadAsStringAsync(); + content = await response.Content.ReadAsStringAsync();*/ + var stream = await response.Content.ReadAsStreamAsync(); + var handler = new ReadHandle(stream); _game.LuaRuntime.PushEvent("http_response", L => { @@ -178,17 +183,10 @@ public class HTTP : IPlugin L.PushString(response.ReasonPhrase); L.SetTable(-3); - L.PushString("content"); - if ((bool)options["binary"]) - L.PushBuffer((byte[])content); - else - L.PushString((string)content); - L.SetTable(-3); - L.PushString("headers"); L.NewTable(); - foreach(var header in response.Headers) + foreach (var header in response.Headers) { L.PushString(header.Key); L.PushArray(header.Value.ToArray()); @@ -197,6 +195,8 @@ public class HTTP : IPlugin L.SetTable(-3); + handler.Push(L, false); + return 2; });