IOHelper.cs 4.9 KB

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