io.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #include "pocketpy/io.h"
  2. #if PK_ENABLE_OS
  3. #include <filesystem>
  4. #include <cstdio>
  5. #endif
  6. namespace pkpy{
  7. #if PK_ENABLE_OS
  8. struct FileIO {
  9. FILE* fp;
  10. bool is_text;
  11. FileIO(VM* vm, const Str& file, const Str& mode);
  12. void close();
  13. static void _register(VM* vm, PyObject* mod, PyObject* type);
  14. };
  15. static FILE* io_fopen(const char* name, const char* mode){
  16. #if _MSC_VER
  17. FILE* fp;
  18. errno_t err = fopen_s(&fp, name, mode);
  19. if(err != 0) return nullptr;
  20. return fp;
  21. #else
  22. return fopen(name, mode);
  23. #endif
  24. }
  25. static size_t io_fread(void* buffer, size_t size, size_t count, FILE* fp){
  26. #if _MSC_VER
  27. return fread_s(buffer, std::numeric_limits<size_t>::max(), size, count, fp);
  28. #else
  29. return fread(buffer, size, count, fp);
  30. #endif
  31. }
  32. unsigned char* _default_import_handler(const char* name_p, int name_size, int* out_size){
  33. std::string name(name_p, name_size);
  34. bool exists = std::filesystem::exists(std::filesystem::path(name));
  35. if(!exists) return nullptr;
  36. FILE* fp = io_fopen(name.c_str(), "rb");
  37. if(!fp) return nullptr;
  38. fseek(fp, 0, SEEK_END);
  39. int buffer_size = ftell(fp);
  40. unsigned char* buffer = new unsigned char[buffer_size];
  41. fseek(fp, 0, SEEK_SET);
  42. size_t sz = io_fread(buffer, 1, buffer_size, fp);
  43. (void)sz; // suppress warning
  44. fclose(fp);
  45. *out_size = buffer_size;
  46. return buffer;
  47. };
  48. void FileIO::_register(VM* vm, PyObject* mod, PyObject* type){
  49. vm->bind_constructor<3>(type, [](VM* vm, ArgsView args){
  50. Type cls = PK_OBJ_GET(Type, args[0]);
  51. return vm->heap.gcnew<FileIO>(cls, vm,
  52. py_cast<Str&>(vm, args[1]),
  53. py_cast<Str&>(vm, args[2]));
  54. });
  55. vm->bind(type, "read(self, size=-1)", [](VM* vm, ArgsView args){
  56. FileIO& io = PK_OBJ_GET(FileIO, args[0]);
  57. i64 size = CAST(i64, args[1]);
  58. i64 buffer_size;
  59. if(size < 0){
  60. long current = ftell(io.fp);
  61. fseek(io.fp, 0, SEEK_END);
  62. buffer_size = ftell(io.fp);
  63. fseek(io.fp, current, SEEK_SET);
  64. }else{
  65. buffer_size = size;
  66. }
  67. unsigned char* buffer = new unsigned char[buffer_size];
  68. i64 actual_size = io_fread(buffer, 1, buffer_size, io.fp);
  69. PK_ASSERT(actual_size <= buffer_size);
  70. // in text mode, CR may be dropped, which may cause `actual_size < buffer_size`
  71. Bytes b(buffer, actual_size);
  72. if(io.is_text) return VAR(b.str());
  73. return VAR(std::move(b));
  74. });
  75. vm->bind_func(type, "write", 2, [](VM* vm, ArgsView args){
  76. FileIO& io = PK_OBJ_GET(FileIO, args[0]);
  77. if(io.is_text){
  78. Str& s = CAST(Str&, args[1]);
  79. fwrite(s.data, 1, s.length(), io.fp);
  80. }else{
  81. Bytes& buffer = CAST(Bytes&, args[1]);
  82. fwrite(buffer.data(), 1, buffer.size(), io.fp);
  83. }
  84. return vm->None;
  85. });
  86. vm->bind_func(type, "tell", 1, [](VM* vm, ArgsView args){
  87. FileIO& io = PK_OBJ_GET(FileIO, args[0]);
  88. long pos = ftell(io.fp);
  89. if(pos == -1) vm->IOError(strerror(errno));
  90. return VAR(pos);
  91. });
  92. vm->bind_func(type, "seek", 3, [](VM* vm, ArgsView args){
  93. FileIO& io = PK_OBJ_GET(FileIO, args[0]);
  94. long offset = CAST(long, args[1]);
  95. int whence = CAST(int, args[2]);
  96. int ret = fseek(io.fp, offset, whence);
  97. if(ret != 0) vm->IOError(strerror(errno));
  98. return vm->None;
  99. });
  100. vm->bind_func(type, "close", 1, [](VM* vm, ArgsView args){
  101. FileIO& io = PK_OBJ_GET(FileIO, args[0]);
  102. io.close();
  103. return vm->None;
  104. });
  105. vm->bind_func(type, __exit__, 1, [](VM* vm, ArgsView args){
  106. FileIO& io = PK_OBJ_GET(FileIO, args[0]);
  107. io.close();
  108. return vm->None;
  109. });
  110. vm->bind_func(type, __enter__, 1, PK_LAMBDA(args[0]));
  111. }
  112. FileIO::FileIO(VM* vm, const Str& file, const Str& mode){
  113. this->is_text = mode.sv().find("b") == std::string::npos;
  114. fp = io_fopen(file.c_str(), mode.c_str());
  115. if(!fp) vm->IOError(strerror(errno));
  116. }
  117. void FileIO::close(){
  118. if(fp == nullptr) return;
  119. fclose(fp);
  120. fp = nullptr;
  121. }
  122. void add_module_io(VM* vm){
  123. PyObject* mod = vm->new_module("io");
  124. vm->register_user_class<FileIO>(mod, "FileIO");
  125. mod->attr().set("SEEK_SET", VAR(SEEK_SET));
  126. mod->attr().set("SEEK_CUR", VAR(SEEK_CUR));
  127. mod->attr().set("SEEK_END", VAR(SEEK_END));
  128. vm->bind(vm->builtins, "open(path, mode='r')", [](VM* vm, ArgsView args){
  129. PK_LOCAL_STATIC StrName m_io("io");
  130. PK_LOCAL_STATIC StrName m_FileIO("FileIO");
  131. return vm->call(vm->_modules[m_io]->attr(m_FileIO), args[0], args[1]);
  132. });
  133. }
  134. void add_module_os(VM* vm){
  135. PyObject* mod = vm->new_module("os");
  136. PyObject* path_obj = vm->heap.gcnew<DummyInstance>(vm->tp_object);
  137. mod->attr().set("path", path_obj);
  138. // Working directory is shared by all VMs!!
  139. vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){
  140. return VAR(std::filesystem::current_path().string());
  141. });
  142. vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){
  143. std::filesystem::path path(CAST(Str&, args[0]).sv());
  144. std::filesystem::current_path(path);
  145. return vm->None;
  146. });
  147. vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){
  148. std::filesystem::path path(CAST(Str&, args[0]).sv());
  149. std::filesystem::directory_iterator di;
  150. try{
  151. di = std::filesystem::directory_iterator(path);
  152. }catch(std::filesystem::filesystem_error&){
  153. vm->IOError(path.string());
  154. }
  155. List ret;
  156. for(auto& p: di) ret.push_back(VAR(p.path().filename().string()));
  157. return VAR(ret);
  158. });
  159. vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){
  160. std::filesystem::path path(CAST(Str&, args[0]).sv());
  161. bool ok = std::filesystem::remove(path);
  162. if(!ok) vm->IOError("operation failed");
  163. return vm->None;
  164. });
  165. vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){
  166. std::filesystem::path path(CAST(Str&, args[0]).sv());
  167. bool ok = std::filesystem::create_directory(path);
  168. if(!ok) vm->IOError("operation failed");
  169. return vm->None;
  170. });
  171. vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){
  172. std::filesystem::path path(CAST(Str&, args[0]).sv());
  173. bool ok = std::filesystem::remove(path);
  174. if(!ok) vm->IOError("operation failed");
  175. return vm->None;
  176. });
  177. vm->bind_func<-1>(path_obj, "join", [](VM* vm, ArgsView args){
  178. std::filesystem::path path;
  179. for(int i=0; i<args.size(); i++){
  180. path /= CAST(Str&, args[i]).sv();
  181. }
  182. return VAR(path.string());
  183. });
  184. vm->bind_func<1>(path_obj, "exists", [](VM* vm, ArgsView args){
  185. std::filesystem::path path(CAST(Str&, args[0]).sv());
  186. bool exists = std::filesystem::exists(path);
  187. return VAR(exists);
  188. });
  189. vm->bind_func<1>(path_obj, "basename", [](VM* vm, ArgsView args){
  190. std::filesystem::path path(CAST(Str&, args[0]).sv());
  191. return VAR(path.filename().string());
  192. });
  193. vm->bind_func<1>(path_obj, "isdir", [](VM* vm, ArgsView args){
  194. std::filesystem::path path(CAST(Str&, args[0]).sv());
  195. bool isdir = std::filesystem::is_directory(path);
  196. return VAR(isdir);
  197. });
  198. vm->bind_func<1>(path_obj, "isfile", [](VM* vm, ArgsView args){
  199. std::filesystem::path path(CAST(Str&, args[0]).sv());
  200. bool isfile = std::filesystem::is_regular_file(path);
  201. return VAR(isfile);
  202. });
  203. vm->bind_func<1>(path_obj, "abspath", [](VM* vm, ArgsView args){
  204. std::filesystem::path path(CAST(Str&, args[0]).sv());
  205. return VAR(std::filesystem::absolute(path).string());
  206. });
  207. }
  208. #else
  209. void add_module_io(VM* vm){}
  210. void add_module_os(VM* vm){}
  211. unsigned char* _default_import_handler(const char* name_p, int name_size, int* out_size){
  212. return nullptr;
  213. }
  214. #endif
  215. } // namespace pkpy