From ea39e0a2d87e798b21a724f64aea3ec16629ad4e Mon Sep 17 00:00:00 2001 From: Alcaro Date: Tue, 20 Dec 2016 16:19:01 +0100 Subject: [PATCH] Update Arlib --- arlib/arlib.h | 2 + arlib/array.h | 1 + arlib/file-mem.cpp | 55 ----------- arlib/file-test.cpp | 126 +++++++++++++++---------- arlib/file-unix.cpp | 52 ++++------- arlib/file.h | 221 ++++++++++++++++++++++++++++++-------------- 6 files changed, 250 insertions(+), 207 deletions(-) delete mode 100644 arlib/file-mem.cpp diff --git a/arlib/arlib.h b/arlib/arlib.h index 4a9b0be..c349f1b 100644 --- a/arlib/arlib.h +++ b/arlib/arlib.h @@ -9,6 +9,8 @@ //WARNING: Arlib comes with zero stability guarantees. It can and will change in arbitrary ways, for any reason and at any time. #pragma once +#include "global.h" +#include // std::move #include "bml.h" #include "containers.h" #include "endian.h" diff --git a/arlib/array.h b/arlib/array.h index b418901..95ead0b 100644 --- a/arlib/array.h +++ b/arlib/array.h @@ -202,6 +202,7 @@ public: T& operator[](size_t n) { resize_grow(n+1); return this->items[n]; } void resize(size_t len) { resize_to(len); } + void reserve(size_t len) { resize_grow(len); } void append(const T& item) { size_t pos = this->count; resize_grow(pos+1); this->items[pos] = item; } void reset() { resize_shrink(0); } diff --git a/arlib/file-mem.cpp b/arlib/file-mem.cpp deleted file mode 100644 index cfb39be..0000000 --- a/arlib/file-mem.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "file.h" -#include "os.h" - -namespace { - class file_mem : public filewrite { - public: - array datawr; - arrayview datard; - - file_mem(arrayview data) : filewrite("", data.size()), datard(data) {} - file_mem(array data) : filewrite("", data.size()), datawr(data), datard(datawr) {} - - size_t read(arrayvieww target, size_t start) - { - size_t bytes_dst = target.size(); - size_t bytes_src = datard.size()-start; - size_t bytes = min(bytes_dst, bytes_src); - memcpy(target.ptr(), datard.ptr()+start, bytes); - return bytes; - } - - arrayview mmap(size_t start, size_t len) { return datard.slice(start, len); } - void unmap(arrayview data) {} - - bool resize(size_t newsize) - { - datawr.resize(newsize); - datard = datawr; - len = newsize; - return true; - } - - bool write(arrayview data, size_t start) - { - size_t bytes_src = data.size(); - size_t bytes_dst = datawr.size()-start; - size_t bytes = min(bytes_dst, bytes_src); - memcpy(datawr.ptr()+start, data.ptr(), bytes); - return true; - } - - arrayvieww mmapw(size_t start, size_t len) { return datawr.slice(start, len); } - void unmapw(arrayvieww data) {} - }; -} - -file* file::create_mem_view(arrayview data) -{ - return new file_mem(data); -} - -filewrite* file::create_mem_copy(array data) -{ - return new file_mem(data); -} diff --git a/arlib/file-test.cpp b/arlib/file-test.cpp index 75d4073..168cd61 100644 --- a/arlib/file-test.cpp +++ b/arlib/file-test.cpp @@ -20,79 +20,78 @@ #endif //criteria: -//- no funny symbols -//- implausible name, nothing of value must be lost by deleting it +//- no funny symbols in the name +//- implausible name, nothing of value may be lost by deleting it #define WRITABLE_FILE "arlib-selftest.txt" test("file reading") { - autoptr f = file::open(READONLY_FILE); - assert(f); - assert(f->len); - assert(f->len > strlen(READONLY_FILE_HEAD)); - assert(f->len >= 66000); - array bytes = f->read(); - assert(bytes.size() == f->len); + file f; + assert(f.open(READONLY_FILE)); + assert(f.size()); + assert(f.size() > strlen(READONLY_FILE_HEAD)); + assert(f.size() >= 66000); + array bytes = f.read(); + assert(bytes.size() == f.size()); assert(!memcmp(bytes.ptr(), READONLY_FILE_HEAD, strlen(READONLY_FILE_HEAD))); - arrayview map = f->mmap(); + arrayview map = f.mmap(); assert(map.ptr()); - assert(map.size() == f->len); + assert(map.size() == f.size()); assert(!memcmp(bytes.ptr(), map.ptr(), bytes.size())); - arrayview map2 = f->mmap(); + arrayview map2 = f.mmap(); assert(map2.ptr()); - assert(map2.size() == f->len); + assert(map2.size() == f.size()); assert(!memcmp(bytes.ptr(), map2.ptr(), bytes.size())); - f->unmap(map2); + f.unmap(map2); const size_t t_start[] = { 0, 65536, 4096, 1, 1, 1, 65537, 65535 }; const size_t t_len[] = { 66000, 400, 400, 65535, 65536, 65999, 400, 2 }; for (size_t i=0;i map3 = f->mmap(t_start[i], t_len[i]); + arrayview map3 = f.mmap(t_start[i], t_len[i]); assert(map3.ptr()); assert(map3.size() == t_len[i]); assert(!memcmp(bytes.ptr()+t_start[i], map3.ptr(), t_len[i])); - f->unmap(map3); + f.unmap(map3); } - f->unmap(map); + f.unmap(map); } test("file writing") { - autoptr f; + file f; - assert(!filewrite::open(READONLY_FILE, filewrite::m_default)); - assert(!filewrite::open(READONLY_FILE, filewrite::m_existing)); - assert(!filewrite::open(READONLY_FILE, filewrite::m_replace)); - assert(!filewrite::open(READONLY_FILE, filewrite::m_create_excl)); + assert(!f.open(READONLY_FILE, file::m_write)); + assert(!f.open(READONLY_FILE, file::m_wr_existing)); + assert(!f.open(READONLY_FILE, file::m_replace)); + assert(!f.open(READONLY_FILE, file::m_create_excl)); - assert(filewrite::unlink(WRITABLE_FILE)); + assert(file::unlink(WRITABLE_FILE)); - assert(!file::open(WRITABLE_FILE)); + assert(!f.open(WRITABLE_FILE)); - f = filewrite::open(WRITABLE_FILE); - assert(f); - assert(f->replace("foo")); + assert(f.open(WRITABLE_FILE, file::m_write)); + assert(f.replace("foo")); assert_eq(string(file::read(WRITABLE_FILE)), "foo"); - f->resize(8); - assert(f->len == 8); + f.resize(8); + assert(f.size() == 8); byte expected[8]={'f','o','o',0,0,0,0,0}; array actual = file::read(WRITABLE_FILE); assert(actual.ptr()); assert(actual.size()==8); assert(!memcmp(actual.ptr(), expected, 8)); - arrayvieww map = f->mmapw(); + arrayvieww map = f.mmapw(); assert(map.ptr()); assert_eq(map.size(), 8); assert(!memcmp(map.ptr(), expected, 8)); map[3]='t'; - f->unmapw(map); + f.unmapw(map); expected[3] = 't'; actual = file::read(WRITABLE_FILE); @@ -100,27 +99,56 @@ test("file writing") assert(actual.size()==8); assert(!memcmp(actual.ptr(), expected, 8)); - f = NULL; - //test the various creation modes //file exists, these three should work - f=NULL; assert( (f=filewrite::open(WRITABLE_FILE, filewrite::m_default))); - f=NULL; assert( (f=filewrite::open(WRITABLE_FILE, filewrite::m_existing))); - assert_eq(f->len, 8); - f=NULL; assert( (f=filewrite::open(WRITABLE_FILE, filewrite::m_replace))); - assert_eq(f->len, 0); - f=NULL; assert(!(f=filewrite::open(WRITABLE_FILE, filewrite::m_create_excl)));//but this shouldn't + assert( (f.open(WRITABLE_FILE, file::m_write))); + assert( (f.open(WRITABLE_FILE, file::m_wr_existing))); + assert_eq(f.size(), 8); + assert( (f.open(WRITABLE_FILE, file::m_replace))); + assert_eq(f.size(), 0); + assert(!(f.open(WRITABLE_FILE, file::m_create_excl)));//but this shouldn't - f=NULL; - assert(filewrite::unlink(WRITABLE_FILE)); - assert(!filewrite::open(WRITABLE_FILE, filewrite::m_existing)); // this should fail - f=NULL; assert(f=filewrite::open(WRITABLE_FILE, filewrite::m_create_excl)); // this should create - assert(filewrite::unlink(WRITABLE_FILE)); + assert(file::unlink(WRITABLE_FILE)); + assert(!f.open(WRITABLE_FILE, file::m_wr_existing)); // this should fail + assert(f.open(WRITABLE_FILE, file::m_create_excl)); // this should create + assert(file::unlink(WRITABLE_FILE)); - f=NULL; assert(f=filewrite::open(WRITABLE_FILE, filewrite::m_replace)); // replacing a nonexistent file is fine - //opening a nonexistent file with m_default is tested at the start of this function - f=NULL; - assert(filewrite::unlink(WRITABLE_FILE)); - assert(filewrite::unlink(WRITABLE_FILE)); // ensure it properly deals with unlinking a nonexistent file + assert(f.open(WRITABLE_FILE, file::m_replace)); // replacing a nonexistent file is fine + //opening a nonexistent file with m_write is tested at the start of this function + f.close(); + assert(file::unlink(WRITABLE_FILE)); + assert(file::unlink(WRITABLE_FILE)); // ensure it properly deals with unlinking a nonexistent file +} + +test("in-memory files") +{ + array bytes; + for (int i=0;i<8;i++) bytes[i]=i; + array bytes2; + bytes2.resize(4); + + file f = file::mem(bytes.slice(0, 8)); + assert(f); + assert_eq(f.size(), 8); + assert(f.read(bytes2, 1)); + for (int i=0;i<4;i++) assert_eq(bytes2[i], i+1); + + //readonly + assert(!f.write(bytes2, 6)); + assert(!f.replace(bytes2)); + assert(!f.mmapw()); + + f = file::mem(bytes); + assert(f); + assert_eq(f.size(), 8); + + assert(f.write(bytes2, 6)); + assert_eq(f.size(), 10); + for (int i=0;i<6;i++) assert_eq(bytes[i], i); + for (int i=0;i<4;i++) assert_eq(bytes[i+6], i+1); + + assert(f.replace(bytes2)); + for (int i=0;i<4;i++) assert_eq(bytes[i], i+1); + assert_eq(f.size(), 4); } #endif diff --git a/arlib/file-unix.cpp b/arlib/file-unix.cpp index fad2c12..93e6449 100644 --- a/arlib/file-unix.cpp +++ b/arlib/file-unix.cpp @@ -129,13 +129,19 @@ static long pagesize; namespace { - class file_fs : public filewrite { + class file_unix : public file::impl { public: int fd; - file_fs(cstring filename, int fd) : filewrite(filename), fd(fd) + file_unix(int fd) : fd(fd) {} + + size_t size() { - len = lseek(fd, 0, SEEK_END); + return lseek(fd, 0, SEEK_END); + } + bool resize(size_t newsize) + { + return (ftruncate(this->fd, newsize)==0); } size_t read(arrayvieww target, size_t start) @@ -144,14 +150,6 @@ namespace { if (ret<0) return 0; else return ret; } - - bool resize(size_t newsize) - { - bool ret = (ftruncate(this->fd, newsize)==0); - len = lseek(fd, 0, SEEK_END); - return ret; - } - bool write(arrayview data, size_t start) { size_t ret = pwrite(fd, data.ptr(), data.size(), start); @@ -159,10 +157,10 @@ namespace { else return ret; } - /*private*/ arrayvieww mmap(bool write, size_t start, size_t len) + /*private*/ arrayvieww mmap(bool writable, size_t start, size_t len) { size_t offset = start % pagesize; - void* data=::mmap(NULL, len+offset, write ? PROT_WRITE|PROT_READ : PROT_READ, MAP_SHARED, this->fd, start-offset); + void* data=::mmap(NULL, len+offset, writable ? PROT_WRITE|PROT_READ : PROT_READ, MAP_SHARED, this->fd, start-offset); if (data==MAP_FAILED) return NULL; return arrayvieww((uint8_t*)data+offset, len); } @@ -177,43 +175,31 @@ namespace { arrayvieww mmapw(size_t start, size_t len) { return mmap(true, start, len); } void unmapw(arrayvieww data) { unmap(data); } - ~file_fs() { close(fd); } + ~file_unix() { close(fd); } }; } -file* file::open_fs(cstring filename) +file::impl* file::open_impl_fs(cstring filename, mode m) { - int fd = ::open(filename, O_RDONLY); - if (fd<0) return NULL; - return new file_fs(filename, fd); -} - -filewrite* filewrite::open_fs(cstring filename, mode m) -{ - int flags[] = { O_RDWR|O_CREAT, O_RDWR, O_RDWR|O_CREAT|O_TRUNC, O_RDWR|O_CREAT|O_EXCL }; + int flags[] = { O_RDONLY, O_RDWR|O_CREAT, O_RDWR, O_RDWR|O_CREAT|O_TRUNC, O_RDWR|O_CREAT|O_EXCL }; int fd = ::open(filename, flags[m], 0666); if (fd<0) return NULL; - return new file_fs(filename, fd); + return new file_unix(fd); } -bool filewrite::unlink_fs(cstring filename) +bool file::unlink_fs(cstring filename) { int ret = ::unlink(filename); return ret==0 || (ret==-1 && errno==ENOENT); } //#ifdef ARGUI_NONE -file* file::open(cstring filename) +file::impl* file::open_impl(cstring filename, mode m) { - return open_fs(filename); + return open_impl_fs(filename, m); } -filewrite* filewrite::open(cstring filename, mode m) -{ - return open_fs(filename, m); -} - -bool filewrite::unlink(cstring filename) +bool file::unlink(cstring filename) { return unlink_fs(filename); } diff --git a/arlib/file.h b/arlib/file.h index ad817ff..ac57c7f 100644 --- a/arlib/file.h +++ b/arlib/file.h @@ -1,106 +1,187 @@ #pragma once #include "global.h" #include "string.h" +#include "array.h" class filewrite; class file : nocopy { - file(){} -protected: - file(cstring filename) : path(filename) {} - file(cstring filename, size_t len) : path(filename), len(len) {} +public: + class impl : nocopy { + friend class file; + protected: + virtual size_t size() = 0; + virtual bool resize(size_t newsize) = 0; + + virtual size_t read(arrayvieww target, size_t start) = 0; + virtual bool write(arrayview data, size_t start = 0) = 0; + virtual bool replace(arrayview data) { return resize(data.size()) && write(data); } + + virtual arrayview mmap(size_t start, size_t len) = 0; + virtual void unmap(arrayview data) = 0; + virtual arrayvieww mmapw(size_t start, size_t len) = 0; + virtual void unmapw(arrayvieww data) = 0; + + virtual ~impl() {} + }; + class implrd : impl { + friend class file; + protected: + virtual size_t size() = 0; + bool resize(size_t newsize) { return false; } + + virtual size_t read(arrayvieww target, size_t start) = 0; + bool write(arrayview data, size_t start = 0) { return false; } + bool replace(arrayview data) { return false; } + + virtual arrayview mmap(size_t start, size_t len) = 0; + virtual void unmap(arrayview data) = 0; + virtual arrayvieww mmapw(size_t start, size_t len) { return NULL; } + virtual void unmapw(arrayvieww data) {} + }; +private: + impl* core; + +public: + enum mode { + m_read, + m_write, // If the file exists, opens it. If it doesn't, creates a new file. + m_wr_existing, // Fails if the file doesn't exist. + m_replace, // If the file exists, it's either deleted and recreated, or truncated. + m_create_excl, // Fails if the file does exist. + }; + + file() : core(NULL) {} + file(file&& f) { core=f.core; f.core=NULL; } + file& operator=(file&& f) { delete core; core=f.core; f.core=NULL; return *this; } + file(impl* core) : core(core) {} + file(cstring filename, mode m = m_read) : core(NULL) { open(filename, m); } + + bool open(cstring filename, mode m = m_read) + { + delete core; + core = open_impl(filename, m); + return core; + } + void close() + { + delete core; + core = NULL; + } + +private: //This one will create the file from the filesystem. //create() can simply return create_fs(filename), or can additionally support stuff like gvfs. - static file* open_fs(cstring filename); - - class mem; + static impl* open_impl_fs(cstring filename, mode m); + //A path refers to a directory if it ends with a slash, and file otherwise. Directories may not be open()ed. + static impl* open_impl(cstring filename, mode m); public: - //While this object may look thread-safe, it isn't. One thread at the time only. - //Interacting with mmap() results doesn't count as interaction. - //A path refers to a directory if it ends with a slash, and file otherwise. Directories may not be open()ed, though listdir() is valid. - static file* open(cstring filename); - //Returns all items in the given directory path, as absolute paths. - static array listdir(cstring path); - - //If the input path is a directory, the basename is the last component after the final slash. - static string dirname(cstring path); - static string basename(cstring path); - - //Changing these two is undefined behavior. Only the implementation may do that. - string path; - size_t len; + operator bool() const { return core; } //Reading outside the file will return partial results. - virtual size_t read(arrayvieww target, size_t start) = 0; - array read() + size_t size() const { return core->size(); } + size_t read(arrayvieww target, size_t start) const { return core->read(target, start); } + array read() const { array ret; - ret.resize(this->len); + ret.resize(this->size()); size_t actual = this->read(ret, 0); ret.resize(actual); return ret; } static array read(cstring path) { - autoptr f = file::open(path); - if (f) return f->read(); + file f(path); + if (f) return f.read(); else return NULL; } + // May only be used if there are no mappings alive, not even read-only. + bool resize(size_t newsize) { return core->resize(newsize); } + //Writes outside the file will extend it. If the write starts after the current size, it's zero extended. Includes mmapw. + bool write(arrayview data, size_t start = 0) { return core->write(data, start); } + bool replace(arrayview data) { return core->replace(data); } + bool replace(cstring data) { return replace(data.bytes()); } + bool write(cstring data) { return write(data.bytes()); } + //Mappings must be deallocated before deleting the file object. //If the underlying file is changed, it's undefined whether the mappings update. To force an update, delete and recreate the mapping. //Mapping outside the file is undefined behavior. - virtual arrayview mmap(size_t start, size_t len) = 0; - arrayview mmap() { return this->mmap(0, this->len); } - virtual void unmap(arrayview data) = 0; + arrayview mmap(size_t start, size_t len) const { return core->mmap(start, len); } + arrayview mmap() const { return this->mmap(0, this->size()); } + void unmap(arrayview data) const { return core->unmap(data); } - virtual ~file() {} + arrayvieww mmapw(size_t start, size_t len) { return core->mmapw(start, len); } + arrayvieww mmapw() { return this->mmapw(0, this->size()); } + void unmapw(arrayvieww data) { return core->unmapw(data); } - //Mostly usable for debug purposes. - static file* create_mem_view(arrayview data); - static filewrite* create_mem_copy(array data); -}; - - -class filewrite : public file { -protected: - filewrite(cstring filename) : file(filename) {} - filewrite(cstring filename, size_t len) : file(filename, len) {} + ~file() { delete core; } -public: - enum mode { - m_default, // If the file exists, opens it. If it doesn't, creates a new file. (O_CREAT) (OPEN_ALWAYS) - m_existing, // Fails if the file doesn't exist. (0) (OPEN_EXISTING) - m_replace, // If the file exists, it's either deleted and recreated, or truncated. (O_CREAT|O_TRUNC) (CREATE_ALWAYS) - m_create_excl, // Fails if the file does exist. (O_CREAT|O_EXCL) (CREATE_NEW) - }; -protected: - //These refer to the physical file system. The public versions can forward to these, or can additionally support stuff like gvfs. - static filewrite* open_fs(cstring filename, mode m = m_default); - static bool unlink_fs(cstring filename); -public: - static filewrite* open(cstring filename, mode m = m_default); - static bool unlink(cstring filename); // Returns whether the file is now gone. If the file didn't exist, returns true. - - virtual bool resize(size_t newsize) = 0; // May only be used if there are no mappings alive, not even read-only. - //Writes outside the file will extend it. If the write starts after the current size, it's zero extended. Includes mmapw. - virtual bool write(arrayview data, size_t start = 0) = 0; - virtual bool replace(arrayview data) { return resize(data.size()) && write(data); } - bool replace(cstring data) { return replace(data.bytes()); } - bool write(cstring data) { return write(data.bytes()); } - - static bool write(cstring path, arrayview data) + static file mem(arrayview data) { - autoptr f = filewrite::open(path, m_replace); - return f->write(data); + return file(new file::memimpl(data)); } + static file mem(array& data) + { + return file(new file::memimpl(&data)); + } +private: + class memimpl : public file::implrd { + public: + arrayview datard; + array* datawr; // this object does not own the array + + memimpl(arrayview data) : datard(data), datawr(NULL) {} + memimpl(array* data) : datard(*data), datawr(data) {} + + size_t size() { return datard.size(); } + bool resize(size_t newsize) + { + if (!datawr) return false; + datawr->resize(newsize); + datard=*datawr; + return true; + } + + size_t read(arrayvieww target, size_t start) + { + size_t nbyte = min(target.size(), datard.size()-start); + memcpy(target.ptr(), datard.slice(start, nbyte).ptr(), nbyte); + return nbyte; + } + virtual bool write(arrayview newdata, size_t start = 0) + { + if (!datawr) return false; + size_t nbyte = newdata.size(); + datawr->reserve(start+nbyte); + memcpy(datawr->slice(start, nbyte).ptr(), newdata.ptr(), nbyte); + datard=*datawr; + return true; + } + virtual bool replace(arrayview newdata) + { + if (!datawr) return false; + *datawr = newdata; + datard = *datawr; + return true; + } + + arrayview mmap(size_t start, size_t len) { return datard.slice(start, len); } + arrayvieww mmapw(size_t start, size_t len) { if (!datawr) return NULL; return datawr->slice(start, len); } + void unmap(arrayview data) {} + void unmapw(arrayvieww data) {} + }; +public: - //The only allowed method on a file object that has an existing writable mapping is unmapw. - //Fails if it goes outside the file; use resize(). - virtual arrayvieww mmapw(size_t start, size_t len) = 0; - arrayvieww mmapw() { return this->mmapw(0, this->len); } - virtual void unmapw(arrayvieww data) = 0; + //Returns all items in the given directory path, as absolute paths. + static array listdir(cstring path); + static bool unlink(cstring filename); // Returns whether the file is now gone. If the file didn't exist, returns true. + //If the input path is a directory, the basename is blank. + static string dirname(cstring path); + static string basename(cstring path); +private: + static bool unlink_fs(cstring filename); }; void _window_init_file();