summaryrefslogtreecommitdiffstats
path: root/src/include/libplacebo/cache.h
blob: 5897ac87fa4757247ea9e23e33945544cd1b4414 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
 * This file is part of libplacebo.
 *
 * libplacebo is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * libplacebo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with libplacebo.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef LIBPLACEBO_CACHE_H_
#define LIBPLACEBO_CACHE_H_

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

#include <libplacebo/config.h>
#include <libplacebo/common.h>
#include <libplacebo/log.h>

PL_API_BEGIN

typedef struct pl_cache_obj {
    // Cache object key. This will uniquely identify this cached object.
    uint64_t key;

    // Cache data pointer and length. 0-length cached objects are invalid
    // and will be silently dropped. You can explicitly remove a cached
    // object by overwriting it with a length 0 object.
    void *data;
    size_t size;

    // Free callback, to free memory associated with `data`. (Optional)
    // Will be called when the object is either explicitly deleted, culled
    // due to hitting size limits, or on pl_cache_destroy().
    void (*free)(void *data);
} pl_cache_obj;

struct pl_cache_params {
    // Optional `pl_log` that is used for logging internal events related
    // to the cache, such as insertions, saving and loading.
    pl_log log;

    // Size limits. If 0, no limit is imposed.
    //
    // Note: libplacebo will never detect or invalidate stale cache entries, so
    // setting an upper size limit is strongly recommended
    size_t max_object_size;
    size_t max_total_size;

    // Optional external callback to call after a cached object is modified
    // (including deletion and (re-)insertion). Note that this is not called on
    // objects which are merely pruned from the cache due to `max_total_size`,
    // so users must rely on some external mechanism to prune stale entries or
    // enforce size limits.
    //
    // Note: `pl_cache_load` does not trigger this callback.
    // Note: Ownership of `obj` does *not* pass to the caller.
    // Note: This function must be thread safe.
    void (*set)(void *priv, pl_cache_obj obj);

    // Optional external callback to call on a cache miss. Ownership of the
    // returned object passes to the `pl_cache`. Objects returned by this
    // callback *should* have a valid `free` callback, unless lifetime can be
    // externally managed and guaranteed to outlive the `pl_cache`.
    //
    // Note: This function must be thread safe.
    pl_cache_obj (*get)(void *priv, uint64_t key);

    // External context for insert/lookup.
    void *priv;
};

#define pl_cache_params(...) (&(struct pl_cache_params) { __VA_ARGS__ })
PL_API extern const struct pl_cache_params pl_cache_default_params;

// Thread-safety: Safe
//
// Note: In any context in which `pl_cache` is used, users may also pass NULL
// to disable caching. In other words, NULL is a valid `pl_cache`.
typedef const struct pl_cache_t {
    struct pl_cache_params params;
} *pl_cache;

// Create a new cache. This function will never fail.
PL_API pl_cache pl_cache_create(const struct pl_cache_params *params);

// Destroy a `pl_cache` object, including all underlying objects.
PL_API void pl_cache_destroy(pl_cache *cache);

// Explicitly clear all objects in the cache without destroying it. This is
// similar to `pl_cache_destroy`, but the cache remains valid afterwards.
//
// Note: Objects destroyed in this way *not* propagated to the `set` callback.
PL_API void pl_cache_reset(pl_cache cache);

// Return the current internal number of objects and total size (bytes)
PL_API int pl_cache_objects(pl_cache cache);
PL_API size_t pl_cache_size(pl_cache cache);

// --- Cache saving and loading APIs

// Serialize the internal state of a `pl_cache` into an abstract cache
// object that can be e.g. saved to disk and loaded again later. Returns the
// number of objects saved.
//
// Note: Using `save/load` is largely redundant with using `insert/lookup`
// callbacks, and the user should decide whether to use the explicit API or the
// callback-based API.
PL_API int pl_cache_save_ex(pl_cache cache,
                            void (*write)(void *priv, size_t size, const void *ptr),
                            void *priv);

// Load the result of a previous `pl_cache_save` call. Any duplicate entries in
// the `pl_cache` will be overwritten. Returns the number of objects loaded, or
// a negative number on serious error (e.g. corrupt header)
//
// Note: This does not trigger the `update` callback.
PL_API int pl_cache_load_ex(pl_cache cache,
                            bool (*read)(void *priv, size_t size, void *ptr),
                            void *priv);

// --- Convenience wrappers around pl_cache_save/load_ex

// Writes data directly to a pointer. Returns the number of bytes that *would*
// have been written, so this can be used on a size 0 buffer to get the required
// total size.
PL_API size_t pl_cache_save(pl_cache cache, uint8_t *data, size_t size);

// Reads data directly from a pointer. This still reads from `data`, so it does
// not avoid a copy.
PL_API int pl_cache_load(pl_cache cache, const uint8_t *data, size_t size);

// Writes/loads data to/from a FILE stream at the current position.
#define pl_cache_save_file(c, file) pl_cache_save_ex(c, pl_write_file_cb, file)
#define pl_cache_load_file(c, file) pl_cache_load_ex(c, pl_read_file_cb,  file)

static inline void pl_write_file_cb(void *priv, size_t size, const void *ptr)
{
    (void) fwrite(ptr, 1, size, (FILE *) priv);
}

static inline bool pl_read_file_cb(void *priv, size_t size, void *ptr)
{
    return fread(ptr, 1, size, (FILE *) priv) == size;
}

// --- Object modification API. Mostly intended for internal use.

// Insert a new cached object into a `pl_cache`. Returns whether successful.
// Overwrites any existing cached object with that signature, so this can be
// used to e.g. delete objects as well (set their size to 0). On success,
// ownership of `obj` passes to the `pl_cache`.
//
// Note: If `object.free` is NULL, this will perform an internal memdup. To
// bypass this (e.g. when directly adding externally managed memory), you can
// set the `free` callback to an explicit noop function.
//
// Note: `obj->data/free` will be reset to NULL on successful insertion.
PL_API bool pl_cache_try_set(pl_cache cache, pl_cache_obj *obj);

// Variant of `pl_cache_try_set` that simply frees `obj` on failure.
PL_API void pl_cache_set(pl_cache cache, pl_cache_obj *obj);

// Looks up `obj->key` in the object cache. If successful, `obj->data` is
// set to memory owned by the caller, which must be either explicitly
// re-inserted, or explicitly freed (using obj->free).
//
// Note: On failure, `obj->data/size/free` are reset to NULL.
PL_API bool pl_cache_get(pl_cache cache, pl_cache_obj *obj);

// Run a callback on every object currently stored in `cache`.
//
// Note: Running any `pl_cache_*` function on `cache` from this callback is
// undefined behavior.
PL_API void pl_cache_iterate(pl_cache cache,
                             void (*cb)(void *priv, pl_cache_obj obj),
                             void *priv);

// Utility wrapper to free a `pl_cache_obj` if necessary (and sanitize it)
static inline void pl_cache_obj_free(pl_cache_obj *obj)
{
    if (obj->free)
        obj->free(obj->data);
    obj->data = NULL;
    obj->free = NULL;
    obj->size = 0;
}

PL_API_END

#endif // LIBPLACEBO_CACHE_H_