#include <alias/file_system.h>
#include "uv_local.h"
#define READDIR_SIZE 32
// ====================================================================================================================
struct FileExists {
uv_fs_t req;
void (*f)(void *ud, bool value);
void *ud;
};
static void FileExists_stat(uv_fs_t *req) {
struct FileExists *state = (struct FileExists *)uv_req_get_data((uv_req_t *)req);
state->f(state->ud, state->req.result >= 0);
uv_fs_req_cleanup(req);
alias_free(NULL, state, sizeof(*state), alignof(*state));
}
void alias_file_exists(alias_str path, void (*f)(void *ud, bool value), void *ud) {
struct FileExists *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
state->f = f;
state->ud = ud;
uv_req_set_data((uv_req_t *)&state->req.data, state);
bool result = false;
uv_fs_stat(alias_libuv_event_loop(), &state->req, path, FileExists_stat);
}
bool alias_file_exists_synchronous(alias_str path) {
uv_fs_t req;
uv_fs_stat(alias_libuv_event_loop(), &req, path, NULL);
bool result = req.result >= 0;
uv_fs_req_cleanup(&req);
return result;
}
struct FileFind {
uv_fs_t req;
uv_dir_t *dir;
uv_dirent_t dirents[READDIR_SIZE];
alias_str pattern;
void (*error)(void *ud);
void (*found)(void *ud, alias_str found_path);
void (*done)(void *ud);
void *ud;
};
static void FileFind_fs_closedir(uv_fs_t *req) {
struct FileFind *state = (struct FileFind *)uv_req_get_data((uv_req_t *)req);
uv_fs_req_cleanup(req);
state->done(state->ud);
alias_str_free(NULL, state->pattern);
alias_free(NULL, state, sizeof(*state), alignof(*state));
}
static void FileFind_fs_readdir(uv_fs_t *req) {
struct FileFind *state = (struct FileFind *)uv_req_get_data((uv_req_t *)req);
if(req->result <= 0) {
uv_fs_req_cleanup(req);
uv_fs_closedir(alias_libuv_event_loop(), &state->req, state->dir, FileFind_fs_closedir);
return;
}
uint32_t count = req->result;
for(uint32_t i = 0; i < count; i++) {
if(state->dirents[i].name == NULL) {
continue;
}
// TODO if(!matches(state->dirents[i].name, state->pattern)) { continue; }
state->found(state->ud, state->dirents[i].name);
}
state->dir->dirents = state->dirents;
state->dir->nentries = READDIR_SIZE;
uv_fs_req_cleanup(req);
uv_fs_readdir(alias_libuv_event_loop(), &state->req, state->dir, FileFind_fs_readdir);
}
static void FileFind_fs_opendir(uv_fs_t *req) {
struct FileFind *state = (struct FileFind *)uv_req_get_data((uv_req_t *)req);
if(req->result < 0) {
uv_fs_req_cleanup(req);
state->error(state->ud);
alias_str_free(NULL, state->pattern);
alias_free(NULL, state, sizeof(*state), alignof(*state));
} else {
state->dir = (uv_dir_t *)req->ptr;
state->dir->dirents = state->dirents;
state->dir->nentries = READDIR_SIZE;
uv_fs_req_cleanup(req);
uv_fs_readdir(alias_libuv_event_loop(), &state->req, state->dir, FileFind_fs_readdir);
}
}
void alias_file_find(alias_str path, alias_str pattern, void (*error)(void *ud),
void (*found)(void *ud, alias_str found_path), void (*done)(void *ud), void *ud) {
struct FileFind *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
state->pattern = alias_str_clone(NULL, pattern);
state->error = error;
state->found = found;
state->done = done;
state->ud = ud;
uv_req_set_data((uv_req_t *)&state->req, state);
uv_fs_opendir(alias_libuv_event_loop(), &state->req, path, FileFind_fs_opendir);
}
void alias_file_find_synchronous(alias_str path, alias_str pattern, alias_MemoryCB *mcb,
struct alias_FileFindSynchronousResults *results) {}
struct FileRead {
uv_fs_t req;
int fd;
uv_pipe_t pipe;
void (*error)(void *ud);
void (*partial)(void *ud, const void *data, uint64_t length);
void (*done)(void *ud);
void *ud;
};
static void FileRead_fs_close_cb(uv_fs_t *req) {
struct FileRead *state = (struct FileRead *)uv_req_get_data((uv_req_t *)req);
alias_free(NULL, state, sizeof(*state), alignof(*state));
}
static void FileRead_close_cb(uv_handle_t *handle) {
struct FileRead *state = (struct FileRead *)uv_handle_get_data(handle);
uv_fs_close(alias_libuv_event_loop(), &state->req, state->fd, FileRead_fs_close_cb);
}
static void FileRead_alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = alias_malloc(NULL, suggested_size, 4);
buf->len = suggested_size;
}
static void FileRead_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
struct FileRead *state = (struct FileRead *)uv_handle_get_data((uv_handle_t *)stream);
if(nread == UV_EOF) {
state->done(state->ud);
goto stop;
} else if(nread < 0) {
state->error(state->ud);
stop:
uv_read_stop(stream);
uv_close((uv_handle_t *)stream, FileRead_close_cb);
} else if(buf->base != NULL) {
state->partial(state->ud, buf->base, buf->len);
alias_free(NULL, buf->base, buf->len, 4);
}
}
static void FileRead_fs_open_cb(uv_fs_t *req) {
struct FileRead *state = (struct FileRead *)uv_req_get_data((uv_req_t *)req);
if(state->req.result < 0) {
state->error(state->ud);
alias_free(NULL, state, sizeof(*state), alignof(*state));
return;
}
state->fd = state->req.result;
uv_fs_req_cleanup(req);
uv_pipe_init(alias_libuv_event_loop(), &state->pipe, 0);
uv_pipe_open(&state->pipe, state->fd);
uv_handle_set_data((uv_handle_t *)&state->pipe, state);
uv_read_start((uv_stream_t *)&state->pipe, FileRead_alloc_cb, FileRead_read_cb);
}
void alias_file_read(alias_str path, void (*error)(void *ud),
void (*partial)(void *ud, const void *data, uint64_t length), void (*done)(void *ud), void *ud) {
struct FileRead *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
state->error = error;
state->partial = partial;
state->done = done;
state->ud = ud;
uv_req_set_data((uv_req_t *)&state->req, state);
uv_fs_open(alias_libuv_event_loop(), &state->req, path, 0, 0, FileRead_fs_open_cb);
}
struct FileReadAll {
uv_fs_t req;
uv_buf_t buf;
int fd;
alias_MemoryCB *mcb;
void (*error)(void *ud);
void (*done)(void *ud, const void *data, uint64_t length);
void *ud;
};
static void FileReadAll_fs_close_cb(uv_fs_t *req) {
struct FileReadAll *state = (struct FileReadAll *)uv_req_get_data((uv_req_t *)req);
uv_fs_req_cleanup(req);
alias_free(NULL, state, sizeof(*state), alignof(*state));
}
static void FileReadAll_fs_read_cb(uv_fs_t *req) {
struct FileReadAll *state = (struct FileReadAll *)uv_req_get_data((uv_req_t *)req);
if(state->req.result < 0) {
state->error(state->ud);
alias_free(state->mcb, state->buf.base, state->buf.len, 4);
} else {
state->done(state->ud, state->buf.base, state->buf.len);
// the data is now owned by the callee, do not free
}
uv_fs_req_cleanup(req);
uv_fs_close(alias_libuv_event_loop(), &state->req, state->fd, FileReadAll_fs_close_cb);
}
static void FileReadAll_fs_fstat_cb(uv_fs_t *req) {
struct FileReadAll *state = (struct FileReadAll *)uv_req_get_data((uv_req_t *)req);
if(state->req.result < 0) {
state->error(state->ud);
uv_fs_req_cleanup(req);
uv_fs_close(alias_libuv_event_loop(), &state->req, state->fd, FileReadAll_fs_close_cb);
} else {
state->buf.len = state->req.statbuf.st_size;
state->buf.base = alias_malloc(state->mcb, state->buf.len, 4);
uv_fs_req_cleanup(req);
uv_fs_read(alias_libuv_event_loop(), &state->req, state->fd, &state->buf, 1, 0, FileReadAll_fs_read_cb);
}
}
static void FileReadAll_fs_open_cb(uv_fs_t *req) {
struct FileReadAll *state = (struct FileReadAll *)uv_req_get_data((uv_req_t *)req);
if(state->req.result < 0) {
state->error(state->ud);
uv_fs_req_cleanup(req);
alias_free(NULL, state, sizeof(*state), alignof(*state));
} else {
state->fd = req->result;
uv_fs_req_cleanup(req);
uv_fs_fstat(alias_libuv_event_loop(), &state->req, state->fd, FileReadAll_fs_fstat_cb);
}
}
void alias_file_read_all(alias_str path, alias_MemoryCB *mcb, void (*error)(void *ud),
void (*done)(void *ud, const void *data, uint64_t length), void *ud) {
struct FileReadAll *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
state->mcb = mcb;
state->error = error;
state->done = done;
state->ud = ud;
uv_req_set_data((uv_req_t *)&state->req.data, state);
uv_fs_open(alias_libuv_event_loop(), &state->req, path, 0, 0, FileReadAll_fs_open_cb);
}
bool alias_file_read_all_synchronous(alias_str path, alias_MemoryCB *mcb, void **data_ptr, uint64_t *length_ptr) {
uv_fs_t req;
uv_fs_open(alias_libuv_event_loop(), &req, path, 0, 0, NULL);
if(req.result < 0) {
uv_fs_req_cleanup(&req);
return false;
}
int file = req.result;
uv_fs_req_cleanup(&req);
uv_fs_fstat(alias_libuv_event_loop(), &req, file, NULL);
if(req.result < 0) {
uv_fs_req_cleanup(&req);
return false;
}
*length_ptr = req.statbuf.st_size;
uv_fs_req_cleanup(&req);
*data_ptr = alias_malloc(mcb, *length_ptr, 4);
uv_buf_t buf;
buf.base = *data_ptr;
buf.len = *length_ptr;
bool error = false;
uv_fs_read(alias_libuv_event_loop(), &req, file, &buf, 1, 0, NULL);
if(req.result < 0) {
uv_fs_req_cleanup(&req);
alias_free(mcb, *data_ptr, *length_ptr, 4);
error = true;
}
uv_fs_req_cleanup(&req);
uv_fs_close(alias_libuv_event_loop(), &req, file, NULL);
if(req.result < 0) {
uv_fs_req_cleanup(&req);
if(!error) {
alias_free(mcb, *data_ptr, *length_ptr, 4);
}
return false;
}
uv_fs_req_cleanup(&req);
return !error;
}
struct FileWrite {
uv_fs_t req;
void (*error)(void *ud);
void (*f)(void *ud, struct alias_FileWriter *writer);
void *ud;
};
static void FileWrite_fs_open_cb(uv_fs_t *req) {
struct FileWrite *state = (struct FileWrite *)uv_req_get_data((uv_req_t *)req);
if(req->result >= 0) {
struct alias_FileWriter writer;
writer.fd = req->result;
uv_pipe_init(alias_libuv_event_loop(), &writer.pipe, 0);
uv_pipe_open(&writer.pipe, writer.fd);
state->f(state->ud, &writer);
alias_FileWriter_close(&writer);
} else {
state->error(state->ud);
}
uv_fs_req_cleanup(req);
}
void alias_file_write(alias_str path, void (*error)(void *ud), void (*f)(void *ud, struct alias_FileWriter *writer), void *ud) {
struct FileWrite *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
state->error = error;
state->f = f;
state->ud = ud;
uv_req_set_data((uv_req_t *)&state->req, state);
uv_fs_open(alias_libuv_event_loop(), &state->req, path, UV_FS_O_CREAT | UV_FS_O_WRONLY, 0640, FileWrite_fs_open_cb);
}
bool alias_file_write_synchronous(alias_str path, struct alias_FileWriter *writer) {
uv_fs_t req;
uv_fs_open(alias_libuv_event_loop(), &req, path, UV_FS_O_CREAT | UV_FS_O_WRONLY, 0640, NULL);
if(req.result < 0) {
uv_fs_req_cleanup(&req);
return false;
}
writer->fd = req.result;
uv_fs_req_cleanup(&req);
uv_pipe_init(alias_libuv_event_loop(), &writer->pipe, 0);
uv_pipe_open(&writer->pipe, writer->fd);
return true;
}
// ====================================================================================================================
void alias_DirectorySet_init(struct alias_DirectorySet *dir) { alias_memory_clear(dir, sizeof(*dir)); }
void alias_DirectorySet_free(struct alias_DirectorySet *dir, alias_MemoryCB *mcb) {
for(uint32_t i = 0; i < dir->paths.length; i++) {
if(dir->paths.data[i].path != NULL) {
alias_str_free(mcb, dir->paths.data[i].path);
}
}
alias_Vector_free(&dir->paths, mcb);
}
uint32_t alias_DirectorySet_add(struct alias_DirectorySet *dir, alias_MemoryCB *mcb, alias_str path) {
uint32_t index = dir->paths.length;
alias_Vector_space_for(&dir->paths, mcb, 1);
alias_Vector_push(&dir->paths)->path = alias_str_clone(NULL, path);
return index;
}
void alias_DirectorySet_reset_before(struct alias_DirectorySet *dir, alias_MemoryCB *mcb, uint32_t index) {
while(dir->paths.length > index) {
dir->paths.length--;
if(dir->paths.data[dir->paths.length].path != NULL) {
alias_str_free(mcb, dir->paths.data[dir->paths.length].path);
}
}
}
struct DirectorySetExists {
const struct alias_DirectorySet *dir;
uint32_t index;
alias_str path;
void (*f)(void *ud, bool value);
void *ud;
};
static void DirectorySetExists_cb(void *state_, bool value) {
struct DirectorySetExists *state = (struct DirectorySetExists *)state_;
if(value) {
state->f(state->ud, true);
goto end;
}
if(state->index == 0) {
state->f(state->ud, false);
end:
alias_str_free(NULL, state->path);
alias_free(NULL, state, sizeof(*state), alignof(*state));
return;
}
uint32_t index = --state->index;
if(state->dir.paths[index].path != NULL) {
char * path;
alias_str_stack_format(path, "%s/%s", state->dir.paths[index].path, state->path);
result = alias_file_exists(path, DirectorySetExists_cb, state);
} else {
DirectorySetExists_cb(state_, state->dir.paths[index].exists_synchronous(state->dir.paths[index].ud, state->path));
}
}
void alias_DirectorySet_exists(const struct alias_DirectorySet *dir, alias_str path, void (*f)(void *ud, bool value),
void *ud) {
struct DirectorySetExists *state = (struct DirectorySetExists *)alias_malloc(NULL, sizeof(*state), alignof(*state));
state->dir = dir;
state->index = dir->paths.length;
state->path = alias_str_clone(NULL, path);
state->f = f;
state->ud = ud;
DirectorySetExists_cb(state, false);
}
bool alias_DirectorySet_exists_synchronous(const struct alias_DirectorySet *dir, alias_str path) {
uint32_t index_ = dir->paths.length;
for(; index; ) {
uint32_t index = --index_;
if(dir->paths.data[index].path != NULL) {
char * path;
alias_str_stack_format(path, "%s/%s", state->dir.paths[index].path, state->path);
if(alias_file_exists_synchronous(path)) {
return true;
}
} else {
if(dir->paths.data[index].exists_synchronous(dir->paths.data[index].ud, path)) {
return true;
}
}
}
return false;
}
struct DirectorySetRead {
const struct alias_DirectorySet *dir;
uint32_t index;
alias_str path;
void (*error)(void *ud);
void (*partial)(void *ud, const void *data, uint64_t length);
void (*done)(void *ud);
void *ud
};
static void DirectorySetRead_free(struct DirectorySetRead *state) {
alias_str_free(NULL, state->path);
alias_free(NULL, state, sizeof(*state), alignof(*state));
}
static void DirectorySetRead_file_read_error_cb(void *state_) {
struct DirectorySetRead *state = (struct DirectorySetRead *)state_;
state->error(state->ud);
DirectorySetRead_free(state);
}
static void DirectorySetRead_file_read_partial_cb(void *state_, const void *data, uint64_t length) {
struct DirectorySetRead *state = (struct DirectorySetRead *)state_;
state->partial(state->ud, data, length);
}
static void DirectorySetRead_file_read_done_cb(void *state_) {
struct DirectorySetRead *state = (struct DirectorySetRead *)state_;
state->done(state->ud);
DirectorySetRead_free(state);
}
static void DirectorySetRead_exists_cb(void *state_, bool value) {
struct DirectorySetRead *state = (struct DirectorySetRead *)state_;
const struct alias_Directory *current;
if(value) {
current = &state->dir->paths.data[state->index];
if(current->path != NULL) {
char * path;
alias_str_stack_format(path, "%s/%s", current->path, state->path);
alias_file_read(path, DirectorySetRead_file_read_error_cb, DirectorySetRead_file_read_partial_cb, DirectorySetRead_file_read_done_cb, state_);
} else {
void * ptr;
uint64_t length;
current->read_all_synchronous(current->ud, &ptr, &length);
state->partial(state->ud, ptr, length);
state->done(state->ud);
DirectorySetRead_free(state);
}
} else {
current = &state->dir->paths.data[--state->index];
if(current->path != NULL) {
char * path;
alias_str_stack_format(path, "%s/%s", current->path, state->path);
alias_file_exists(path, DirectorySetExists_cb, state_);
} else {
DirectorySetRead_exists_cb(state_, current->exists_synchronous(current->ud, state->path));
}
}
}
void alias_DirectorySet_read(const struct alias_DirectorySet *dir, alias_str path, void (*error)(void *ud),
void (*partial)(void *ud, const void *data, uint64_t length), void (*done)(void *ud),
void *ud) {
struct DirectorySetRead *state = (struct DirectorySetRead *)alias_malloc(NULL, sizeof(*state), alignof(*state));
state->dir = dir;
state->index = dir->paths.length;
state->path = alias_str_clone(NULL, path);
state->error = error;
state->partial = partial;
state->done = done;
DirectorySetRead_exists_cb(state, false);
}
void alias_DirectorySet_read_all(const struct alias_DirectorySet *dir, alias_str path, void (*error)(void *ud),
void (*done)(void *ud, const void *data, uint64_t length), void *ud) {}
bool alias_DirectorySet_read_all_synchronous(const struct alias_DirectorySet *dir, alias_str path, void **data_ptr,
uint64_t *length_ptr) {
return false;
}
void alias_DirectorySet_write(const struct alias_DirectorySet *dir, alias_str path, struct alias_FileWriter *writer) {}
// ====================================================================================================================
struct FileWriterWrite {
uv_write_t req;
void (*error)(void *ud);
void (*done)(void *ud);
void *ud;
};
static void FileWriterWrite_write_cb(uv_write_t *req, int status) {
struct FileWriterWrite *state = (struct FileWriterWrite *)uv_req_get_data((uv_req_t *)req);
if(status < 0) {
state->error(state->ud);
} else {
state->done(state->ud);
}
alias_free(NULL, state, sizeof(*state), alignof(*state));
}
void alias_FileWriter_write(struct alias_FileWriter *writer, const void *data, uint64_t length, void (*error)(void *ud), void (*done)(void *ud), void *ud) {
uv_buf_t buf;
buf.base = (void *)data;
buf.len = length;
struct FileWriterWrite *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
state->error = error;
state->done = done;
state->ud = ud;
uv_req_set_data((uv_req_t *)&state->req, state);
uv_write(&state->req, (uv_stream_t *)&writer->pipe, &buf, 1, FileWriterWrite_write_cb);
}
bool alias_FileWriter_write_synchronous(struct alias_FileWriter *writer, const void *data, uint64_t length) {
uv_buf_t buf;
buf.base = (void *)data;
buf.len = length;
while(buf.len > 0) {
int result = uv_try_write((uv_stream_t *)&writer->pipe, &buf, 1);
if(result == UV_EAGAIN) {
continue;
}
if(result < 0) {
return false;
}
if(result >= buf.len) {
break;
}
buf.base += result;
buf.len -= result;
}
return true;
}
static void FileWriterClose_fs_close(uv_fs_t *req) {
alias_free(NULL, req, sizeof(*req), alignof(*req));
}
static void FileWriterClose_close(uv_handle_t *handle) {
struct alias_FileWriter *writer = (struct alias_FileWriter *)uv_handle_get_data(handle);
uv_fs_t *req = (uv_fs_t *)alias_malloc(NULL, sizeof(*req), alignof(*req));
uv_fs_close(alias_libuv_event_loop(), req, writer->fd, FileWriterClose_fs_close);
}
void alias_FileWriter_close(struct alias_FileWriter *writer) {
uv_handle_set_data((uv_handle_t *)&writer->pipe, writer);
uv_close((uv_handle_t *)&writer->pipe, FileWriterClose_close);
}