From d0f3ec887e36ef2716fd305bcfb2634e4bbcbbd6 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Fri, 3 Jul 2020 20:06:13 +0200 Subject: [PATCH] [mpy/files] Added ion.file --- ion/include/ion/storage.h | 4 +- python/Makefile | 1 + python/port/genhdr/qstrdefs.in.h | 4 + python/port/mod/ion/file.cpp | 265 +++++++++++++++++++++++++++ python/port/mod/ion/modion.h | 1 + python/port/mod/ion/modion_table.cpp | 2 + 6 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 python/port/mod/ion/file.cpp diff --git a/ion/include/ion/storage.h b/ion/include/ion/storage.h index e18607612..070cc5366 100644 --- a/ion/include/ion/storage.h +++ b/ion/include/ion/storage.h @@ -120,6 +120,9 @@ public: void destroyRecordWithBaseNameAndExtension(const char * baseName, const char * extension); void destroyRecordsWithExtension(const char * extension); + // Useful + static bool FullNameCompliant(const char * name); + private: constexpr static uint32_t Magic = 0xEE0BDDBA; constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8); @@ -149,7 +152,6 @@ private: bool isFullNameTaken(const char * fullName, const Record * recordToExclude = nullptr); bool isBaseNameWithExtensionTaken(const char * baseName, const char * extension, Record * recordToExclude = nullptr); bool isNameOfRecordTaken(Record r, const Record * recordToExclude); - static bool FullNameCompliant(const char * name); char * endBuffer(); size_t sizeOfBaseNameAndExtension(const char * baseName, const char * extension) const; size_t sizeOfRecordWithBaseNameAndExtension(const char * baseName, const char * extension, size_t size) const; diff --git a/python/Makefile b/python/Makefile index 503d7fad1..14a556cd8 100644 --- a/python/Makefile +++ b/python/Makefile @@ -136,6 +136,7 @@ port_src += $(addprefix python/port/,\ helpers.c \ mod/ion/modion.cpp \ mod/ion/modion_table.cpp \ + mod/ion/file.cpp \ mod/kandinsky/modkandinsky.cpp \ mod/kandinsky/modkandinsky_table.c \ mod/matplotlib/modmatplotlib.cpp \ diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 6670adc17..2650ca5ab 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -493,3 +493,7 @@ Q(colormode) Q(time) Q(sleep) Q(monotonic) + +// file QSTRs +Q(file) + diff --git a/python/port/mod/ion/file.cpp b/python/port/mod/ion/file.cpp new file mode 100644 index 000000000..388cbf07e --- /dev/null +++ b/python/port/mod/ion/file.cpp @@ -0,0 +1,265 @@ +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +} + +#include +#include + +STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); + +extern const mp_obj_type_t file_type = { + { &mp_type_type }, + 0, + MP_QSTR_file, + file_print, + file_make_new, +}; + +typedef enum _file_location_t { + RAM = 0, FLASH = 1 +} file_location_t; + +typedef enum _file_mode_t { + READ = 0, WRITE = 1, APPEND = 2, CREATE = 3 +} file_mode_t; + +typedef enum _file_bin_t { + TEXT = 0, BINARY = 1 +} file_bin_t; + + +typedef struct _file_obj_t { + mp_obj_base_t base; + + mp_obj_t name; + + file_mode_t open_mode; + bool edit_mode; + file_bin_t binary_mode; + + file_location_t location; + // If location is set to RAM, record is used. + Ion::Storage::Record record; + + uint16_t position; + +} file_obj_t; + +STATIC void file_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + file_obj_t *self = (file_obj_t*) MP_OBJ_TO_PTR(self_in); + + size_t l; + const char* file_name = mp_obj_str_get_data(self->name, &l); + + uint8_t offset = 0; + char file_mode[4] = {0, 0, 0, 0}; + + switch(self->open_mode) { + case READ: + file_mode[0] = 'r'; + break; + case WRITE: + file_mode[0] = 'w'; + break; + case APPEND: + file_mode[0] = 'a'; + break; + case CREATE: + file_mode[0] = 'x'; + break; + } + + if (self->edit_mode) { + file_mode[1] = '+'; + offset = 1; + } + + switch(self->binary_mode) { + case TEXT: + file_mode[1 + offset] = 't'; + break; + case BINARY: + file_mode[1 + offset] = 'b'; + break; + } + + + mp_print_str(print, ""); +} + +/* + * File constructor takes two arguments: + * - name: name of the file + * - mode: mode of opening (optional, r by default) + * + * File system organisation: + * - /ram/ + * Contains all files in RAM (python, poincare stuff, etc..) + * - /flash/ + * Contains all files in external's TAR archive + * + * When nme doesn't start with "/", we use RAM. + */ +STATIC mp_obj_t file_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 2, true); + + file_obj_t *file = m_new_obj(file_obj_t); + + if (!mp_obj_is_str(args[0])) { + mp_raise_ValueError("First argument must be a string!"); + } + + // Store and parse file name + size_t l; + const char* file_name = mp_obj_str_get_data(args[0], &l); + file->name = mp_obj_new_str(file_name, l); + + // Parses file mode + file->open_mode = READ; + file->edit_mode = false; + file->binary_mode = TEXT; + + if (n_args == 2) { + if (!mp_obj_is_str(args[1])) { + mp_raise_ValueError("Second argument must be a string!"); + } + + const char* file_mode = mp_obj_str_get_data(args[1], &l); + + for(size_t i = 0; i < l; i++) { + char c = file_mode[i]; + + switch(c) { + case 'r': + file->open_mode = READ; + break; + case 'w': + file->open_mode = WRITE; + break; + case 'a': + file->open_mode = APPEND; + break; + case 'x': + file->open_mode = CREATE; + break; + case '+': + file->edit_mode = true; + break; + case 't': + file->binary_mode = TEXT; + break; + case 'b': + file->binary_mode = BINARY; + break; + } + } + } + + // If "", throw file not found + if (l > 0) { + file->location = RAM; + // Check location (RAM/FLASH) + if (file_name[0] == '/') { + if (strncmp(file_name, "/ram/", 5) == 0) { + file->location = RAM; + file_name = file_name + 5; + // } else if (strncmp(file_name, "/flash/", 7)) { + // file->location = FLASH; + // file_name = file_name + 7; + } else { + mp_raise_OSError(2); + } + } + } else { + mp_raise_OSError(2); + } + + if (!Ion::Storage::FullNameCompliant(file_name)) { + mp_raise_OSError(22); + } + + if(file->location == RAM) { + Ion::Storage::Record::ErrorStatus status; + + switch(file->open_mode) { + case READ: + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->position = 0; + if (file->record == Ion::Storage::Record()) { + mp_raise_OSError(2); + } + break; + case CREATE: + file->position = 0; + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + switch (status) { + case Ion::Storage::Record::ErrorStatus::NameTaken: + mp_raise_OSError(17); + break; + case Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable: + mp_raise_OSError(28); + break; + default: + break; + } + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + break; + case WRITE: + file->position = 0; + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + switch (status) { + case Ion::Storage::Record::ErrorStatus::NameTaken: + // setValue messes with empty buffer, so we delete record and re-create it. + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->record.destroy(); + + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + + if (status == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) { + mp_raise_OSError(28); + } + break; + case Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable: + mp_raise_OSError(28); + break; + default: + break; + } + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + break; + case APPEND: + file->record = Ion::Storage::sharedStorage()->recordNamed(file_name); + file->position = 0; + if (file->record == Ion::Storage::Record()) { + status = Ion::Storage::sharedStorage()->createRecordWithFullName(file_name, "", 0); + if (status == Ion::Storage::Record::ErrorStatus::NotEnoughSpaceAvailable) { + mp_raise_OSError(28); + } + } + file->position = file->record.value().size; + break; + } + } + + // mp_raise_FileNotFoundError("Test"); + + + file->base.type = &file_type; + return MP_OBJ_FROM_PTR(file); +} + + + diff --git a/python/port/mod/ion/modion.h b/python/port/mod/ion/modion.h index d8ffe219b..4b4558be5 100644 --- a/python/port/mod/ion/modion.h +++ b/python/port/mod/ion/modion.h @@ -1,3 +1,4 @@ #include mp_obj_t modion_keyboard_keydown(mp_obj_t key_o); +extern const mp_obj_type_t file_type; diff --git a/python/port/mod/ion/modion_table.cpp b/python/port/mod/ion/modion_table.cpp index aed204c1f..b1d861f51 100644 --- a/python/port/mod/ion/modion_table.cpp +++ b/python/port/mod/ion/modion_table.cpp @@ -65,6 +65,8 @@ STATIC const mp_rom_map_elem_t modion_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_KEY_EE), MP_OBJ_NEW_SMALL_INT(Ion::Keyboard::Key::EE) }, { MP_ROM_QSTR(MP_QSTR_KEY_ANS), MP_OBJ_NEW_SMALL_INT(Ion::Keyboard::Key::Ans) }, { MP_ROM_QSTR(MP_QSTR_KEY_EXE), MP_OBJ_NEW_SMALL_INT(Ion::Keyboard::Key::EXE) }, + + { MP_ROM_QSTR(MP_QSTR_file), (mp_obj_t)&file_type} }; STATIC MP_DEFINE_CONST_DICT(modion_module_globals, modion_module_globals_table);