IOHelper.cs 5.2 KB

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