/* SPDX-License-Identifier: LGPL-2.1-only */ /* Copyright (C) 2020 Gediminas Jakutis */ #include #include #include "defs.h" static off_t cache_walker(struct stream * const restrict in, ssize_t idx); int cache_create(struct stream * const restrict in, const struct settings * const restrict s) { int ret; void *cache; try(!in->cached, err, EINVAL, "cannot create cache: stream is uncached"); try(!(cache = calloc(in->n, s->stride)), err, ENOMEM, "out of memory"); in->cache = cache; err: return ret; } int cache_populate(struct stream * const restrict in) { int ret = 0; size_t i; try(!in->cached, err, EINVAL, "cannot populate cache: stream is uncached"); for (i = 0; i < in->n && !ret; ++i) { ret = in->get_element(in, i, in->cache + i); } err: return ret; } int cache_flush(struct stream * const in) { int ret; size_t i; try(!in->cached, err, EINVAL, "no cache to flush: stream is uncached"); try(in->out != 1, err, EINVAL, "cannot flush a non-output cache"); for (i = 0; i < in->n && !ret; ++i) { ret = in->put_element(in, i, in->cache + i); } err: return ret; } int cache_destroy(struct stream * const in) { int ret; try(!in->cached, err, EINVAL, "no cache to destroy: stream uncached"); free(in->cache); in->cache = NULL; err: return ret; } int cache_transfer(struct stream * const from, struct stream * const to) { int ret; try(!from->cached || !to->cached, err, EINVAL, "cannot transfer caches of uncached streams"); try(!from->cache, err, EINVAL, "no cache to transfer"); try(!to->cache, err, EINVAL, "cannot transfer cache: recipient cache already exists"); to->cache = from->cache; from->cache = NULL; err: return ret; } int cached_get_array(struct stream * const in, ssize_t idx, struct entry_l * const data) { int ret = 0; const struct entry * const cache = in->cache; data->val = cache[idx].val; return ret; } int cached_get_list(struct stream * const restrict in, ssize_t idx, struct entry_l * const data) { int ret = 0; off_t offset; offset = cache_walker(in, idx); *data = in->cache[offset]; return ret; } int cached_put_array(struct stream * const in, ssize_t idx, const struct entry_l * const data) { int ret = 0; struct entry * const cache = in->cache; cache[idx].val = data->val; return ret; } int cached_put_list(struct stream * const restrict in, ssize_t idx, const struct entry_l * const data) { int ret = 0; off_t offset; offset = cache_walker(in, idx); in->cache[offset] = *data; return ret; } static off_t cache_walker(struct stream * const restrict in, ssize_t idx) { size_t i; off_t ret = in->prev_off; if (in->prev_idx < idx) { /* previous index smaller, walk forward */ for (i = labs(idx - in->prev_idx); i; --i) { ret = in->cache[ret].next; } } else if (in->prev_idx > idx) { /* previous index larger, walk backwards */ for (i = in->prev_idx - idx; i; --i) { ret = in->cache[ret].prev; } } /* ...or we're standing on it already, if neither */ in->prev_idx = idx; in->prev_off = ret; return ret; }