123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- #pragma once
- #include <nall/platform.hpp>
- #include <nall/array-span.hpp>
- #include <nall/array-view.hpp>
- #include <nall/inode.hpp>
- #include <nall/range.hpp>
- #include <nall/stdint.hpp>
- #include <nall/string.hpp>
- #include <nall/utility.hpp>
- #include <nall/varint.hpp>
- #include <nall/hash/sha256.hpp>
- namespace nall {
- //on Windows (at least for 7 and earlier), FILE* is not buffered
- //thus, reading/writing one byte at a time will be dramatically slower
- //on all other OSes, FILE* is buffered
- //in order to ensure good performance, file_buffer implements its own buffer
- //this speeds up Windows substantially, without harming performance elsewhere much
- struct file_buffer {
- struct mode { enum : uint { read, write, modify, append }; };
- struct index { enum : uint { absolute, relative }; };
- file_buffer(const file_buffer&) = delete;
- auto operator=(const file_buffer&) -> file_buffer& = delete;
- file_buffer() = default;
- file_buffer(const string& filename, uint mode) { open(filename, mode); }
- file_buffer(file_buffer&& source) { operator=(move(source)); }
- ~file_buffer() { close(); }
- auto operator=(file_buffer&& source) -> file_buffer& {
- buffer = source.buffer;
- bufferOffset = source.bufferOffset;
- bufferDirty = source.bufferDirty;
- fileHandle = source.fileHandle;
- fileOffset = source.fileOffset;
- fileSize = source.fileSize;
- fileMode = source.fileMode;
- source.bufferOffset = -1;
- source.bufferDirty = false;
- source.fileHandle = nullptr;
- source.fileOffset = 0;
- source.fileSize = 0;
- source.fileMode = mode::read;
- return *this;
- }
- explicit operator bool() const {
- return (bool)fileHandle;
- }
- auto read() -> uint8_t {
- if(!fileHandle) return 0; //file not open
- if(fileMode == mode::write) return 0; //reads not permitted
- if(fileOffset >= fileSize) return 0; //cannot read past end of file
- bufferSynchronize();
- return buffer[fileOffset++ & buffer.size() - 1];
- }
- template<typename T = uint64_t> auto readl(uint length = 1) -> T {
- T data = 0;
- for(uint n : range(length)) {
- data |= (T)read() << n * 8;
- }
- return data;
- }
- template<typename T = uint64_t> auto readm(uint length = 1) -> T {
- T data = 0;
- while(length--) {
- data <<= 8;
- data |= read();
- }
- return data;
- }
- auto reads(uint length) -> string {
- string result;
- result.resize(length);
- for(auto& byte : result) byte = read();
- return result;
- }
- auto read(array_span<uint8_t> memory) -> void {
- for(auto& byte : memory) byte = read();
- }
- auto write(uint8_t data) -> void {
- if(!fileHandle) return; //file not open
- if(fileMode == mode::read) return; //writes not permitted
- bufferSynchronize();
- buffer[fileOffset++ & buffer.size() - 1] = data;
- bufferDirty = true;
- if(fileOffset > fileSize) fileSize = fileOffset;
- }
- template<typename T = uint64_t> auto writel(T data, uint length = 1) -> void {
- while(length--) {
- write(uint8_t(data));
- data >>= 8;
- }
- }
- template<typename T = uint64_t> auto writem(T data, uint length = 1) -> void {
- for(uint n : reverse(range(length))) {
- write(uint8_t(data >> n * 8));
- }
- }
- auto writes(const string& s) -> void {
- for(auto& byte : s) write(byte);
- }
- auto write(array_view<uint8_t> memory) -> void {
- for(auto& byte : memory) write(byte);
- }
- template<typename... P> auto print(P&&... p) -> void {
- string s{forward<P>(p)...};
- for(auto& byte : s) write(byte);
- }
- auto flush() -> void {
- bufferFlush();
- fflush(fileHandle);
- }
- auto seek(int64_t offset, uint index_ = index::absolute) -> void {
- if(!fileHandle) return;
- bufferFlush();
- int64_t seekOffset = fileOffset;
- switch(index_) {
- case index::absolute: seekOffset = offset; break;
- case index::relative: seekOffset += offset; break;
- }
- if(seekOffset < 0) seekOffset = 0; //cannot seek before start of file
- if(seekOffset > fileSize) {
- if(fileMode == mode::read) { //cannot seek past end of file
- seekOffset = fileSize;
- } else { //pad file to requested location
- fileOffset = fileSize;
- while(fileSize < seekOffset) write(0);
- }
- }
- fileOffset = seekOffset;
- }
- auto offset() const -> uint64_t {
- if(!fileHandle) return 0;
- return fileOffset;
- }
- auto size() const -> uint64_t {
- if(!fileHandle) return 0;
- return fileSize;
- }
- auto truncate(uint64_t size) -> bool {
- if(!fileHandle) return false;
- #if defined(API_POSIX)
- return ftruncate(fileno(fileHandle), size) == 0;
- #elif defined(API_WINDOWS)
- return _chsize(fileno(fileHandle), size) == 0;
- #endif
- }
- auto end() const -> bool {
- if(!fileHandle) return true;
- return fileOffset >= fileSize;
- }
- auto open(const string& filename, uint mode_) -> bool {
- close();
- switch(fileMode = mode_) {
- #if defined(API_POSIX)
- case mode::read: fileHandle = fopen(filename, "rb" ); break;
- case mode::write: fileHandle = fopen(filename, "wb+"); break; //need read permission for buffering
- case mode::modify: fileHandle = fopen(filename, "rb+"); break;
- case mode::append: fileHandle = fopen(filename, "wb+"); break;
- #elif defined(API_WINDOWS)
- case mode::read: fileHandle = _wfopen(utf16_t(filename), L"rb" ); break;
- case mode::write: fileHandle = _wfopen(utf16_t(filename), L"wb+"); break;
- case mode::modify: fileHandle = _wfopen(utf16_t(filename), L"rb+"); break;
- case mode::append: fileHandle = _wfopen(utf16_t(filename), L"wb+"); break;
- #endif
- }
- if(!fileHandle) return false;
- bufferOffset = -1;
- fileOffset = 0;
- fseek(fileHandle, 0, SEEK_END);
- fileSize = ftell(fileHandle);
- fseek(fileHandle, 0, SEEK_SET);
- return true;
- }
- auto close() -> void {
- if(!fileHandle) return;
- bufferFlush();
- fclose(fileHandle);
- fileHandle = nullptr;
- }
- private:
- array<uint8_t[4096]> buffer;
- int bufferOffset = -1;
- bool bufferDirty = false;
- FILE* fileHandle = nullptr;
- uint64_t fileOffset = 0;
- uint64_t fileSize = 0;
- uint fileMode = mode::read;
- auto bufferSynchronize() -> void {
- if(!fileHandle) return;
- if(bufferOffset == (fileOffset & ~(buffer.size() - 1))) return;
- bufferFlush();
- bufferOffset = fileOffset & ~(buffer.size() - 1);
- fseek(fileHandle, bufferOffset, SEEK_SET);
- uint64_t length = bufferOffset + buffer.size() <= fileSize ? buffer.size() : fileSize & buffer.size() - 1;
- if(length) (void)fread(buffer.data(), 1, length, fileHandle);
- }
- auto bufferFlush() -> void {
- if(!fileHandle) return; //file not open
- if(fileMode == mode::read) return; //buffer cannot be written to
- if(bufferOffset < 0) return; //buffer unused
- if(!bufferDirty) return; //buffer unmodified since read
- fseek(fileHandle, bufferOffset, SEEK_SET);
- uint64_t length = bufferOffset + buffer.size() <= fileSize ? buffer.size() : fileSize & buffer.size() - 1;
- if(length) (void)fwrite(buffer.data(), 1, length, fileHandle);
- bufferOffset = -1;
- bufferDirty = false;
- }
- };
- }
|