add wip span type and new file2 interface and fstream impl.
testing was very minimal
This commit is contained in:
parent
d304d719e9
commit
b82f039aaf
@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
|||||||
project(solanaceae)
|
project(solanaceae)
|
||||||
|
|
||||||
add_library(solanaceae_util
|
add_library(solanaceae_util
|
||||||
|
./solanaceae/util/span.hpp
|
||||||
|
|
||||||
./solanaceae/util/utils.hpp
|
./solanaceae/util/utils.hpp
|
||||||
./solanaceae/util/utils.cpp
|
./solanaceae/util/utils.cpp
|
||||||
|
|
||||||
@ -20,3 +22,16 @@ target_compile_features(solanaceae_util PUBLIC cxx_std_17)
|
|||||||
#target_link_libraries(solanaceae_util PUBLIC
|
#target_link_libraries(solanaceae_util PUBLIC
|
||||||
#)
|
#)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
|
||||||
|
add_library(solanaceae_file2
|
||||||
|
./solanaceae/file/file2.hpp
|
||||||
|
./solanaceae/file/file2_std.hpp
|
||||||
|
./solanaceae/file/file2_std.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(solanaceae_file2 PUBLIC .)
|
||||||
|
target_compile_features(solanaceae_file2 PUBLIC cxx_std_17)
|
||||||
|
target_link_libraries(solanaceae_file2 PUBLIC
|
||||||
|
solanaceae_util
|
||||||
|
)
|
||||||
|
25
solanaceae/file/file2.hpp
Normal file
25
solanaceae/file/file2.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/util/span.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
struct File2I {
|
||||||
|
// read only
|
||||||
|
uint64_t _file_size {0};
|
||||||
|
|
||||||
|
const bool can_write {false};
|
||||||
|
const bool can_read {false};
|
||||||
|
// TODO: non-seekable files?
|
||||||
|
|
||||||
|
explicit File2I(bool can_write_, bool can_read_) : can_write(can_write_), can_read(can_read_) {}
|
||||||
|
virtual ~File2I(void) {}
|
||||||
|
|
||||||
|
virtual bool isGood(void) = 0;
|
||||||
|
|
||||||
|
// pos -1 means stream, append to last written, or read position (independent, like FILE*s)
|
||||||
|
virtual bool write(const ByteSpan data, int64_t pos = -1) = 0;
|
||||||
|
virtual std::variant<ByteSpan, std::vector<uint8_t>> read(uint64_t size, int64_t pos = -1) = 0;
|
||||||
|
};
|
||||||
|
|
129
solanaceae/file/file2_std.cpp
Normal file
129
solanaceae/file/file2_std.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#include "./file2_std.hpp"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// why is this so ugly?
|
||||||
|
// WARNING: has g pos side effect
|
||||||
|
static uint64_t get_file_size(std::fstream& f) {
|
||||||
|
// figure out size
|
||||||
|
f.seekg(0, f.end);
|
||||||
|
uint64_t file_size = f.tellg();
|
||||||
|
f.seekg(0, f.beg);
|
||||||
|
return file_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
File2WFile::File2WFile(std::string_view file_path, bool trunc) :
|
||||||
|
File2I(true, false),
|
||||||
|
_file(
|
||||||
|
static_cast<std::string>(file_path),
|
||||||
|
std::ios::out |
|
||||||
|
(trunc ? std::ios::trunc | std::ios::binary : std::ios::binary) // hacky but type safe
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_file_size = get_file_size(_file);
|
||||||
|
|
||||||
|
if (!_file.is_open()) {
|
||||||
|
return; // TODO: error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File2WFile::isGood(void) {
|
||||||
|
return _file.is_open() && _file.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
//bool write(const ByteSpan data, int64_t pos = -1) override;
|
||||||
|
bool File2WFile::write(const ByteSpan data, int64_t pos) {
|
||||||
|
if (pos != -1) {
|
||||||
|
//std::cerr << "invalid pos\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.empty()) {
|
||||||
|
//std::cerr << "no data\n";
|
||||||
|
return false; // true instead?
|
||||||
|
}
|
||||||
|
|
||||||
|
_file.write(reinterpret_cast<const char*>(data.ptr), data.size);
|
||||||
|
|
||||||
|
return _file.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<ByteSpan, std::vector<uint8_t>> File2WFile::read(uint64_t, int64_t) {
|
||||||
|
return ByteSpan{};
|
||||||
|
}
|
||||||
|
|
||||||
|
File2RWFile::File2RWFile(std::string_view file_path, uint64_t file_size, bool trunc) :
|
||||||
|
File2I(true, true),
|
||||||
|
_file(
|
||||||
|
static_cast<std::string>(file_path),
|
||||||
|
std::ios::in |
|
||||||
|
std::ios::out |
|
||||||
|
(trunc ? std::ios::trunc | std::ios::binary : std::ios::binary) // hacky but type safe
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (file_size == -1) {
|
||||||
|
_file_size = get_file_size(_file);
|
||||||
|
} else {
|
||||||
|
_file_size = file_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_file.is_open()) {
|
||||||
|
return; // TODO: error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File2RWFile::isGood(void) {
|
||||||
|
return _file.is_open() && _file.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool File2RWFile::write(const ByteSpan data, int64_t pos) {
|
||||||
|
if (pos >= _file_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.empty()) {
|
||||||
|
return false; // true instead?
|
||||||
|
}
|
||||||
|
|
||||||
|
// if out-of-order, seek
|
||||||
|
if (pos >= 0 && _file.tellp() != int64_t(pos)) {
|
||||||
|
// TODO: error check
|
||||||
|
if (_file.seekp(pos, std::ios::beg).fail()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (pos < -1) {
|
||||||
|
// error !!!!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_file.write(reinterpret_cast<const char*>(data.ptr), data.size);
|
||||||
|
|
||||||
|
return _file.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::variant<ByteSpan, std::vector<uint8_t>> File2RWFile::read(uint64_t size, int64_t pos) {
|
||||||
|
if (pos >= int64_t(_file_size)) {
|
||||||
|
return ByteSpan{};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos != -1) {
|
||||||
|
// TODO: error check
|
||||||
|
_file.seekg(pos, std::ios::beg);
|
||||||
|
} else if (pos < -1) {
|
||||||
|
// error !!!!
|
||||||
|
return ByteSpan{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// we copy the data from file (not mapped)
|
||||||
|
std::vector<uint8_t> chunk(size);
|
||||||
|
const auto nread = _file.read(reinterpret_cast<char*>(chunk.data()), chunk.size()).gcount();
|
||||||
|
if (nread != std::numeric_limits<std::streamsize>::max()) {
|
||||||
|
chunk.resize(nread); // usually a noop
|
||||||
|
} else {
|
||||||
|
chunk.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
50
solanaceae/file/file2_std.hpp
Normal file
50
solanaceae/file/file2_std.hpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./file2.hpp"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
// std fstream backed files
|
||||||
|
|
||||||
|
// write steam file
|
||||||
|
// no preallocation required, but seeking is disabled
|
||||||
|
struct File2WFile : public File2I {
|
||||||
|
std::fstream _file;
|
||||||
|
|
||||||
|
// dont truncate by default
|
||||||
|
File2WFile(std::string_view file_path, bool trunc = false);
|
||||||
|
|
||||||
|
virtual ~File2WFile(void) {}
|
||||||
|
|
||||||
|
bool isGood(void) override;
|
||||||
|
|
||||||
|
bool write(const ByteSpan data, int64_t pos = -1) override;
|
||||||
|
std::variant<ByteSpan, std::vector<uint8_t>> read(uint64_t size, int64_t pos = -1) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// read write, requires an existing file, file size is fixed
|
||||||
|
struct File2RWFile : public File2I {
|
||||||
|
std::fstream _file;
|
||||||
|
|
||||||
|
// dont truncate by default
|
||||||
|
// pass -1 for fetching the size from file
|
||||||
|
File2RWFile(std::string_view file_path, uint64_t file_size = -1, bool trunc = false);
|
||||||
|
|
||||||
|
virtual ~File2RWFile(void) {}
|
||||||
|
|
||||||
|
bool isGood(void) override;
|
||||||
|
|
||||||
|
bool write(const ByteSpan data, int64_t pos = -1) override;
|
||||||
|
std::variant<ByteSpan, std::vector<uint8_t>> read(uint64_t size, int64_t pos = -1) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// cut down interface (write disabled)
|
||||||
|
// TODO: remove
|
||||||
|
struct File2RFile : public File2RWFile {
|
||||||
|
File2RFile(std::string_view file_path) : File2RWFile(file_path) {}
|
||||||
|
virtual ~File2RFile(void) {}
|
||||||
|
using File2RWFile::isGood;
|
||||||
|
using File2RWFile::read;
|
||||||
|
bool write(const ByteSpan, int64_t = -1) override { return false; }
|
||||||
|
};
|
||||||
|
|
39
solanaceae/util/span.hpp
Normal file
39
solanaceae/util/span.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// non owning view
|
||||||
|
template<typename T>
|
||||||
|
struct Span final {
|
||||||
|
const T* ptr {nullptr};
|
||||||
|
uint64_t size {0};
|
||||||
|
|
||||||
|
constexpr Span(void) {}
|
||||||
|
constexpr Span(const T* ptr_, uint64_t size_) : ptr(ptr_), size(size_) {}
|
||||||
|
constexpr explicit Span(const std::vector<T>& vec) : ptr(vec.data()), size(vec.size()) {}
|
||||||
|
|
||||||
|
explicit operator std::vector<T>() const {
|
||||||
|
return std::vector<T>{ptr, ptr+size};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T& operator[](uint64_t i) const {
|
||||||
|
if (i > size) {
|
||||||
|
throw std::out_of_range("accessed span out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const T* cbegin(void) const { return ptr; }
|
||||||
|
constexpr const T* cend(void) const { return ptr+size; }
|
||||||
|
constexpr const T* begin(void) const { return ptr; }
|
||||||
|
constexpr const T* end(void) const { return ptr+size; }
|
||||||
|
|
||||||
|
constexpr bool empty(void) const { return ptr == nullptr || size == 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// useful alias
|
||||||
|
using ByteSpan = Span<uint8_t>;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user