#include "io.hpp" #include #include #include #include #include #include #include #include "coders/commons.hpp" #include "coders/gzip.hpp" #include "coders/json.hpp" #include "coders/toml.hpp" #include "util/stringutil.hpp" #include "devices/Device.hpp" namespace fs = std::filesystem; static std::map> devices; void io::set_device(const std::string& name, std::shared_ptr device) { devices[name] = device; } void io::remove_device(const std::string& name) { devices.erase(name); } std::shared_ptr io::get_device(const std::string& name) { const auto& found = devices.find(name); if (found == devices.end()) { return nullptr; } return found->second; } io::Device& io::require_device(const std::string& name) { auto device = get_device(name); if (!device) { throw std::runtime_error("io-device not found: " + name); } return *device; } void io::create_subdevice( const std::string& name, const std::string& parent, const io::path& root ) { auto parentDevice = get_device(parent); if (!parentDevice) { throw std::runtime_error("parent device not found for entry-point: " + parent); } set_device(name, std::make_shared(parentDevice, root.pathPart())); } io::directory_iterator::directory_iterator(const io::path& folder) : folder(folder) { auto& device = io::require_device(folder.entryPoint()); generator = device.list(folder.pathPart()); } io::rafile::rafile(const io::path& filename) : file(std::make_unique(io::resolve(filename), std::ios::binary | std::ios::ate)) { if (!*file) { throw std::runtime_error("could not to open file " + filename.string()); } filelength = file->tellg(); file->seekg(0); } io::rafile::rafile(std::unique_ptr file, size_t length) : file(std::move(file)), filelength(length) { } size_t io::rafile::length() const { return filelength; } void io::rafile::seekg(std::streampos pos) { file->seekg(pos); } void io::rafile::read(char* buffer, std::streamsize size) { file->read(buffer, size); } bool io::write_bytes( const io::path& filename, const ubyte* data, size_t size ) { auto device = io::get_device(filename.entryPoint()); if (device == nullptr) { throw std::runtime_error("io-device not found: " + filename.entryPoint()); } auto stream = device->write(filename.pathPart()); stream->write(reinterpret_cast(data), size); return stream->good(); } bool io::read(const io::path& filename, char* data, size_t size) { auto device = io::get_device(filename.entryPoint()); if (device == nullptr) { return false; } auto stream = io::read(filename); stream->read(data, size); return stream->good(); } std::unique_ptr io::write(const io::path& file) { auto device = io::get_device(file.entryPoint()); if (device == nullptr) { throw std::runtime_error("io-device not found: " + file.entryPoint()); } return device->write(file.pathPart()); } std::unique_ptr io::read(const io::path& filename) { auto device = io::get_device(filename.entryPoint()); if (device == nullptr) { throw std::runtime_error("io-device not found: " + filename.entryPoint()); } return device->read(filename.pathPart()); } util::Buffer io::read_bytes_buffer(const path& file) { size_t size; auto bytes = io::read_bytes(file, size); return util::Buffer(std::move(bytes), size); } std::unique_ptr io::read_bytes( const io::path& filename, size_t& length ) { auto& device = io::require_device(filename.entryPoint()); length = device.size(filename.pathPart()); auto data = std::make_unique(length); auto stream = io::read(filename); stream->read(reinterpret_cast(data.get()), length); return stream->good() ? std::move(data) : nullptr; } std::vector io::read_bytes(const path& filename) { auto& device = io::require_device(filename.entryPoint()); size_t length = device.size(filename.pathPart()); std::vector data(length); auto stream = io::read(filename); stream->read(reinterpret_cast(data.data()), length); return data; } std::string io::read_string(const path& filename) { size_t size; auto bytes = read_bytes(filename, size); return std::string((const char*)bytes.get(), size); } bool io::write_string(const io::path& file, std::string_view content) { return io::write_bytes(file, (const ubyte*)content.data(), content.size()); } bool io::write_json( const io::path& file, const dv::value& obj, bool nice ) { return io::write_string(file, json::stringify(obj, nice, " ")); } bool io::write_binary_json( const io::path& file, const dv::value& obj, bool compression ) { auto bytes = json::to_binary(obj, compression); return io::write_bytes(file, bytes.data(), bytes.size()); } dv::value io::read_json(const path& filename) { std::string text = io::read_string(filename); return json::parse(filename.string(), text); } dv::value io::read_binary_json(const path& file) { size_t size; auto bytes = io::read_bytes(file, size); return json::from_binary(bytes.get(), size); } dv::value io::read_toml(const path& file) { return toml::parse(file.string(), io::read_string(file)); } std::vector io::read_list(const io::path& filename) { auto stream = io::read(filename); std::vector lines; std::string line; while (std::getline(*stream, line)) { util::trim(line); if (line.length() == 0) continue; if (line[0] == '#') continue; lines.push_back(line); } return lines; } bool io::is_regular_file(const io::path& file) { if (file.emptyOrInvalid()) { return false; } auto device = io::get_device(file.entryPoint()); if (device == nullptr) { return false; } return device->isfile(file.pathPart()); } bool io::is_directory(const io::path& file) { if (file.emptyOrInvalid()) { return false; } auto device = io::get_device(file.entryPoint()); if (device == nullptr) { return false; } return device->isdir(file.pathPart()); } bool io::exists(const io::path& file) { if (file.emptyOrInvalid()) { return false; } auto device = io::get_device(file.entryPoint()); if (device == nullptr) { return false; } return device->exists(file.pathPart()); } bool io::create_directory(const io::path& file) { auto& device = io::require_device(file.entryPoint()); if (device.isdir(file.pathPart())) { return false; } return device.mkdirs(file.pathPart()); } bool io::create_directories(const io::path& file) { auto& device = io::require_device(file.entryPoint()); if (device.isdir(file.pathPart())) { return false; } return device.mkdirs(file.pathPart()); } bool io::remove(const io::path& file) { auto& device = io::require_device(file.entryPoint()); return device.remove(file.pathPart()); } uint64_t io::remove_all(const io::path& file) { auto& device = io::require_device(file.entryPoint()); return device.removeAll(file.pathPart()); } bool io::copy(const io::path& src, const io::path& dst) { auto& srcDevice = io::require_device(src.entryPoint()); auto& dstDevice = io::require_device(dst.entryPoint()); if (!srcDevice.isfile(src.pathPart())) { return false; } auto input = srcDevice.read(src.pathPart()); auto output = dstDevice.write(dst.pathPart()); size_t size = srcDevice.size(src.pathPart()); std::vector buffer(16'384); while (size > 0) { size_t read = std::min(size, buffer.size()); input->read(buffer.data(), read); auto gcount = input->gcount(); output->write(buffer.data(), gcount); size -= gcount; if (input->eof()) { break; } if (!input->good() || !output->good()) { return false; } } return output->good(); } uint64_t io::copy_all(const io::path& src, const io::path& dst) { auto& srcDevice = io::require_device(src.entryPoint()); auto& dstDevice = io::require_device(dst.entryPoint()); auto dstPath = dst.pathPart(); if (!dstDevice.isdir(dstPath) && !dstDevice.mkdirs(dstPath)) { return 0; } uint64_t count = 0; for (auto& srcSubFile : directory_iterator(src)) { auto dstSubFile = dst / srcSubFile.name(); auto srcSubPath = srcSubFile.pathPart(); auto dstSubPath = dstSubFile.pathPart(); if (srcDevice.isdir(srcSubPath)) { if (!dstDevice.mkdirs(dstSubPath)) { continue; } count += copy_all(srcSubFile, dstSubFile); } else if (copy(srcSubFile, dstSubFile)) { count++; } } return count; } size_t io::file_size(const io::path& file) { auto& device = io::require_device(file.entryPoint()); return device.size(file.pathPart()); } io::file_time_type io::last_write_time(const io::path& file) { auto& device = io::require_device(file.entryPoint()); return device.lastWriteTime(file.pathPart()); } std::filesystem::path io::resolve(const io::path& file) { auto device = io::get_device(file.entryPoint()); if (device == nullptr) { return {}; } return device->resolve(file.pathPart()); } #include #include "coders/json.hpp" #include "coders/toml.hpp" using DecodeFunc = dv::value(*)(std::string_view, std::string_view); static std::map data_decoders { {fs::u8path(".json"), json::parse}, {fs::u8path(".toml"), toml::parse}, }; bool io::is_data_file(const io::path& file) { return is_data_interchange_format(file.extension()); } bool io::is_data_interchange_format(const std::string& ext) { return data_decoders.find(ext) != data_decoders.end(); } dv::value io::read_object(const path& file) { const auto& found = data_decoders.find(file.extension()); if (found == data_decoders.end()) { throw std::runtime_error("unknown file format"); } auto text = read_string(file); try { return found->second(file.string(), text); } catch (const parsing_error& err) { throw std::runtime_error(err.errorLog()); } }