/* SPDX-License-Identifier: LGPL-2.1-only */ /* Copyright (C) 2020 Gediminas Jakutis */ #include #include #include #include #include #include "defs.h" static int cache_rewind(struct stream * const in); int cache_create(struct stream * const in) { int ret = 0; void *cache; try(in->settings->access != cached, err, EINVAL, "cannot create cache: stream is uncached"); try(!(cache = calloc(in->n, in->settings->stride)), err, ENOMEM, "out of memory"); /* yeah... */ in->cache = cache; in->cache_a = cache; in->cache_l = cache; err: return ret; } int cache_populate(struct stream * const in) { int ret = 0; size_t i; struct entry_l *tmp; try(in->settings->access != cached, err, EINVAL, "cannot populate cache: stream is uncached"); /* if reading a a randstream, fall back to the one-element-at-a-time mode */ if (in->type == stream_randread) { for (i = 0; i < in->n && !ret; ++i) { errno = 0; tmp = in->get_next_element_direct(in); if (tmp) { /* non-cache reads CAN fail */ put(in, tmp); } else { ret = errno; break; } } } else { /* TODO */ } err: return ret; } int cache_destroy(struct stream * const in) { int ret = 0; try(in->settings->access != cached, err, EINVAL, "cannot destroy cache: stream is uncached"); free(in->cache); in->cache_l = NULL; in->cache_a = NULL; in->cache = NULL; err: return ret; } int cache_transfer(struct stream * const src, struct stream * const dest) { int ret = 0; try(src->settings->access != cached || dest->settings->access != cached, err, EINVAL, "cannot transfer caches of uncached streams"); try(!src->cache, err, EINVAL, "no cache to transfer"); try(dest->cache, err, EINVAL, "cannot transfer cache: recipient cache already exists"); dest->cache = src->cache; dest->cache_a = (struct entry *) src->cache; dest->cache_l = (struct entry_l *) src->cache; src->cache = NULL; src->cache_a = NULL; src->cache_l = NULL; err: return ret; } int cache_block_copy(struct stream const * const src, struct stream * const dest) { int ret = 0; try(src->settings->access != cached || dest->settings->access != cached, err, EINVAL, "cannot cache-copy between uncached streams"); try(!src->cache, err, EINVAL, "no cache to transfer"); try(!(src->n < dest->settings->to), err, EINVAL, "invalid copy size"); try(!dest->cache, err, EINVAL, "no cache to transfer to"); memcpy(dest->cache, src->cache_a + dest->settings->ss, (dest->settings->to - dest->settings->ss) * dest->settings->stride); dest->n = dest->settings->to - dest->settings->ss; err: return ret; } int cache_list_copy(struct stream * const src, struct stream * const dest) { int ret = 0; struct entry_l *tmp; try(src->settings->access != cached || dest->settings->access != cached, err, EINVAL, "cannot cache-copy between uncached streams"); try(!src->cache, err, EINVAL, "no cache to transfer"); try(!(src->n < dest->settings->to), err, EINVAL, "invalid copy size"); try(!dest->cache, err, EINVAL, "no cache to transfer to"); while ((tmp = get(src))) { put(dest, tmp); } cache_rewind(src); err: return ret; } int cache_block_split(struct stream * const src, struct stream * const A, struct stream * const B) { int ret = 0; try(src->n < 2, err, EINVAL, "cannot split single element stream."); /* copy everything, then change whatever's neccesary */ *A = *B = *src; A->n = A->n / 2 + A->n % 2; B->n = B->n / 2; A->index = B->index = 0; B->cache_a = B->cache_a + A->n; B->cache_l = (struct entry_l *) B->cache_a; B->cache = (char *) B->cache_a; err: return ret; } int cache_list_split(struct stream * const src, struct stream * const A, struct stream * const B) { int ret = ENOSYS; (void) src; (void) A; (void) B; rin_warn("stub!"); return ret; } struct entry_l *cached_get_array(struct stream * const in) { struct entry_l *ret = NULL; if (in->index < in->n) { ret = (struct entry_l *)(in->cache_a + in->index); ++in->index; } return ret; } struct entry_l *cached_get_list(struct stream * const in) { struct entry_l *ret = NULL; if (in->index < in->n) { ret = in->cnode; in->cnode = ret->next; ++in->index; } return ret; } int cached_put_array(struct stream * const in, const struct entry_l * const data) { int ret = 0; if (in->index < in->n) { in->cache_a[in->index].val = data->val; ++in->index; } return ret; } /* This is were fun with the fact mergesort is NOT an in-place algorightm begins. * Since we only ever need to "put" on a) generating data and b) merging lists, * we basically have to generate the list anew each and every time. For generating, * it's a no-brainer, but for lists, while reusing existing nodes COULD be done in * a chached variant, file variant cannot do this, as cross-file links are not * something that could be handled without going into pretty insane (and laughably * inefficient) lenghts either way. * * And thus alas, we have no option but to just make a list anew */ int cached_put_list(struct stream * const restrict in, const struct entry_l * const node) { int ret = 0; if (!in->cnode) { /* if this is the very first one */ in->pnode = NULL; in->cnode = in->cache_l; in->cnode->val = node->val; in->cnode->next = NULL; in->index = 0; } else { in->pnode = in->cnode; in->cnode = in->cnode + 1; /* lol says librin, lmao */ in->pnode->next = in->cnode; in->cnode->val = node->val; in->cnode->next = NULL; ++in->index; } return ret; } int cache_rewind(struct stream * const in) { int ret = 0; in->pnode = NULL; in->cnode = in->cnode ? in->cache_l : NULL; in->index = 0; return ret; }