diff --git a/io/include/blue/io/file.h b/io/include/blue/io/file.h index b47e2a6..b4d5dca 100644 --- a/io/include/blue/io/file.h +++ b/io/include/blue/io/file.h @@ -1,17 +1,21 @@ -#ifndef BLUELIB_IO_FILE_H_ -#define BLUELIB_IO_FILE_H_ +#ifndef BLUE_IO_FILE_H_ +#define BLUE_IO_FILE_H_ #include +#include #include #include -#include -#define B_FILE(p) ((b_file *)(p)) +B_DECLS_BEGIN; -#define B_OFFSET_CURRENT ((size_t) - 1) +#define B_TYPE_FILE (b_file_get_type()) -struct b_directory; -struct b_path; +B_DECLARE_TYPE(b_file); + +B_TYPE_CLASS_DECLARATION_BEGIN(b_file) +B_TYPE_CLASS_DECLARATION_END(b_file) + +#define B_OFFSET_CURRENT ((size_t)-1) typedef enum b_seek_basis { B_SEEK_BEGINNING, @@ -47,11 +51,11 @@ typedef struct b_file_info { size_t length; } b_file_info; -typedef struct b_file b_file; +BLUE_API b_type b_file_get_type(void); BLUE_API b_result b_file_open( - struct b_directory *root, const struct b_path *path, b_file_mode mode, - b_file **out); + B_TYPE_FWDREF(b_directory) * root, const B_TYPE_FWDREF(b_path) * path, + b_file_mode mode, b_file **out); BLUE_API b_result b_file_open_temp(b_file_mode mode, b_file **out); BLUE_API b_result b_file_open_shadow( b_file *original, b_file_mode mode, b_file **out); @@ -62,7 +66,7 @@ BLUE_API b_status b_file_size(b_file *file, size_t *out_len); BLUE_API b_status b_file_cursor(b_file *file, size_t *out_pos); BLUE_API b_status b_file_resize(b_file *file, size_t len); BLUE_API b_status b_file_seek(b_file *file, long long offset, b_seek_basis basis); -BLUE_API const struct b_path *b_file_path(const b_file *file); +BLUE_API const B_TYPE_FWDREF(b_path) * b_file_path(const b_file *file); BLUE_API b_status b_file_swap_shadow(b_file *main_file, b_file *shadow_file); @@ -72,13 +76,6 @@ BLUE_API b_status b_file_write( b_file *file, size_t offset, size_t len, const void *buf, size_t *nr_written); -static inline b_file *b_file_retain(b_file *file) -{ - return B_FILE(b_retain(B_DSREF(file))); -} -static inline void b_file_release(b_file *file) -{ - b_release(B_DSREF(file)); -} +B_DECLS_END; #endif diff --git a/io/sys/darwin/file.c b/io/sys/darwin/file.c index d684c24..bade5bf 100644 --- a/io/sys/darwin/file.c +++ b/io/sys/darwin/file.c @@ -2,10 +2,10 @@ #include "posix.h" #include +#include #include #include #include -#include #include #include #include @@ -14,24 +14,26 @@ #include #include -struct b_file { - struct b_dsref base; +#define CHECK_FLAG(v, f) (((v) & (f)) == (f)) + +static enum b_status stream_close(struct b_stream *); +static enum b_status stream_getc(struct b_stream *, int *); +static enum b_status stream_read( + struct b_stream *, unsigned char *, size_t, size_t *); +static enum b_status stream_write( + struct b_stream *, const unsigned char *, size_t, size_t *); +static enum b_status stream_seek(struct b_stream *, long long, b_stream_seek_origin); +static enum b_status stream_tell(const struct b_stream *, size_t *); + +/*** PRIVATE DATA *************************************************************/ + +struct b_file_p { enum b_file_mode mode; int fd; - struct b_path *path; + b_path *path; }; -static void file_release(struct b_dsref *obj); - -static struct b_dsref_type file_type = { - .t_name = "corelib::file", - .t_flags = B_DSREF_FUNDAMENTAL, - .t_id = B_DSREF_TYPE_FILE, - .t_instance_size = sizeof(struct b_file), - .t_release = file_release, -}; - -#define CHECK_FLAG(v, f) (((v) & (f)) == (f)) +/*** PRIVATE FUNCTIONS ********************************************************/ static unsigned int b_mode_to_unix_mode(enum b_file_mode mode) { @@ -62,125 +64,40 @@ static unsigned int b_mode_to_unix_mode(enum b_file_mode mode) return result; } -b_result b_file_open( - struct b_directory *root, const struct b_path *path, - enum b_file_mode mode, struct b_file **out) -{ - struct b_path *file_path = b_path_retain((struct b_path *)path); - unsigned int flags = b_mode_to_unix_mode(mode); - - if (flags == (unsigned int)-1) { - b_path_release(file_path); - return B_RESULT_ERR(INVALID_ARGUMENT); - } - - struct b_path *root_path = NULL; - if (root) { - root_path = b_path_retain( - (struct b_path *)b_directory_get_path(root)); - } else { - root_path = b_path_create_cwd(); - } - - const struct b_path *parts[] = { - root_path, - file_path, - }; - - struct b_path *abs_path - = b_path_join(parts, sizeof parts / sizeof parts[0]); - b_path_release(root_path); - b_path_release(file_path); - - if (!abs_path) { - return B_RESULT_ERR(NO_MEMORY); - } - - int fd = open(b_path_ptr(abs_path), flags, 0644); - if (fd == -1) { - b_path_release(abs_path); - return B_RESULT_STATUS( - b_status_from_errno(errno, B_ERR_IO_FAILURE)); - } - - struct b_file *file - = (struct b_file *)b_dsref_type_instantiate(&file_type); - if (!file) { - close(fd); - b_path_release(abs_path); - return B_RESULT_ERR(NO_MEMORY); - } - - file->fd = fd; - file->path = abs_path; - file->mode = mode; - - *out = file; - return B_RESULT_SUCCESS; -} - -b_result b_file_open_temp(enum b_file_mode mode, struct b_file **out) -{ - mode |= B_FILE_DELETE_ON_CLOSE; - - char name[16]; - char path[128]; - - while (1) { - z__b_io_generate_tmp_filename(name, sizeof name); - snprintf(path, sizeof path, "/tmp/%s", name); - struct b_path *rpath = b_path_create_from_cstr(path); - - b_result status = b_file_open( - B_DIRECTORY_ROOT, rpath, mode | B_FILE_CREATE_ONLY, out); - - if (b_error_get_status_code(status) == B_ERR_NAME_EXISTS) { - b_path_release(rpath); - continue; - } - - b_path_unlink(rpath); - b_path_release(rpath); - - return status; - } -} - -b_result b_file_open_shadow( - struct b_file *original, enum b_file_mode mode, struct b_file **out) +static b_result file_open_shadow( + struct b_file_p *original, enum b_file_mode mode, b_file **out) { mode |= B_FILE_SHADOW | B_FILE_DELETE_ON_CLOSE | B_FILE_CREATE; - struct b_path *dir; + b_path *dir; b_path_get_directory(original->path, &dir); - struct b_string *filename = b_string_create(); + b_string *filename = b_string_create(); b_path_get_filename(original->path, filename); b_string_prepend_cstr(filename, ".~"); - struct b_path *shadow_filename - = b_path_create_from_cstr(b_string_ptr(filename)); - b_string_release(filename); + b_path *shadow_filename = b_path_create_from_cstr(b_string_ptr(filename)); + b_string_unref(filename); - const struct b_path *parts[] = { + const b_path *parts[] = { dir, shadow_filename, }; - struct b_path *shadow_filepath + b_path *shadow_filepath = b_path_join(parts, sizeof parts / sizeof parts[0]); - b_path_release(dir); - b_path_release(shadow_filename); + b_path_unref(dir); + b_path_unref(shadow_filename); if (b_path_exists(shadow_filepath)) { b_path_unlink(shadow_filepath); } - struct b_file *shadow_file; + b_file *shadow_file; b_result status = b_file_open( B_DIRECTORY_ROOT, shadow_filepath, mode, &shadow_file); - b_path_release(shadow_filepath); + b_path_unref(shadow_filepath); if (b_result_is_error(status)) { return status; @@ -190,108 +107,13 @@ b_result b_file_open_shadow( return B_RESULT_SUCCESS; } -static enum b_status stream_close(struct b_stream *stream) -{ - struct b_file *file = stream->s_ptr; - b_file_release(file); - - return B_SUCCESS; -} - -static enum b_status stream_getc(struct b_stream *stream, int *out) -{ - struct b_file *file = stream->s_ptr; - char c; - size_t nr_read = 0; - - enum b_status status - = b_file_read(file, stream->s_cursor, sizeof c, &c, &nr_read); - if (status != B_SUCCESS) { - return status; - } - - if (nr_read == 0) { - return B_ERR_NO_DATA; - } - - stream->s_cursor += nr_read; - *out = c; - return B_SUCCESS; -} - -static enum b_status stream_read( - struct b_stream *stream, unsigned char *buf, size_t max, size_t *nr_read) -{ - struct b_file *file = stream->s_ptr; - - enum b_status status - = b_file_read(file, stream->s_cursor, max, buf, nr_read); - - stream->s_cursor += *nr_read; - - return status; -} - -static enum b_status stream_write( - struct b_stream *stream, const unsigned char *buf, size_t count, - size_t *nr_written) -{ - struct b_file *file = stream->s_ptr; - - enum b_status status - = b_file_write(file, stream->s_cursor, count, buf, nr_written); - - stream->s_cursor += *nr_written; - - return status; -} - -static enum b_status stream_seek( - struct b_stream *stream, long long offset, b_stream_seek_origin origin) -{ - b_seek_basis basis; - switch (origin) { - case B_STREAM_SEEK_START: - basis = B_SEEK_BEGINNING; - break; - case B_STREAM_SEEK_CURRENT: - basis = B_SEEK_CURRENT; - break; - case B_STREAM_SEEK_END: - basis = B_SEEK_END; - break; - default: - return B_ERR_INVALID_ARGUMENT; - } - - struct b_file *file = stream->s_ptr; - - enum b_status status = b_file_seek(file, offset, basis); - if (!B_OK(status)) { - return status; - } - - return b_file_cursor(file, &stream->s_cursor); -} - -static enum b_status stream_tell(const struct b_stream *stream, size_t *pos) -{ - const struct b_file *file = stream->s_ptr; - off_t v = lseek(file->fd, 0, SEEK_CUR); - if (v == (off_t)-1) { - return b_status_from_errno(errno, B_ERR_IO_FAILURE); - } - - *pos = v; - return B_SUCCESS; -} - -const struct b_path *b_file_path(const struct b_file *file) +static const b_path *file_path(const struct b_file_p *file) { return file->path; } -enum b_status b_file_open_stream(struct b_file *file, struct b_stream **out) +static enum b_status file_open_stream( + struct b_file_p *file, b_file *obj, struct b_stream **out) { struct b_stream *stream = malloc(sizeof *stream); if (!stream) { @@ -312,7 +134,8 @@ enum b_status b_file_open_stream(struct b_file *file, struct b_stream **out) stream->s_mode |= B_STREAM_BINARY; } - stream->s_ptr = b_file_retain(file); + stream->s_ptr0 = b_file_ref(obj); + stream->s_ptr1 = file; stream->s_close = stream_close; stream->s_getc = stream_getc; stream->s_read = stream_read; @@ -325,7 +148,7 @@ enum b_status b_file_open_stream(struct b_file *file, struct b_stream **out) return B_SUCCESS; } -enum b_status b_file_stat(struct b_file *file, struct b_file_info *out) +static enum b_status file_stat(struct b_file_p *file, struct b_file_info *out) { struct stat st; int err = fstat(file->fd, &st); @@ -339,7 +162,7 @@ enum b_status b_file_stat(struct b_file *file, struct b_file_info *out) return b_file_info_from_stat(&st, out); } -enum b_status b_file_size(struct b_file *file, size_t *out_len) +static enum b_status file_size(struct b_file_p *file, size_t *out_len) { off_t cur = lseek(file->fd, 0, SEEK_CUR); if (cur == (off_t)-1) { @@ -360,7 +183,7 @@ enum b_status b_file_size(struct b_file *file, size_t *out_len) return B_SUCCESS; } -enum b_status b_file_cursor(struct b_file *file, size_t *out_pos) +static enum b_status file_cursor(struct b_file_p *file, size_t *out_pos) { off_t cur = lseek(file->fd, 0, SEEK_CUR); if (cur == (off_t)-1) { @@ -371,7 +194,7 @@ enum b_status b_file_cursor(struct b_file *file, size_t *out_pos) return B_SUCCESS; } -enum b_status b_file_resize(struct b_file *file, size_t len) +static enum b_status file_resize(struct b_file_p *file, size_t len) { int err = ftruncate(file->fd, len); if (err == 0) { @@ -381,8 +204,8 @@ enum b_status b_file_resize(struct b_file *file, size_t len) return b_status_from_errno(errno, B_ERR_IO_FAILURE); } -enum b_status b_file_seek( - struct b_file *file, long long offset, enum b_seek_basis basis) +static enum b_status file_seek( + struct b_file_p *file, long long offset, enum b_seek_basis basis) { int whence; switch (basis) { @@ -407,7 +230,8 @@ enum b_status b_file_seek( return B_SUCCESS; } -enum b_status b_file_swap_shadow(struct b_file *main_file, struct b_file *shadow_file) +static enum b_status file_swap_shadow( + struct b_file_p *main_file, struct b_file_p *shadow_file) { if (main_file->mode & B_FILE_SHADOW) { return B_ERR_NOT_SUPPORTED; @@ -417,34 +241,34 @@ enum b_status b_file_swap_shadow(struct b_file *main_file, struct b_file *shadow return B_ERR_NOT_SUPPORTED; } - struct b_path *dir_path; + b_path *dir_path; b_path_get_directory(main_file->path, &dir_path); - struct b_path *tmp_path = NULL; + b_path *tmp_path = NULL; while (1) { char tmp_name[16]; z__b_io_generate_tmp_filename(tmp_name, sizeof tmp_name); - struct b_path *tmp_name_p = b_path_create_from_cstr(tmp_name); + b_path *tmp_name_p = b_path_create_from_cstr(tmp_name); - const struct b_path *parts[] = { + const b_path *parts[] = { dir_path, tmp_name_p, }; tmp_path = b_path_join(parts, sizeof parts / sizeof parts[0]); - b_path_release(tmp_name_p); + b_path_unref(tmp_name_p); if (!b_path_exists(tmp_path)) { break; } - b_path_release(tmp_path); + b_path_unref(tmp_path); tmp_path = NULL; } - b_path_release(dir_path); + b_path_unref(dir_path); int err; @@ -452,7 +276,7 @@ enum b_status b_file_swap_shadow(struct b_file *main_file, struct b_file *shadow err = rename(b_path_ptr(shadow_file->path), b_path_ptr(main_file->path)); err = rename(b_path_ptr(tmp_path), b_path_ptr(shadow_file->path)); - b_path_release(tmp_path); + b_path_unref(tmp_path); int fd = main_file->fd; main_file->fd = shadow_file->fd; @@ -461,8 +285,8 @@ enum b_status b_file_swap_shadow(struct b_file *main_file, struct b_file *shadow return B_SUCCESS; } -enum b_status b_file_read( - struct b_file *file, size_t offset, size_t len, void *buf, size_t *nr_read) +static enum b_status file_read( + struct b_file_p *file, size_t offset, size_t len, void *buf, size_t *nr_read) { if (offset != B_OFFSET_CURRENT) { lseek(file->fd, offset, SEEK_SET); @@ -480,8 +304,8 @@ enum b_status b_file_read( return status; } -enum b_status b_file_write( - struct b_file *file, size_t offset, size_t len, const void *buf, +static enum b_status file_write( + struct b_file_p *file, size_t offset, size_t len, const void *buf, size_t *nr_written) { if (offset != B_OFFSET_CURRENT) { @@ -500,14 +324,283 @@ enum b_status b_file_write( return status; } -static void file_release(struct b_dsref *obj) +/*** STREAM FUNCTIONS *********************************************************/ + +static enum b_status stream_close(struct b_stream *stream) { - struct b_file *file = (struct b_file *)obj; + b_file *file = stream->s_ptr0; + b_file_unref(file); + + return B_SUCCESS; +} + +static enum b_status stream_getc(struct b_stream *stream, int *out) +{ + struct b_file_p *file = stream->s_ptr1; + char c; + size_t nr_read = 0; + + enum b_status status + = file_read(file, stream->s_cursor, sizeof c, &c, &nr_read); + if (status != B_SUCCESS) { + return status; + } + + if (nr_read == 0) { + return B_ERR_NO_DATA; + } + + stream->s_cursor += nr_read; + *out = c; + return B_SUCCESS; +} + +static enum b_status stream_read( + struct b_stream *stream, unsigned char *buf, size_t max, size_t *nr_read) +{ + struct b_file_p *file = stream->s_ptr1; + + enum b_status status + = file_read(file, stream->s_cursor, max, buf, nr_read); + + stream->s_cursor += *nr_read; + + return status; +} + +static enum b_status stream_write( + struct b_stream *stream, const unsigned char *buf, size_t count, + size_t *nr_written) +{ + struct b_file_p *file = stream->s_ptr1; + + enum b_status status + = file_write(file, stream->s_cursor, count, buf, nr_written); + + stream->s_cursor += *nr_written; + + return status; +} + +static enum b_status stream_seek( + struct b_stream *stream, long long offset, b_stream_seek_origin origin) +{ + b_seek_basis basis; + switch (origin) { + case B_STREAM_SEEK_START: + basis = B_SEEK_BEGINNING; + break; + case B_STREAM_SEEK_CURRENT: + basis = B_SEEK_CURRENT; + break; + case B_STREAM_SEEK_END: + basis = B_SEEK_END; + break; + default: + return B_ERR_INVALID_ARGUMENT; + } + + struct b_file_p *file = stream->s_ptr1; + + enum b_status status = file_seek(file, offset, basis); + if (!B_OK(status)) { + return status; + } + + return file_cursor(file, &stream->s_cursor); +} + +static enum b_status stream_tell(const struct b_stream *stream, size_t *pos) +{ + const struct b_file_p *file = stream->s_ptr1; + off_t v = lseek(file->fd, 0, SEEK_CUR); + if (v == (off_t)-1) { + return b_status_from_errno(errno, B_ERR_IO_FAILURE); + } + + *pos = v; + return B_SUCCESS; +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +b_result b_file_open( + b_directory *root, const b_path *path, enum b_file_mode mode, b_file **out) +{ + const b_path *file_path = path; + unsigned int flags = b_mode_to_unix_mode(mode); + + if (flags == (unsigned int)-1) { + return B_RESULT_ERR(INVALID_ARGUMENT); + } + + const b_path *root_path = NULL; + bool free_root_path = false; + if (root) { + root_path = b_directory_get_path(root); + } else { + root_path = b_path_create_cwd(); + free_root_path = true; + } + + const b_path *parts[] = { + root_path, + file_path, + }; + + b_path *abs_path = b_path_join(parts, sizeof parts / sizeof parts[0]); + + if (free_root_path) { + b_path_unref((b_path *)root_path); + } + + if (!abs_path) { + return B_RESULT_ERR(NO_MEMORY); + } + + int fd = open(b_path_ptr(abs_path), flags, 0644); + if (fd == -1) { + b_path_unref(abs_path); + return B_RESULT_STATUS( + b_status_from_errno(errno, B_ERR_IO_FAILURE)); + } + + b_file *file = b_object_create(B_TYPE_FILE); + if (!file) { + close(fd); + b_path_unref(abs_path); + return B_RESULT_ERR(NO_MEMORY); + } + + struct b_file_p *p = b_object_get_private(file, B_TYPE_FILE); + + p->fd = fd; + p->path = abs_path; + p->mode = mode; + + *out = file; + return B_RESULT_SUCCESS; +} + +b_result b_file_open_temp(enum b_file_mode mode, b_file **out) +{ + mode |= B_FILE_DELETE_ON_CLOSE; + + char name[16]; + char path[128]; + + while (1) { + z__b_io_generate_tmp_filename(name, sizeof name); + snprintf(path, sizeof path, "/tmp/%s", name); + b_path *rpath = b_path_create_from_cstr(path); + + b_result status = b_file_open( + B_DIRECTORY_ROOT, rpath, mode | B_FILE_CREATE_ONLY, out); + + if (b_error_get_status_code(status) == B_ERR_NAME_EXISTS) { + b_path_unref(rpath); + continue; + } + + b_path_unlink(rpath); + b_path_unref(rpath); + + return status; + } +} + +b_result b_file_open_shadow(b_file *original, enum b_file_mode mode, b_file **out) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_FILE, file_open_shadow, original, mode, out); +} + +const b_path *b_file_path(const b_file *file) +{ + B_CLASS_DISPATCH_STATIC_0(B_TYPE_FILE, file_path, file); +} + +enum b_status b_file_open_stream(b_file *file, struct b_stream **out) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_FILE, file_open_stream, file, file, out); +} + +enum b_status b_file_stat(b_file *file, struct b_file_info *out) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_FILE, file_stat, file, out); +} + +enum b_status b_file_size(b_file *file, size_t *out_len) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_FILE, file_size, file, out_len); +} + +enum b_status b_file_cursor(b_file *file, size_t *out_pos) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_FILE, file_cursor, file, out_pos); +} + +enum b_status b_file_resize(b_file *file, size_t len) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_FILE, file_resize, file, len); +} + +enum b_status b_file_seek(b_file *file, long long offset, enum b_seek_basis basis) +{ + B_CLASS_DISPATCH_STATIC(B_TYPE_FILE, file_seek, file, offset, basis); +} + +enum b_status b_file_swap_shadow(b_file *main_file, b_file *shadow_file) +{ + struct b_file_p *main_p = b_object_get_private(main_file, B_TYPE_FILE); + struct b_file_p *shadow_p = b_object_get_private(main_file, B_TYPE_FILE); + return file_swap_shadow(main_p, shadow_p); +} + +enum b_status b_file_read( + b_file *file, size_t offset, size_t len, void *buf, size_t *nr_read) +{ + B_CLASS_DISPATCH_STATIC( + B_TYPE_FILE, file_read, file, offset, len, buf, nr_read); +} + +enum b_status b_file_write( + b_file *file, size_t offset, size_t len, const void *buf, size_t *nr_written) +{ + B_CLASS_DISPATCH_STATIC( + B_TYPE_FILE, file_write, file, offset, len, buf, nr_written); +} + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void file_init(b_object *obj, void *priv) +{ + struct b_file_p *file = priv; +} + +static void file_fini(b_object *obj, void *priv) +{ + struct b_file_p *file = priv; close(file->fd); if (file->mode & B_FILE_DELETE_ON_CLOSE) { b_path_unlink(file->path); } - b_path_release(file->path); + b_path_unref(file->path); } + +/*** CLASS DEFINITION *********************************************************/ + +B_TYPE_CLASS_DEFINITION_BEGIN(b_file) + B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT) + B_INTERFACE_ENTRY(to_string) = NULL; + B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT) +B_TYPE_CLASS_DEFINITION_END(b_file) + +B_TYPE_DEFINITION_BEGIN(b_file) + B_TYPE_ID(0x495a73f6, 0xb8c3, 0x4e17, 0xb5f4, 0x6fc321f67c7b); + B_TYPE_CLASS(b_file_class); + B_TYPE_INSTANCE_PRIVATE(struct b_file_p); + B_TYPE_INSTANCE_INIT(file_init); + B_TYPE_INSTANCE_FINI(file_fini); +B_TYPE_DEFINITION_END(b_file)