IOHelper.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. using System.Text;
  2. using Lua.Internal;
  3. using Lua.IO;
  4. namespace Lua.Standard.Internal;
  5. internal static class IOHelper
  6. {
  7. public static int Open(LuaThread thread, string fileName, string mode, bool throwError)
  8. {
  9. var fileMode = mode switch
  10. {
  11. "r" or "rb" => LuaFileOpenMode.Read,
  12. "w" or "wb" => LuaFileOpenMode.Write,
  13. "a" or "ab" => LuaFileOpenMode.Append,
  14. "r+" or "rb+" => LuaFileOpenMode.ReadWriteOpen,
  15. "w+" or "wb+" => LuaFileOpenMode.ReadWriteCreate,
  16. "a+" or "ab+" => LuaFileOpenMode.ReadAppend,
  17. _ => throw new LuaRuntimeException(thread, "bad argument #2 to 'open' (invalid mode)"),
  18. };
  19. var binary = mode.Contains("b");
  20. if (binary) throw new LuaRuntimeException(thread, "binary mode is not supported");
  21. try
  22. {
  23. var stream = thread.State.FileSystem.Open(fileName, fileMode);
  24. thread.Stack.Push(new LuaValue(new FileHandle(stream)));
  25. return 1;
  26. }
  27. catch (IOException ex)
  28. {
  29. if (throwError)
  30. {
  31. throw;
  32. }
  33. thread.Stack.Push(LuaValue.Nil);
  34. thread.Stack.Push(ex.Message);
  35. thread.Stack.Push(ex.HResult);
  36. return 3;
  37. }
  38. }
  39. // TODO: optimize (use IBuffertWrite<byte>, async)
  40. public static async ValueTask<int> WriteAsync(FileHandle file, string name, LuaFunctionExecutionContext context, CancellationToken cancellationToken)
  41. {
  42. try
  43. {
  44. for (int i = 0; i < context.ArgumentCount; i++)
  45. {
  46. var arg = context.Arguments[i];
  47. if (arg.TryRead<string>(out var str))
  48. {
  49. await file.WriteAsync(str.AsMemory(), cancellationToken);
  50. }
  51. else if (arg.TryRead<double>(out var d))
  52. {
  53. using var fileBuffer = new PooledArray<char>(64);
  54. var span = fileBuffer.AsSpan();
  55. d.TryFormat(span, out var charsWritten);
  56. await file.WriteAsync(fileBuffer.AsMemory()[..charsWritten], cancellationToken);
  57. }
  58. else
  59. {
  60. LuaRuntimeException.BadArgument(context.Thread, i + 1, name);
  61. }
  62. }
  63. }
  64. catch (IOException ex)
  65. {
  66. context.Thread.Stack.PopUntil(context.ReturnFrameBase);
  67. var stack = context.Thread.Stack;
  68. stack.Push(LuaValue.Nil);
  69. stack.Push(ex.Message);
  70. stack.Push(ex.HResult);
  71. return 3;
  72. }
  73. context.Thread.Stack.PopUntil(context.ReturnFrameBase);
  74. context.Thread.Stack.Push(new(file));
  75. return 1;
  76. }
  77. static readonly LuaValue[] defaultReadFormat = ["*l"];
  78. public static async ValueTask<int> ReadAsync(LuaThread thread, FileHandle file, string name, int startArgumentIndex, ReadOnlyMemory<LuaValue> formats, bool throwError, CancellationToken cancellationToken)
  79. {
  80. if (formats.Length == 0)
  81. {
  82. formats = defaultReadFormat;
  83. }
  84. var stack = thread.Stack;
  85. var top = stack.Count;
  86. try
  87. {
  88. for (int i = 0; i < formats.Length; i++)
  89. {
  90. var format = formats.Span[i];
  91. if (format.TryRead<string>(out var str))
  92. {
  93. switch (str)
  94. {
  95. case "*n":
  96. case "*number":
  97. // TODO: support number format
  98. throw new NotImplementedException();
  99. case "*a":
  100. case "*all":
  101. stack.Push(await file.ReadToEndAsync(cancellationToken));
  102. break;
  103. case "*l":
  104. case "*line":
  105. stack.Push(await file.ReadLineAsync(cancellationToken) ?? LuaValue.Nil);
  106. break;
  107. case "L":
  108. case "*L":
  109. var text = await file.ReadLineAsync(cancellationToken);
  110. stack.Push(text == null ? LuaValue.Nil : text + Environment.NewLine);
  111. break;
  112. }
  113. }
  114. else if (format.TryRead<int>(out var count))
  115. {
  116. var ret = await file.ReadStringAsync(count, cancellationToken);
  117. if (ret == null)
  118. {
  119. stack.PopUntil(top);
  120. stack.Push(default);
  121. return 1;
  122. }
  123. else
  124. {
  125. stack.Push(ret);
  126. }
  127. }
  128. else
  129. {
  130. LuaRuntimeException.BadArgument(thread, i + 1, name);
  131. }
  132. }
  133. return formats.Length;
  134. }
  135. catch (IOException ex)
  136. {
  137. if (throwError)
  138. {
  139. throw;
  140. }
  141. stack.PopUntil(top);
  142. stack.Push(LuaValue.Nil);
  143. stack.Push(ex.Message);
  144. stack.Push(ex.HResult);
  145. return 3;
  146. }
  147. }
  148. }