Cthulhu  0.2.10
Cthulhu compiler collection
virtual.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 
3 #include "common.h"
4 
5 #include "arena/arena.h"
6 #include "os/os.h"
7 
8 #include "io/impl.h"
9 #include "std/map.h"
10 
11 #include "base/util.h"
12 #include "base/panic.h"
13 
14 #include "core/macros.h"
15 
16 typedef struct virtual_t
17 {
18  const char *name;
19 } virtual_t;
20 
21 typedef struct virtual_file_t
22 {
23  // TODO: mutex
24 
25  char *data;
26  size_t used;
27  size_t size;
29 
30 typedef struct virtual_dir_t
31 {
34 
35 typedef union virtual_inode_t
36 {
40 
41 // io impl
42 
43 typedef struct virtual_io_t
44 {
46  size_t offset;
47 } virtual_io_t;
48 
49 static virtual_io_t *vfs_data(io_t *self)
50 {
51  return io_data(self);
52 }
53 
54 static size_t vfs_read(io_t *self, void *dst, size_t size)
55 {
56  virtual_io_t *io = vfs_data(self);
57  size_t len = CT_MIN(size, io->data->used - io->offset);
58  ctu_memcpy(dst, io->data->data + io->offset, len);
59  io->offset += len;
60  return len;
61 }
62 
63 static size_t vfs_write(io_t *self, const void *src, size_t size)
64 {
65  virtual_io_t *io = vfs_data(self);
66  virtual_file_t *data = io->data;
67 
68  data->used = CT_MAX(data->used, io->offset + size);
69  if (io->offset + size > data->size)
70  {
71  size_t new_size = CT_MAX(data->size * 2, io->offset + size);
72  data->data = arena_realloc(data->data, new_size, data->size, self->arena);
73  data->size = new_size;
74  }
75 
76  ctu_memcpy(data->data + io->offset, src, size);
77  io->offset += size;
78 
79  return size;
80 }
81 
82 static size_t vfs_size(io_t *self)
83 {
84  virtual_io_t *io = vfs_data(self);
85  return io->data->used;
86 }
87 
88 static size_t vfs_seek(io_t *self, size_t offset)
89 {
90  virtual_io_t *io = vfs_data(self);
91  io->offset = CT_MIN(offset, io->data->used);
92  return io->offset;
93 }
94 
95 static void *vfs_map(io_t *self, os_protect_t protect)
96 {
97  CTASSERTF(!(protect & eOsProtectExecute), "cannot map vfs io object as executable `%s`", io_name(self));
98 
99  virtual_io_t *io = vfs_data(self);
100  return io->data->data;
101 }
102 
103 static os_error_t vfs_close(io_t *self)
104 {
105  CT_UNUSED(self);
106  return eOsSuccess;
107 }
108 
109 static const io_callbacks_t kVirtualCallbacks = {
110  .fn_read = vfs_read,
111  .fn_write = vfs_write,
112 
113  .fn_get_size = vfs_size,
114  .fn_seek = vfs_seek,
115 
116  .fn_map = vfs_map,
117  .fn_close = vfs_close,
118 
119  .size = sizeof(virtual_io_t),
120 };
121 
122 static io_t *vfs_io(fs_inode_t *inode, os_access_t flags, arena_t *arena)
123 {
124  virtual_file_t *file = inode_data(inode);
125  const char *name = inode_name(inode);
126 
127  virtual_io_t data = {
128  .data = file,
129  .offset = 0
130  };
131 
132  if (flags & eOsAccessTruncate)
133  {
134  file->used = 0;
135  }
136 
137  return io_new(&kVirtualCallbacks, flags, name, &data, arena);
138 }
139 
140 // fs impl
141 
142 static fs_inode_t *virtual_dir(fs_t *fs, const char *name)
143 {
144  virtual_dir_t dir = {
145  .dirents = map_optimal(64, kTypeInfoString, fs->arena)
146  };
147 
148  virtual_inode_t inode = {
149  .dir = dir
150  };
151 
152  return inode_dir(fs, name, &inode);
153 }
154 
155 static fs_inode_t *vfs_query_node(fs_t *fs, const fs_inode_t *self, const char *name)
156 {
157  CT_UNUSED(fs);
158 
159  if (ctu_strlen(name) == 0) return (fs_inode_t*)self;
160  if (ctu_strlen(name) == 1)
161  {
162  if (name[0] == '.') return (fs_inode_t*)self;
163  if (name[0] == '/') return (fs_inode_t*)self;
164  }
165 
167  return map_get_default(dir->dirents, name, &gInvalidFileNode);
168 }
169 
170 static io_t *vfs_query_file(fs_t *fs, fs_inode_t *self, os_access_t flags)
171 {
172  return vfs_io(self, flags, fs->arena);
173 }
174 
175 static inode_result_t vfs_create_dir(fs_t *fs, fs_inode_t *self, const char *name)
176 {
177  virtual_dir_t *dir = inode_data(self);
178  fs_inode_t *node = virtual_dir(fs, name);
179  map_set(dir->dirents, name, node);
180 
181  inode_result_t result = { .node = node };
182  return result;
183 }
184 
185 static os_error_t vfs_delete_dir(fs_t *fs, fs_inode_t *self, const char *name)
186 {
187  CT_UNUSED(fs);
188 
189  virtual_dir_t *dir = inode_data(self);
190  bool result = map_delete(dir->dirents, name);
191  return result ? eOsSuccess : eOsNotFound;
192 }
193 
194 static inode_result_t vfs_create_file(fs_t *fs, fs_inode_t *parent, const char *name)
195 {
196  virtual_dir_t *dir = inode_data(parent);
197 
198  virtual_file_t file = {
199  .data = ARENA_MALLOC(0x1000, "data", parent, fs->arena),
200  .used = 0,
201  .size = 0x1000
202  };
203 
204  virtual_inode_t inode = {
205  .file = file
206  };
207 
208  fs_inode_t *node = inode_file(fs, name, &inode);
209  ARENA_REPARENT(node, parent, fs->arena);
210  ARENA_REPARENT(file.data, node, fs->arena);
211  map_set(dir->dirents, name, node);
212  inode_result_t result = { .node = node };
213  return result;
214 }
215 
216 static os_error_t vfs_delete_file(fs_t *fs, fs_inode_t *self, const char *name)
217 {
218  CT_UNUSED(fs);
219 
220  virtual_dir_t *dir = inode_data(self);
221  bool result = map_delete(dir->dirents, name);
222  return result ? eOsSuccess : eOsNotFound;
223 }
224 
225 static os_error_t vfs_iter_begin(fs_t *fs, const fs_inode_t *dir, fs_iter_t *iter)
226 {
227  CT_UNUSED(fs);
228 
230  map_iter_t *result = iter_data(iter);
231  *result = map_iter(data->dirents);
232 
233  return eOsSuccess;
234 }
235 
236 static os_error_t vfs_iter_next(fs_iter_t *iter)
237 {
238  map_iter_t *it = iter_data(iter);
239  if (!map_has_next(it))
240  return eOsNotFound;
241 
242  map_entry_t entry = map_next(it);
243  iter->current = entry.value;
244 
245  return eOsSuccess;
246 }
247 
248 static os_error_t vfs_iter_end(fs_iter_t *iter)
249 {
250  CT_UNUSED(iter);
251  return eOsSuccess;
252 }
253 
254 static const fs_callbacks_t kVirtualInterface = {
255  .pfn_query_node = vfs_query_node,
256  .pfn_query_file = vfs_query_file,
257 
258  .pfn_create_dir = vfs_create_dir,
259  .pfn_delete_dir = vfs_delete_dir,
260 
261  .pfn_create_file = vfs_create_file,
262  .pfn_delete_file = vfs_delete_file,
263 
264  .pfn_iter_begin = vfs_iter_begin,
265  .pfn_iter_next = vfs_iter_next,
266  .pfn_iter_end = vfs_iter_end,
267 
268  .iter_size = sizeof(map_iter_t),
269  .inode_size = sizeof(virtual_inode_t)
270 };
271 
272 STA_DECL
273 fs_t *fs_virtual(const char *name, arena_t *arena)
274 {
275  CTASSERT(name != NULL);
276 
277  virtual_t self = {
278  .name = name
279  };
280 
281  virtual_dir_t dir = {
282  .dirents = map_optimal(64, kTypeInfoString, arena),
283  };
284 
285  virtual_inode_t inode = {
286  .dir = dir
287  };
288 
289  return fs_new(&inode, &kVirtualInterface, &self, sizeof(virtual_t), arena);
290 }
fs_inode_t gInvalidFileNode
Definition: common.c:15
const char * inode_name(const fs_inode_t *inode)
Definition: common.c:63
void * inode_data(fs_inode_t *inode)
Definition: common.c:41
fs_inode_t * inode_file(fs_t *fs, const char *name, const void *data)
Definition: common.c:31
fs_inode_t * inode_dir(fs_t *fs, const char *name, const void *data)
Definition: common.c:36
fs_t * fs_new(void *root, const fs_callbacks_t *cb, const void *data, size_t size, arena_t *arena)
Definition: common.c:93
void * iter_data(fs_iter_t *iter)
Definition: common.c:48
CT_NODISCARD size_t size
Definition: scan.h:128
os_protect_t
file mapping memory protection
Definition: core.h:47
os_access_t
file access mode
Definition: core.h:38
#define STA_DECL
sal2 annotation on function implementations to copy annotations from the declaration
CT_NOALIAS CT_BASE_API void * ctu_memcpy(STA_WRITES(size) void *CT_RESTRICT dst, STA_READS(size) const void *CT_RESTRICT src, size_t size)
copy memory from one location to another equivalent to memcpy but with safety checks
CT_NODISCARD CT_PUREFN CT_BASE_API size_t ctu_strlen(const char *str)
get the length of a string not including the null terminator equivalent to strlen but with safety che...
Definition: util.c:87
STA_DECL fs_t * fs_virtual(const char *name, arena_t *arena)
create a virtual filesystem interface
Definition: virtual.c:273
CT_NODISCARD CT_PUREFN CT_STD_API void * map_get_default(const map_t *map, const void *key, void *other)
get a value from a map or a default value
Definition: map.c:333
CT_NODISCARD CT_STD_API map_t * map_optimal(size_t size, hash_info_t info, arena_t *arena)
create a new map with an optimal size
Definition: optimal.c:85
CT_NODISCARD CT_PUREFN CT_STD_API bool map_has_next(const map_iter_t *iter)
check if a map iterator has more elements
Definition: map.c:509
CT_STD_API bool map_delete(map_t *map, const void *key)
delete a key-value pair from a map
Definition: map.c:361
CT_NODISCARD CT_PUREFN CT_STD_API map_iter_t map_iter(const map_t *map)
create a new map iterator
Definition: map.c:456
CT_STD_API void map_set(map_t *map, const void *key, void *value)
set a key-value pair in a map
Definition: map.c:294
CT_NODISCARD CT_NOALIAS CT_STD_API map_entry_t map_next(map_iter_t *iter)
get the next key-value pair from a map iterator
Definition: map.c:476
CT_PUREFN CT_IO_API void * io_data(io_t *io)
get the user data from an io object
Definition: common.c:10
CT_IO_API io_t * io_new(const io_callbacks_t *cb, os_access_t flags, const char *name, STA_READS(cb->size) const void *data, arena_t *arena)
create a new IO object for a given interface
CT_NODISCARD CT_IO_API const char * io_name(const io_t *io)
get the name of an io object
Definition: io.c:112
#define CT_MIN(L, R)
Definition: macros.h:38
#define CT_UNUSED(x)
mark a variable as unused
Definition: macros.h:46
#define CT_MAX(L, R)
Definition: macros.h:34
#define ARENA_REPARENT(arena, ptr, parent)
reparent a pointer in a custom allocator
Definition: arena.h:391
CT_NODISCARD CT_ARENA_API void * arena_realloc(STA_RELEASE void *ptr, size_t new_size, size_t old_size, arena_t *arena)
resize a memory allocation from a custom allocator
#define ARENA_MALLOC(size, name, parent, arena)
allocate memory from a custom allocator
Definition: arena.h:392
#define CTASSERT(expr)
assert a condition, prints the condition as a message
Definition: panic.h:130
#define CTASSERTF(expr,...)
assert a condition with a message and optional format arguments
Definition: panic.h:116
CT_STD_API const hash_info_t kTypeInfoString
type information for a c style string
Definition: typeinfo.c:35
CT_NODISCARD OUT_NOTNULL os_inode_t * dir
Definition: os.h:245
@ eOsNotFound
Definition: posix.h:25
@ eOsSuccess
Definition: posix.h:24
an allocator object
Definition: arena.h:86
fs_query_node_t pfn_query_node
Definition: common.h:53
fs_inode_t * current
Definition: common.h:23
Definition: common.h:72
arena_t * arena
Definition: common.h:74
fs_inode_t * node
Definition: common.h:29
io callback interface
Definition: impl.h:86
io_read_t fn_read
read callback may be NULL on non-readable objects
Definition: impl.h:89
io object implementation
Definition: impl.h:122
a key-value pair in a map
Definition: map.h:175
void * value
the value of this entry
Definition: map.h:177
a map iterator handle
Definition: map.h:183
an unordered hash map
Definition: map.h:38
map_t * dirents
map<const char *, fs_inode_t *>
Definition: virtual.c:32
size_t size
Definition: virtual.c:27
char * data
Definition: virtual.c:25
size_t used
Definition: virtual.c:26
size_t offset
Definition: virtual.c:46
virtual_file_t * data
Definition: virtual.c:45
const char * name
Definition: virtual.c:18
virtual_dir_t dir
Definition: virtual.c:38
virtual_file_t file
Definition: virtual.c:37