libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
file_cache.c
1
5#include "region_impl.h"
6#include "region_file.h"
7#include "error.h"
8#include <stdint.h>
9#include <stdckdint.h>
10#include <stdlib.h>
11#include <string.h>
12
13#define INDEX_NIL SIZE_MAX
14
15struct file_cache {
16 struct region_file *rf;
17 struct timespec last_access;
18 int64_t pos[];
19};
20
21CLOD_CONST static size_t file_cache_size(const uint8_t dims) {
22 const size_t unaligned = sizeof(struct file_cache) + sizeof(int64_t) * dims;
23 return (unaligned + alignof(struct file_cache) - 1) &~ (alignof(struct file_cache) - 1);
24}
25static int64_t evictable_duration_ns(const size_t cache_size) {
26 constexpr int64_t evictable_max = UINT64_C(10 * 1000 * 1000 * 1000);
27 static atomic int cache_max = 0;
28 if (cache_max == 0) cache_max = num_procs();
29 return evictable_max - evictable_max / (int64_t)cache_max * (int64_t)cache_size;
30}
31static int64_t timespec_diff_ns(const struct timespec from, const struct timespec to) {
32 int64_t sec_diff;
33 if (ckd_sub(&sec_diff, to.tv_sec, from.tv_sec))
34 return to.tv_sec > from.tv_sec ? INT64_MAX : INT64_MIN;
35 if (ckd_mul(&sec_diff, sec_diff, 1000000000))
36 return to.tv_sec > from.tv_sec ? INT64_MAX : INT64_MIN;
37 int64_t nsec_diff;
38 if (ckd_sub(&nsec_diff, to.tv_nsec, from.tv_nsec))
39 return to.tv_nsec > from.tv_nsec ? INT64_MAX : INT64_MIN;
40 if (ckd_add(&nsec_diff, nsec_diff, sec_diff))
41 return to.tv_sec > from.tv_sec ? INT64_MAX : INT64_MIN;
42 return nsec_diff;
43}
44
45#define index(i) (assert(i < r->cache_len), (struct file_cache*)((char*)r->cache + i * file_cache_size(r->opts.dims)))
46#define fc_size file_cache_size(r->opts.dims)
47// global mutex must be held.
48enum clod_region_result region_file_get(struct clod_region *r, struct region_file **rf_ptr, const int64_t *pos, const bool create) {
49 struct timespec now;
50 if (!monotonic_now(&now)) {
51 region_error(CLOD_REGION_INVALID_USAGE, "Failed to get current time.");
53 }
54
55 const int64_t evictable_duration = evictable_duration_ns(r->cache_len);
56
57 size_t empty = INDEX_NIL;
58 for (size_t i = 0; i < r->cache_len; i++) {
59 auto const fc = index(i);
60
61 if (fc->rf == nullptr) {
62 if (empty == INDEX_NIL) empty = i;
63 } else if (memcmp(pos, fc->pos, sizeof(fc->pos[0]) * r->opts.dims) == 0) {
64 fc->last_access = now;
65 *rf_ptr = fc->rf;
66 return CLOD_REGION_OK;
67 } else if (timespec_diff_ns(fc->last_access, now) > evictable_duration) {
68 auto const res = region_file_close(fc->rf);
69 fc->rf = nullptr;
70 if (res != CLOD_REGION_OK) return res;
71 }
72 }
73
74 struct region_file *rf;
75 auto const res = region_file_open(r, &rf, pos, create);
76 if (res != CLOD_REGION_OK) return res;
77
78 if (empty == INDEX_NIL) {
79 void *new = malloc((r->cache_len + 1) * fc_size);
80 if (!new) {
81 region_error(CLOD_REGION_INVALID_USAGE, "Failed to allocate memory for file cache.");
82 region_file_close(rf);
84 }
85
86 memcpy(new, r->cache, r->cache_len * fc_size);
87 free(r->cache);
88 r->cache = new;
89 empty = r->cache_len++;
90 }
91
92 auto const fc = index(empty);
93 fc->rf = rf;
94 fc->last_access = now;
95 memcpy(fc->pos, pos, sizeof(fc->pos[0]) * r->opts.dims);
96 *rf_ptr = rf;
97
98 return CLOD_REGION_OK;
99}
100enum clod_region_result file_cache_destroy(struct clod_region *r) {
102 for (size_t i = 0; i < r->cache_len; i++) {
103 auto const fc = index(i);
104 if (fc->rf) {
105 auto const close_res = region_file_close(fc->rf);
106 if (res == CLOD_REGION_OK) res = close_res;
107 }
108 }
109 free(r->cache);
110 r->cache = nullptr;
111 r->cache_len = 0;
112 return res;
113}
clod_region_result
Definition region.h:43
@ CLOD_REGION_INVALID_USAGE
Definition region.h:49
@ CLOD_REGION_OK
Definition region.h:45
uint8_t dims
Definition region.h:173