source.hpp (9398B)
1 #ifndef UUID_11A3E8B0_C5C5_4C4E_A22E_56F6E5346CEC 2 #define UUID_11A3E8B0_C5C5_4C4E_A22E_56F6E5346CEC 3 #pragma once 4 5 #include "dumpable.hpp" 6 #include "endian.hpp" 7 8 #include <libshit/check.hpp> 9 #include <libshit/low_io.hpp> 10 #include <libshit/lua/value_object.hpp> 11 #include <libshit/meta_utils.hpp> 12 #include <libshit/nonowning_string.hpp> 13 #include <libshit/not_null.hpp> 14 #include <libshit/shared_ptr.hpp> 15 16 #include <array> 17 #include <cstdint> 18 #include <boost/filesystem/path.hpp> 19 20 namespace Neptools 21 { 22 23 LIBSHIT_GEN_EXCEPTION_TYPE(SourceOverflow, std::logic_error); 24 25 /// A fixed size, read-only, seekable data source (or something that emulates 26 /// it) 27 class LIBSHIT_LUAGEN(const: false) Source final 28 : public Libshit::Lua::ValueObject 29 { 30 LIBSHIT_LUA_CLASS; 31 public: 32 struct BufEntry 33 { 34 const Byte* ptr = nullptr; 35 FilePosition offset = static_cast<FilePosition>(-1); 36 FileMemSize size = 0; 37 }; 38 39 Source(Source s, FilePosition offset, FilePosition size) noexcept 40 : Source{std::move(s)} { Slice(offset, size); get = 0; } 41 42 static Source FromFile(const boost::filesystem::path& fname); 43 LIBSHIT_NOLUA 44 static Source FromFd( 45 boost::filesystem::path fname, Libshit::LowIo::FdType fd, bool owning); 46 static Source FromMemory(std::string data) 47 { return FromMemory("", std::move(data)); } 48 static Source FromMemory(boost::filesystem::path fname, std::string data); 49 LIBSHIT_NOLUA 50 static Source FromMemory(boost::filesystem::path fname, 51 std::unique_ptr<char[]> data, std::size_t len); 52 53 template <typename Checker = Libshit::Check::Assert> 54 void Slice(FilePosition offset, FilePosition size) noexcept 55 { 56 LIBSHIT_CHECK(SourceOverflow, offset <= this->size && 57 offset + size <= this->size, "Slice: invalid sizes"); 58 this->offset += offset; 59 this->get -= offset; 60 this->size = size; 61 } 62 63 FilePosition GetOffset() const noexcept { return offset; } 64 FilePosition GetOrigSize() const noexcept { return p->size; } 65 const boost::filesystem::path& GetFileName() const noexcept 66 { return p->file_name; } 67 68 FilePosition GetSize() const noexcept { return size; } 69 70 template <typename Checker = Libshit::Check::Assert> 71 void Seek(FilePosition pos) noexcept 72 { 73 LIBSHIT_CHECK(SourceOverflow, pos <= size, "Seek past end of source"); 74 get = pos; 75 } 76 FilePosition Tell() const noexcept { return get; } 77 FilePosition GetRemainingSize() const noexcept { return size - get; } 78 bool Eof() const noexcept { return get == size; } 79 80 void CheckSize(FilePosition size) const 81 { 82 if (p->size < size) 83 LIBSHIT_THROW(Libshit::DecodeError, "Premature end of data", 84 "Used source", *this); 85 } 86 void CheckRemainingSize(FilePosition size) const { CheckSize(get + size); } 87 88 template <typename Checker = Libshit::Check::Assert, typename T> 89 LIBSHIT_NOLUA void ReadGen(T& x) 90 { Read<Checker>(reinterpret_cast<Byte*>(&x), Libshit::EmptySizeof<T>); } 91 92 template <typename T, typename Checker = Libshit::Check::Assert> 93 LIBSHIT_NOLUA T ReadGen() { T ret; ReadGen<Checker>(ret); return ret; } 94 95 96 template <typename Checker = Libshit::Check::Assert, typename T> 97 LIBSHIT_NOLUA void PreadGen(FilePosition offs, T& x) const 98 { Pread<Checker>(offs, reinterpret_cast<Byte*>(&x), Libshit::EmptySizeof<T>); } 99 100 template <typename T, typename Checker = Libshit::Check::Assert> 101 LIBSHIT_NOLUA T PreadGen(FilePosition offs) const 102 { T ret; PreadGen<Checker>(offs, ret); return ret; } 103 104 105 template <typename Checker = Libshit::Check::Assert> 106 LIBSHIT_NOLUA void Read(Byte* buf, FileMemSize len) 107 { Pread<Checker>(get, buf, len); get += len; } 108 template <typename Checker = Libshit::Check::Assert> 109 LIBSHIT_NOLUA void Read(char* buf, FileMemSize len) 110 { Pread<Checker>(get, buf, len); get += len; } 111 112 template <typename Checker = Libshit::Check::Assert> 113 LIBSHIT_NOLUA void Pread( 114 FilePosition offs, Byte* buf, FileMemSize len) const 115 { 116 LIBSHIT_ADD_INFOS( 117 LIBSHIT_CHECK(SourceOverflow, offs <= size && offs+len <= size, 118 "Source overflow"); 119 Pread_(offs, buf, len), 120 "Used source", *this, "Read offset", offs, "Read size", len); 121 } 122 123 template <typename Checker = Libshit::Check::Assert> 124 LIBSHIT_NOLUA 125 void Pread(FilePosition offs, char* buf, FileMemSize len) const 126 { Pread<Checker>(offs, reinterpret_cast<Byte*>(buf), len); } 127 128 // helper 129 #define NEPTOOLS_GEN_HLP2(bits, Camel, snake) \ 130 template <typename Checker = Libshit::Check::Assert> \ 131 std::uint##bits##_t Read##Camel##Uint##bits() \ 132 { \ 133 return boost::endian::snake##_to_native( \ 134 ReadGen<std::uint##bits##_t, Checker>()); \ 135 } \ 136 template <typename Checker = Libshit::Check::Assert> \ 137 std::uint##bits##_t Pread##Camel##Uint##bits(FilePosition offs) const \ 138 { \ 139 return boost::endian::snake##_to_native( \ 140 PreadGen<std::uint##bits##_t, Checker>(offs)); \ 141 } 142 #define NEPTOOLS_GEN_HLP(bits) \ 143 template <typename Checker = Libshit::Check::Assert> \ 144 std::uint##bits##_t ReadUint##bits(Endian e) \ 145 { \ 146 return ToNativeCopy(ReadGen<std::uint##bits##_t, Checker>(), e); \ 147 } \ 148 template <typename Checker = Libshit::Check::Assert> \ 149 std::uint##bits##_t PreadUint##bits(FilePosition offs, Endian e) \ 150 { \ 151 return ToNativeCopy( \ 152 PreadGen<std::uint##bits##_t, Checker>(offs), e); \ 153 } \ 154 NEPTOOLS_GEN_HLP2(bits, Little, little) \ 155 NEPTOOLS_GEN_HLP2(bits, Big, big) 156 157 // 8-bit values have no endian, but have these functions for consistency 158 NEPTOOLS_GEN_HLP(8) 159 NEPTOOLS_GEN_HLP(16) 160 NEPTOOLS_GEN_HLP(32) 161 NEPTOOLS_GEN_HLP(64) 162 #undef NEPTOOLS_GEN_HLP 163 164 std::string ReadCString() 165 { 166 auto ret = PreadCString(Tell()); 167 Seek(Tell() + ret.size() + 1); 168 return ret; 169 } 170 std::string PreadCString(FilePosition offs) const; 171 172 struct Provider : public Libshit::RefCounted 173 { 174 Provider(boost::filesystem::path file_name, FilePosition size) 175 : file_name{std::move(file_name)}, size{size} {} 176 Provider(const Provider&) = delete; 177 void operator=(const Provider&) = delete; 178 virtual ~Provider() = default; 179 180 virtual void Pread(FilePosition offs, Byte* buf, FileMemSize len) = 0; 181 182 void LruPush(const Byte* ptr, FilePosition offset, FileMemSize size); 183 bool LruGet(FilePosition offs); 184 185 std::array<BufEntry, 4> lru; 186 boost::filesystem::path file_name; 187 FilePosition size; 188 }; 189 LIBSHIT_NOLUA Source(Libshit::NotNullSmartPtr<Provider> p) 190 : size{p->size}, p{Libshit::Move(p)} {} 191 192 void Dump(Sink& sink) const; 193 LIBSHIT_NOLUA void Dump(Sink&& sink) const { Dump(sink); } 194 LIBSHIT_NOLUA void Inspect(std::ostream& os) const; 195 LIBSHIT_NOLUA void Inspect(std::ostream&& os) const { Inspect(os); } 196 std::string Inspect() const; 197 198 LIBSHIT_NOLUA Libshit::StringView GetChunk(FilePosition offs) const; 199 200 private: 201 // offset: in original file! 202 BufEntry GetTemporaryEntry(FilePosition offs) const; 203 204 void Pread_(FilePosition offs, Byte* buf, FileMemSize len) const; 205 static Source FromFile_(const boost::filesystem::path& fname); 206 207 FilePosition offset = 0, size, get = 0; 208 209 Libshit::NotNull<Libshit::SmartPtr<Provider>> p; 210 }; 211 212 inline std::ostream& operator<<(std::ostream& os, const Source s) 213 { s.Inspect(os); return os; } 214 215 std::string to_string(const Source& s); 216 217 class DumpableSource final : public Dumpable 218 { 219 LIBSHIT_DYNAMIC_OBJECT; 220 public: 221 LIBSHIT_NOLUA 222 DumpableSource(Source&& s) noexcept : src{std::move(s)} {} 223 DumpableSource(const Source& s, FilePosition offset, FilePosition size) noexcept 224 : src{s, offset, size} {} 225 DumpableSource(const Source& s) noexcept : src{s} {} // NOLINT 226 227 void Fixup() override {} 228 229 FilePosition GetSize() const override { return src.GetSize(); } 230 Source GetSource() const noexcept { return src; } 231 private: 232 Source src; 233 void Dump_(Sink& sink) const override { src.Dump(sink); } 234 void Inspect_(std::ostream& os, unsigned) const override; 235 }; 236 237 #define ADD_SOURCE(expr, ...) \ 238 LIBSHIT_ADD_INFOS(expr, "Used source", __VA_ARGS__) 239 240 struct QuotedSource { Source src; }; 241 inline std::ostream& operator<<(std::ostream& os, QuotedSource q) 242 { DumpBytes(os, q.src); return os; } 243 inline QuotedSource Quoted(Source src) { return {src}; } 244 245 } 246 #endif