Makes a scratch directory, stores and reads entries, checks the path a key maps to, then gives three entries known last-used times and checks the size prune removes the least recently used first, the age expiry removes only the old, and neither ever touches a file the cache did not make. Last, two caches pruned as a group share one budget and one order.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#ifdef __windows__
#include <direct.h>
#include <sys/utime.h>
#define ex_mkdir(p) _mkdir(p)
#else
#include <utime.h>
#define ex_mkdir(p) mkdir(p, 0700)
#endif
#define EX_DIR "ex_disk_cache.dir"
if (!ok) {
} else {
}
}
int opt = 0;
while ((opt = getopt(argc, argv, "V:")) != -1) {
if (opt == 'V') {
} else {
return -1;
}
}
return 0;
}
static int exists(
const char* path) {
struct stat st;
return stat(path, &st) == 0;
}
char path[512];
FILE* f = NULL;
snprintf(path,
sizeof(path),
"%s/%s",
EX_DIR, name);
f = fopen(path, "wb");
if (f) {
fputs("not the cache's", f);
fclose(f);
}
}
char data[100];
char path[512];
struct utimbuf when;
memset(data,
key[0],
sizeof(data));
when.actime = time(NULL) - ago;
when.modtime = when.actime;
utime(path, &when);
}
char path[512];
}
int main(
int argc,
char** argv) {
unsigned char* data = NULL;
size_t len = 0;
uint64_t total = 0;
char p1[512], p2[512], p3[512];
const char* upper = "0123456789ABCDEF0123456789ABCDEF.bin";
fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
return 2;
}
expect_true(
"an existing directory opens", cache != NULL);
if (!cache) return 1;
expect_true(
"the same key has the same path", strcmp(p1, p2) == 0);
expect_true(
"another extension, another file", strcmp(p1, p3) != 0);
expect_true(
"the path is the directory, 32 digits and the extension",
expect_true(
"get finds it",
n_disk_cache_get(cache,
"hello", 5, NULL, 1024, &data, &len) && len == 5 && memcmp(data,
"world", 5) == 0 && data[5] ==
'\0');
free(data);
data = NULL;
free(data);
data = NULL;
expect_true(
"remove removes",
n_disk_cache_remove(cache,
"hello", 5, NULL) && !
n_disk_cache_get(cache,
"hello", 5, NULL, 1024, &data, &len));
snprintf(p1,
sizeof(p1),
"%s/notes.txt",
EX_DIR);
snprintf(p2,
sizeof(p2),
"%s/%s",
EX_DIR, upper);
remove(p1);
remove(p2);
{
if (other) {
both[0] = cache;
both[1] = other;
expect_true(
"the two oldest, one from each", !
has(cache,
"f") && !
has(other,
"g") &&
has(cache,
"h") &&
has(other,
"i"));
}
expect_true(
"the second scratch directory is empty again", rmdir(
EX_DIR "/other") == 0);
}
expect_true(
"close sets the handle to NULL", cache == NULL);
fprintf(stderr,
"ex_disk_cache: %d failure(s)\n",
failures);
return 1;
}
printf("ex_disk_cache: ok\n");
return 0;
}
void process_args(int argc, char **argv)
static void expect_true(const char *label, int cond)
static int exists(const char *path)
does a file exist
#define ex_mkdir(p)
owner only elsewhere
static void write_foreign(const char *name)
write a small file that is not an entry
#define EX_DIR
the scratch directory, made and removed by this example
static int has(const N_DISK_CACHE *cache, const char *key)
is the entry for key there
static void put_aged(const N_DISK_CACHE *cache, const char *key, long ago)
store 100 bytes under key and say it was last used ago seconds ago
volatile char key[227]
TRUE while the key is held.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_INFO
informational
const char * n_disk_cache_dir(const N_DISK_CACHE *cache)
The cache's directory, as it was opened.
#define N_DISK_CACHE_NAME_LEN
length of an entry's name before its extension: 128 bits in hexadecimal
int n_disk_cache_get(const N_DISK_CACHE *cache, const void *key, size_t key_len, const char *ext, size_t max_len, unsigned char **data, size_t *len)
Read an entry.
int n_disk_cache_path(const N_DISK_CACHE *cache, const void *key, size_t key_len, const char *ext, char *out, size_t out_len)
The file an entry lives in, whether or not it exists.
void n_disk_cache_close(N_DISK_CACHE **cache)
Forget a cache.
int n_disk_cache_put(const N_DISK_CACHE *cache, const void *key, size_t key_len, const char *ext, const void *data, size_t len)
Store an entry, replacing any under the same key and extension.
int n_disk_cache_prune_group(const N_DISK_CACHE *const *caches, int count, uint64_t max_bytes)
n_disk_cache_prune() over several caches that share one budget.
int n_disk_cache_prune(const N_DISK_CACHE *cache, uint64_t max_bytes)
Remove least recently used entries until the rest fit a budget.
int n_disk_cache_remove(const N_DISK_CACHE *cache, const void *key, size_t key_len, const char *ext)
Remove one entry, for a caller that found it unreadable.
int n_disk_cache_expire(const N_DISK_CACHE *cache, int64_t max_age_seconds)
Remove every entry unused for longer than an age.
int n_disk_cache_size(const N_DISK_CACHE *cache, uint64_t *total)
How many bytes the cache's entries take.
N_DISK_CACHE * n_disk_cache_open(const char *dir)
Use a directory as a cache.
Common headers and low-level functions & define.
a cache: only where it is
A bounded cache of files in one directory, keyed by content.