Branch data Line data Source code
1 : : #include "utils.h"
2 : :
3 : : #include <libplacebo/cache.h>
4 : :
5 : : // Returns "foo" for even keys, "bar" for odd
6 : 2 : static pl_cache_obj lookup_foobar(void *priv, uint64_t key)
7 : : {
8 : 2 : return (pl_cache_obj) {
9 : : .key = 0xFFFF, // test key sanity
10 [ + + ]: 2 : .data = (key & 1) ? "bar" : "foo",
11 : : .size = 3,
12 : : };
13 : : }
14 : :
15 : 5 : static void update_count(void *priv, pl_cache_obj obj)
16 : : {
17 : : int *count = priv;
18 [ + + ]: 5 : *count += obj.size ? 1 : -1;
19 : 5 : }
20 : :
21 : : enum {
22 : : KEY1 = 0x9c65575f419288f5,
23 : : KEY2 = 0x92da969be9b88086,
24 : : KEY3 = 0x7fcb62540b00bc8b,
25 : : KEY4 = 0x46c60ec11af9dde3,
26 : : KEY5 = 0xcb6760b98ece2477,
27 : : KEY6 = 0xf37dc72b7f9e5c88,
28 : : KEY7 = 0x30c18c962d82e5f5,
29 : : };
30 : :
31 : 1 : int main()
32 : : {
33 : 1 : pl_log log = pl_test_logger();
34 : 1 : pl_cache test = pl_cache_create(pl_cache_params(
35 : : .log = log,
36 : : .max_object_size = 16,
37 : : .max_total_size = 32,
38 : : ));
39 : :
40 : 1 : pl_cache_obj obj1 = { .key = KEY1, .data = "abc", .size = 3 };
41 : 1 : pl_cache_obj obj2 = { .key = KEY2, .data = "de", .size = 2 };
42 : 1 : pl_cache_obj obj3 = { .key = KEY3, .data = "xyzw", .size = 4 };
43 [ - + ]: 1 : REQUIRE_CMP(pl_cache_signature(test), ==, 0x0, PRIu64);
44 : :
45 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj1));
46 [ - + ]: 1 : REQUIRE_CMP(pl_cache_signature(test), ==, KEY1, PRIu64);
47 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj2));
48 [ - + ]: 1 : REQUIRE_CMP(pl_cache_signature(test), ==, KEY1 ^ KEY2, PRIu64);
49 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj3));
50 [ - + ]: 1 : REQUIRE_CMP(pl_cache_signature(test), ==, KEY1 ^ KEY2 ^ KEY3, PRIu64);
51 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 9, "zu");
52 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d");
53 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj2)); // delete KEY2
54 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 7, "zu");
55 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 2, "d");
56 [ - + ]: 1 : REQUIRE_CMP(pl_cache_signature(test), ==, KEY1 ^ KEY3, PRIu64);
57 : :
58 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj1));
59 [ - + ]: 1 : REQUIRE(!pl_cache_get(test, &obj2));
60 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj3));
61 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 0, "zu");
62 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 0, "d");
63 : 1 : REQUIRE_MEMEQ(obj1.data, "abc", 3);
64 : 1 : REQUIRE_MEMEQ(obj3.data, "xyzw", 4);
65 : :
66 : : // Re-insert removed objects (in reversed order)
67 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj3));
68 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj1));
69 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 7, "zu");
70 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 2, "d");
71 : :
72 : : uint8_t ref[72];
73 : : memset(ref, 0xbe, sizeof(ref));
74 : : uint8_t *refp = ref;
75 : :
76 : : #define PAD_ALIGN(x) PL_ALIGN2(x, sizeof(uint32_t))
77 : : #define W(type, ...) \
78 : : do { \
79 : : size_t sz = sizeof((type){__VA_ARGS__}); \
80 : : pl_assert(ref + sizeof(ref) - refp >= sz); \
81 : : memcpy(refp, &(type){__VA_ARGS__}, sz); \
82 : : refp += sz; \
83 : : size_t pad_sz = PAD_ALIGN(sz) - sz; \
84 : : pl_assert(ref + sizeof(ref) - refp >= pad_sz); \
85 : : memcpy(refp, &(char[PAD_ALIGN(1)]){0}, pad_sz); \
86 : : refp += pad_sz; \
87 : : } while (0)
88 : :
89 : : W(char[], 'p', 'l', '_', 'c', 'a', 'c', 'h', 'e'); // cache magic
90 : : W(uint32_t, 1); // cache version
91 : : W(uint32_t, 2); // number of objects
92 : :
93 : : // object 3
94 : : W(uint64_t, KEY3); // key
95 : : W(uint64_t, 4); // size
96 : : #ifdef PL_HAVE_XXHASH
97 : : W(uint64_t, 0xd43612ef3fbee8be); // hash
98 : : #else
99 : : W(uint64_t, 0xec18884e5e471117); // hash
100 : : #endif
101 : : W(char[], 'x', 'y', 'z', 'w'); // data
102 : :
103 : : // object 1
104 : : W(uint64_t, KEY1); // key
105 : : W(uint64_t, 3); // size
106 : : #ifdef PL_HAVE_XXHASH
107 : : W(uint64_t, 0x78af5f94892f3950); // hash
108 : : #else
109 : : W(uint64_t, 0x3a204d408a2e2d77); // hash
110 : : #endif
111 : : W(char[], 'a', 'b', 'c'); // data
112 : :
113 : : #undef W
114 : : #undef PAD_ALIGN
115 : :
116 : : uint8_t data[100];
117 : : pl_static_assert(sizeof(data) >= sizeof(ref));
118 [ - + ]: 1 : REQUIRE_CMP(pl_cache_save(test, data, sizeof(data)), ==, sizeof(ref), "zu");
119 : 1 : REQUIRE_MEMEQ(data, ref, sizeof(ref));
120 : :
121 : 1 : pl_cache test2 = pl_cache_create(pl_cache_params( .log = log ));
122 [ - + ]: 1 : REQUIRE_CMP(pl_cache_load(test2, data, sizeof(data)), ==, 2, "d");
123 [ - + ]: 1 : REQUIRE_CMP(pl_cache_signature(test), ==, pl_cache_signature(test2), PRIu64);
124 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test2), ==, 7, "zu");
125 [ - + ]: 1 : REQUIRE_CMP(pl_cache_save(test2, NULL, 0), ==, sizeof(ref), "zu");
126 [ - + ]: 1 : REQUIRE_CMP(pl_cache_save(test2, data, sizeof(data)), ==, sizeof(ref), "zu");
127 : 1 : REQUIRE_MEMEQ(data, ref, sizeof(ref));
128 : :
129 : : // Test loading invalid data
130 [ - + ]: 1 : REQUIRE_CMP(pl_cache_load(test2, ref, 0), <, 0, "d"); // empty file
131 [ - + ]: 1 : REQUIRE_CMP(pl_cache_load(test2, ref, 5), <, 0, "d"); // truncated header
132 [ - + ]: 1 : REQUIRE_CMP(pl_cache_load(test2, ref, 64), ==, 1, "d"); // truncated object data
133 : 1 : data[sizeof(ref) - 2] = 'X'; // corrupt data
134 [ - + ]: 1 : REQUIRE_CMP(pl_cache_load(test2, data, sizeof(ref)), ==, 1, "d"); // bad checksum
135 : 1 : pl_cache_destroy(&test2);
136 : :
137 : : // Inserting too large object should fail
138 : 1 : uint8_t zero[32] = {0};
139 : 1 : pl_cache_obj obj4 = { .key = KEY4, .data = zero, .size = 32 };
140 [ - + ]: 1 : REQUIRE(!pl_cache_try_set(test, &obj4));
141 [ - + ]: 1 : REQUIRE(!pl_cache_get(test, &obj4));
142 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 7, "zu");
143 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 2, "d");
144 : :
145 : : // Inserting 16-byte object should succeed, and not purge old entries
146 : 1 : obj4 = (pl_cache_obj) { .key = KEY4, .data = zero, .size = 16 };
147 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj4));
148 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 23, "zu");
149 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d");
150 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj1));
151 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj3));
152 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj4));
153 : 1 : pl_cache_set(test, &obj1);
154 : 1 : pl_cache_set(test, &obj3);
155 : 1 : pl_cache_set(test, &obj4);
156 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 23, "zu");
157 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d");
158 : :
159 : : // Inserting another 10-byte object should purge entry KEY1
160 : 1 : pl_cache_obj obj5 = { .key = KEY5, .data = zero, .size = 10 };
161 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj5));
162 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 30, "zu");
163 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d");
164 [ - + ]: 1 : REQUIRE(!pl_cache_get(test, &obj1));
165 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj3));
166 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj4));
167 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj5));
168 : 1 : pl_cache_set(test, &obj3);
169 : 1 : pl_cache_set(test, &obj4);
170 : 1 : pl_cache_set(test, &obj5);
171 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 30, "zu");
172 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d");
173 : :
174 : : // Inserting final 6-byte object should purge entry KEY3
175 : 1 : pl_cache_obj obj6 = { .key = KEY6, .data = zero, .size = 6 };
176 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test, &obj6));
177 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 32, "zu");
178 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d");
179 [ - + ]: 1 : REQUIRE(!pl_cache_get(test, &obj3));
180 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj4));
181 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj5));
182 [ - + ]: 1 : REQUIRE(pl_cache_get(test, &obj6));
183 [ - + ]: 1 : REQUIRE_CMP(pl_cache_size(test), ==, 0, "zu");
184 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test), ==, 0, "d");
185 : : pl_cache_obj_free(&obj4);
186 : : pl_cache_obj_free(&obj5);
187 : : pl_cache_obj_free(&obj6);
188 : :
189 : : // Test callback API
190 : 1 : int num_objects = 0;
191 : 1 : test2 = pl_cache_create(pl_cache_params(
192 : : .get = lookup_foobar,
193 : : .set = update_count,
194 : : .priv = &num_objects,
195 : : ));
196 : :
197 [ - + ]: 1 : REQUIRE(pl_cache_get(test2, &obj1));
198 [ - + ]: 1 : REQUIRE_CMP(obj1.key, ==, KEY1, PRIu64);
199 [ - + ]: 1 : REQUIRE_CMP(obj1.size, ==, 3, "zu");
200 : 1 : REQUIRE_MEMEQ(obj1.data, "bar", 3);
201 [ - + ]: 1 : REQUIRE(pl_cache_get(test2, &obj2));
202 [ - + ]: 1 : REQUIRE_CMP(obj2.key, ==, KEY2, PRIu64);
203 [ - + ]: 1 : REQUIRE_CMP(obj2.size, ==, 3, "zu");
204 : 1 : REQUIRE_MEMEQ(obj2.data, "foo", 3);
205 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test2), ==, 0, "d");
206 [ - + ]: 1 : REQUIRE_CMP(num_objects, ==, 0, "d");
207 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test2, &obj1));
208 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test2, &obj2));
209 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test2, &(pl_cache_obj) { .key = KEY7, .data = "abcde", .size = 5 }));
210 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test2), ==, 3, "d");
211 [ - + ]: 1 : REQUIRE_CMP(num_objects, ==, 3, "d");
212 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test2, &obj1));
213 [ - + ]: 1 : REQUIRE(pl_cache_try_set(test2, &obj2));
214 [ - + ]: 1 : REQUIRE_CMP(pl_cache_objects(test2), ==, 1, "d");
215 [ - + ]: 1 : REQUIRE_CMP(num_objects, ==, 1, "d");
216 : 1 : pl_cache_destroy(&test2);
217 : :
218 : 1 : pl_cache_destroy(&test);
219 : 1 : pl_log_destroy(&log);
220 : : return 0;
221 : : }
|