io.cpp 7.9 KB

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