summaryrefslogtreecommitdiffstats
path: root/src/cache.c
blob: de182ec8f26520082aff4462d89427c7a5cdb9ec (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
/* SPDX-License-Identifier: LGPL-2.1-only */

/* Copyright (C) 2020 Gediminas Jakutis */

#include <errno.h>
#include <stdlib.h>
#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;
}