mirror of
https://github.com/Alcaro/Flips.git
synced 2026-09-03 16:24:50 -05:00
Update Arlib
This commit is contained in:
@@ -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 <utility> // std::move
|
||||
#include "bml.h"
|
||||
#include "containers.h"
|
||||
#include "endian.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); }
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#include "file.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace {
|
||||
class file_mem : public filewrite {
|
||||
public:
|
||||
array<byte> datawr;
|
||||
arrayview<byte> datard;
|
||||
|
||||
file_mem(arrayview<byte> data) : filewrite("", data.size()), datard(data) {}
|
||||
file_mem(array<byte> data) : filewrite("", data.size()), datawr(data), datard(datawr) {}
|
||||
|
||||
size_t read(arrayvieww<byte> 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<byte> mmap(size_t start, size_t len) { return datard.slice(start, len); }
|
||||
void unmap(arrayview<byte> data) {}
|
||||
|
||||
bool resize(size_t newsize)
|
||||
{
|
||||
datawr.resize(newsize);
|
||||
datard = datawr;
|
||||
len = newsize;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write(arrayview<byte> 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<byte> mmapw(size_t start, size_t len) { return datawr.slice(start, len); }
|
||||
void unmapw(arrayvieww<byte> data) {}
|
||||
};
|
||||
}
|
||||
|
||||
file* file::create_mem_view(arrayview<byte> data)
|
||||
{
|
||||
return new file_mem(data);
|
||||
}
|
||||
|
||||
filewrite* file::create_mem_copy(array<byte> data)
|
||||
{
|
||||
return new file_mem(data);
|
||||
}
|
||||
@@ -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<file> f = file::open(READONLY_FILE);
|
||||
assert(f);
|
||||
assert(f->len);
|
||||
assert(f->len > strlen(READONLY_FILE_HEAD));
|
||||
assert(f->len >= 66000);
|
||||
array<byte> 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<byte> bytes = f.read();
|
||||
assert(bytes.size() == f.size());
|
||||
assert(!memcmp(bytes.ptr(), READONLY_FILE_HEAD, strlen(READONLY_FILE_HEAD)));
|
||||
|
||||
arrayview<byte> map = f->mmap();
|
||||
arrayview<byte> 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<byte> map2 = f->mmap();
|
||||
arrayview<byte> 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<ARRAY_SIZE(t_start);i++)
|
||||
{
|
||||
arrayview<byte> map3 = f->mmap(t_start[i], t_len[i]);
|
||||
arrayview<byte> 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<filewrite> 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<byte> actual = file::read(WRITABLE_FILE);
|
||||
assert(actual.ptr());
|
||||
assert(actual.size()==8);
|
||||
assert(!memcmp(actual.ptr(), expected, 8));
|
||||
|
||||
arrayvieww<byte> map = f->mmapw();
|
||||
arrayvieww<byte> 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<byte> bytes;
|
||||
for (int i=0;i<8;i++) bytes[i]=i;
|
||||
array<byte> 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
|
||||
|
||||
@@ -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<byte> 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<byte> data, size_t start)
|
||||
{
|
||||
size_t ret = pwrite(fd, data.ptr(), data.size(), start);
|
||||
@@ -159,10 +157,10 @@ namespace {
|
||||
else return ret;
|
||||
}
|
||||
|
||||
/*private*/ arrayvieww<byte> mmap(bool write, size_t start, size_t len)
|
||||
/*private*/ arrayvieww<byte> 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<byte>((uint8_t*)data+offset, len);
|
||||
}
|
||||
@@ -177,43 +175,31 @@ namespace {
|
||||
arrayvieww<byte> mmapw(size_t start, size_t len) { return mmap(true, start, len); }
|
||||
void unmapw(arrayvieww<byte> 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);
|
||||
}
|
||||
|
||||
221
arlib/file.h
221
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<byte> target, size_t start) = 0;
|
||||
virtual bool write(arrayview<byte> data, size_t start = 0) = 0;
|
||||
virtual bool replace(arrayview<byte> data) { return resize(data.size()) && write(data); }
|
||||
|
||||
virtual arrayview<byte> mmap(size_t start, size_t len) = 0;
|
||||
virtual void unmap(arrayview<byte> data) = 0;
|
||||
virtual arrayvieww<byte> mmapw(size_t start, size_t len) = 0;
|
||||
virtual void unmapw(arrayvieww<byte> 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<byte> target, size_t start) = 0;
|
||||
bool write(arrayview<byte> data, size_t start = 0) { return false; }
|
||||
bool replace(arrayview<byte> data) { return false; }
|
||||
|
||||
virtual arrayview<byte> mmap(size_t start, size_t len) = 0;
|
||||
virtual void unmap(arrayview<byte> data) = 0;
|
||||
virtual arrayvieww<byte> mmapw(size_t start, size_t len) { return NULL; }
|
||||
virtual void unmapw(arrayvieww<byte> 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<string> 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<byte> target, size_t start) = 0;
|
||||
array<byte> read()
|
||||
size_t size() const { return core->size(); }
|
||||
size_t read(arrayvieww<byte> target, size_t start) const { return core->read(target, start); }
|
||||
array<byte> read() const
|
||||
{
|
||||
array<byte> ret;
|
||||
ret.resize(this->len);
|
||||
ret.resize(this->size());
|
||||
size_t actual = this->read(ret, 0);
|
||||
ret.resize(actual);
|
||||
return ret;
|
||||
}
|
||||
static array<byte> read(cstring path)
|
||||
{
|
||||
autoptr<file> 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<byte> data, size_t start = 0) { return core->write(data, start); }
|
||||
bool replace(arrayview<byte> 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<byte> mmap(size_t start, size_t len) = 0;
|
||||
arrayview<byte> mmap() { return this->mmap(0, this->len); }
|
||||
virtual void unmap(arrayview<byte> data) = 0;
|
||||
arrayview<byte> mmap(size_t start, size_t len) const { return core->mmap(start, len); }
|
||||
arrayview<byte> mmap() const { return this->mmap(0, this->size()); }
|
||||
void unmap(arrayview<byte> data) const { return core->unmap(data); }
|
||||
|
||||
virtual ~file() {}
|
||||
arrayvieww<byte> mmapw(size_t start, size_t len) { return core->mmapw(start, len); }
|
||||
arrayvieww<byte> mmapw() { return this->mmapw(0, this->size()); }
|
||||
void unmapw(arrayvieww<byte> data) { return core->unmapw(data); }
|
||||
|
||||
//Mostly usable for debug purposes.
|
||||
static file* create_mem_view(arrayview<byte> data);
|
||||
static filewrite* create_mem_copy(array<byte> 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<byte> data, size_t start = 0) = 0;
|
||||
virtual bool replace(arrayview<byte> 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<byte> data)
|
||||
static file mem(arrayview<byte> data)
|
||||
{
|
||||
autoptr<filewrite> f = filewrite::open(path, m_replace);
|
||||
return f->write(data);
|
||||
return file(new file::memimpl(data));
|
||||
}
|
||||
static file mem(array<byte>& data)
|
||||
{
|
||||
return file(new file::memimpl(&data));
|
||||
}
|
||||
private:
|
||||
class memimpl : public file::implrd {
|
||||
public:
|
||||
arrayview<byte> datard;
|
||||
array<byte>* datawr; // this object does not own the array
|
||||
|
||||
memimpl(arrayview<byte> data) : datard(data), datawr(NULL) {}
|
||||
memimpl(array<byte>* 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<byte> 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<byte> 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<byte> newdata)
|
||||
{
|
||||
if (!datawr) return false;
|
||||
*datawr = newdata;
|
||||
datard = *datawr;
|
||||
return true;
|
||||
}
|
||||
|
||||
arrayview<byte> mmap(size_t start, size_t len) { return datard.slice(start, len); }
|
||||
arrayvieww<byte> mmapw(size_t start, size_t len) { if (!datawr) return NULL; return datawr->slice(start, len); }
|
||||
void unmap(arrayview<byte> data) {}
|
||||
void unmapw(arrayvieww<byte> 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<byte> mmapw(size_t start, size_t len) = 0;
|
||||
arrayvieww<byte> mmapw() { return this->mmapw(0, this->len); }
|
||||
virtual void unmapw(arrayvieww<byte> data) = 0;
|
||||
//Returns all items in the given directory path, as absolute paths.
|
||||
static array<string> 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();
|
||||
|
||||
Reference in New Issue
Block a user