-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkvs.h
413 lines (375 loc) · 11.6 KB
/
kvs.h
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/*
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright (c) 2022 Laczen
*/
/**
* @defgroup kvs
* @{
* @brief Key Value Store
*
* Generic key value store interface to store and retrieve key-value entries on
* different kind of memory devices e.g. RAM, FLASH (nor or nand), EEPROM, ...
*
* Key-value entries are stored as:
* Entry header: the maximum length of the header is 13 bytes.
* byte 0: |. . .. .. ..|
* | | | | |-- value length bits 0-3: value length is 1-4 bytes
* | | | |----- key length bits 0-3: key length is 1-4 bytes
* | | |------ unused
* | |-------- 1: includes wrap/erase counter, 0 no wrap counter
* |---------- odd parity bit (makes byte 0 odd parity)
* key length bytes
* value length bytes
* wrap counter (if included, 4 bytes)
* Entry data:
* key bytes (key length)
* value bytes (value length)
* fill bytes
* Entry footer:
* CRC32 value calculated over entry header and data (excluding fill).
*
* Entries are written sequentially to blocks that have a configurable size. At
* the beginning of each block a wrap counter is added to the entry. The wrap
* counter is increased each time the memory wraps around. When a new block is
* started the key value store verifies whether it needs to move old entries to
* keep a copy and does so if required.
*
* The configurable block size needs to be a power of 2. The block size limits
* the maximum size of an entry as it needs to fit within one block. The block
* size is not limited to an erase block size of the memory device, this allows
* using memory devices with non constant erase block sizes. However in this
* last case carefull parameter selection is required to guarantee that there
* will be no loss of data.
*
*/
#ifndef KVS_H_
#define KVS_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define KVS_MIN(a, b) (a < b ? a : b)
#define KVS_MAX(a, b) (a < b ? b : a)
#define KVS_ALIGNUP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define KVS_ALIGNDOWN(num, align) ((num) & ~((align) - 1))
/**
* @brief KVS interface definition
*
*/
/**
* @brief KVS constant values
*
*/
enum kvs_constants
{
KVS_PARITY_BITMASK = 0b10000000,
KVS_WRAPCNT_BITMASK = 0b01000000,
KVS_HDR_BUFSIZE = 16, /* should be larger than maximum header length */
KVS_BUFSIZE = 16,
KVS_WRAPCNTSIZE = 4,
KVS_CRCSIZE = 4,
KVS_FILLCHAR = 0b01100110,
};
/**
* @brief KVS error codes
*
*/
enum kvs_error_codes
{
KVS_ENOENT = 2, /**< No such entry */
KVS_EIO = 5, /**< I/O Error */
KVS_EAGAIN = 11, /**< No more contexts */
KVS_EFAULT = 14, /**< Bad address */
KVS_EINVAL = 22, /**< Invalid argument */
KVS_ENOSPC = 28, /**< No space left on device */
KVS_EDEADLK = 45, /**< Resource deadlock avoided */
};
/**
* @brief KVS stop codes
*
*/
enum kvs_stop_codes
{
KVS_DONE = 1, /**< Finished processing */
};
/**
* @brief KVS entry structure
*
*/
struct kvs_ent {
struct kvs *kvs; /**< pointer to the kvs */
uint32_t start; /**< start position of the entry */
uint32_t next; /**< position of the next entry */
uint32_t key_start; /**< start of key bytes (from start) */
uint32_t val_start; /**< start of value bytes (from start) */
uint32_t fil_start; /**< start of fill bytes (from start) */
uint32_t crc32; /**< crc32 calculated over the entry */
};
/**
* @brief KVS memory configuration definition
*
* This defines the functions provided to access the memory and os interface
*
*/
struct kvs_cfg {
const uint32_t bsz; /**< block or sector size (byte), power of 2! */
const uint32_t bcnt; /**< block count (including spare blocks) */
const uint32_t bspr; /**< spare block count */
const void *ctx; /**< opaque context pointer */
const void *pbuf; /**< pointer to prog buffer */
const uint32_t psz; /**< prog buffer size (byte), power of 2! */
/**
* @brief read from memory device
*
* @param[in] ctx pointer to memory context
* @param[in] off starting address
* @param[in] data pointer to buffer to place read data
* @param[in] len number of bytes
*
* @return 0 on success, -KVS_EIO on error.
*/
int (*read)(const void *ctx, uint32_t off, void *data, uint32_t len);
/**
* @brief program memory device
*
* REMARK: When writing to a memory device that needs to be erased
* before write, the first write to a erase block should wipe (erase)
* the block.
*
* @param[in] ctx pointer to memory context
* @param[in] off starting address
* @param[in] data pointer to data to be written
* @param[in] len number of bytes
*
* @return 0 on success, -KVS_EIO on error
*/
int (*prog)(const void *ctx, uint32_t off, const void *data,
uint32_t len);
/**
* @brief compare data to memory device content (optional)
*
* @param[in] ctx pointer to memory context
* @param[in] off starting address
* @param[in] data pointer to data to be compared
* @param[in] len number of bytes
*
* @return 0 on success, -KVS_EIO on error
*/
int (*comp)(const void *ctx, uint32_t off, const void *data,
uint32_t len);
/**
* @brief memory device sync
*
* @param[in] ctx pointer to memory context
* @param[in] off next writing address, passed to allow writing a end
* marker to the backend (e.g. for eeprom).
*
* @return 0 on success, error is propagated to user
*/
int (*sync)(const void *ctx, uint32_t off);
/**
* @brief memory device init function
*
* @param[in] ctx pointer to memory context
*
* @return 0 on success, error is propagated to user
*/
int (*init)(const void *ctx);
/**
* @brief memory device release function
*
* @param[in] ctx pointer to memory context
*
* @return 0 on success, error is propagated to user
*/
int (*release)(const void *ctx);
/**
* @brief os provided lock function
*
* @param[in] ctx pointer to memory context
*
* @return 0 on success, error is propagated to user
*/
int (*lock)(const void *ctx);
/**
* @brief os provided unlock function
*
* @param[in] ctx pointer to memory context
*
* @return 0 on success, error is ignored
*/
int (*unlock)(const void *ctx);
};
/**
* @brief KVS data structure
*
*/
struct kvs_data {
bool ready;
uint32_t pos; /**< current memory (write) position */
uint32_t bend; /**< current memory (write) block end */
uint32_t wrapcnt; /**< current wrap/erase counter */
};
/**
* @brief KVS structure
*
*/
struct kvs {
const struct kvs_cfg *cfg;
struct kvs_data *data;
};
/**
* @brief Helper macro to define a kvs
*
*/
#define DEFINE_KVS(_name, _bsz, _bcnt, _bspr, _ctx, _pbuf, _psz, _read, _prog, \
_comp, _sync, _init, _release, _lock, _unlock) \
struct kvs_cfg _name##_cfg = { \
.bsz = _bsz, \
.bcnt = _bcnt, \
.bspr = _bspr, \
.ctx = _ctx, \
.pbuf = _pbuf, \
.psz = _psz, \
.read = _read, \
.prog = _prog, \
.comp = _comp, \
.sync = _sync, \
.init = _init, \
.release = _release, \
.lock = _lock, \
.unlock = _unlock, \
}; \
struct kvs_data _name##_data; \
struct kvs _name = { \
.cfg = &_name##_cfg, \
.data = &_name##_data, \
}
/**
* @brief Helper macro to get a pointer to a KVS structure
*
*/
#define GET_KVS(_name) &_name
/**
* @brief mount the key value store
*
* @param[in] kvs pointer to key value store
*
* @return 0 on success, negative errorcode on error
*/
int kvs_mount(struct kvs *kvs);
/**
* @brief unmount the key value store
*
* @param[in] kvs pointer to key value store
*
* @return 0 on success, negative errorcode on error
*/
int kvs_unmount(struct kvs *kvs);
/**
* @brief erase the key value store, should be called on a unmounted fs.
* Overwrites the memory backend with a preset value (KVS_FILLCHAR) so
* that it is sure that no data is left in the memory backend.
*
* @param[in] kvs pointer to key value store
*
* @return 0 on success, negative errorcode on error
*/
int kvs_erase(struct kvs *kvs);
/**
* @brief compact the key value store (refreshes key value store and minimizes
* occupied flash).
*
* @param[in] kvs pointer to key value store
*
* @return 0 on success, negative errorcode on error
*/
int kvs_compact(const struct kvs *kvs);
/**
* @brief get a entry from the key value store
*
* @param[out] ent pointer to the entry
* @param[in] kvs pointer to key value store
* @param[in] key key of the entry
*
* @return 0 on success, negative errorcode on error
*/
int kvs_entry_get(struct kvs_ent *ent, const struct kvs *kvs, const char *key);
/**
* @brief read data from a entry in the kvs at offset
*
* @param[in] ent pointer to the entry
* @param[in] off offset from entry start
* @param[out] data
* @param[in] len bytes to read
*
* @return 0 on success, negative errorcode on error
*/
int kvs_entry_read(const struct kvs_ent *ent, uint32_t off, void *data,
uint32_t len);
/**
* @brief read value for a key in the kvs
*
* @param[in] kvs pointer to the kvs
* @param[in] key
* @param[out] value
* @param[in] len value length (bytes)
*
* @return 0 on success, negative errorcode on error
*/
int kvs_read(const struct kvs *kvs, const char *key, void *value, uint32_t len);
/**
* @brief write value for a key in the kvs
*
* @param[in] kvs pointer to the kvs
* @param[in] key
* @param[in] value
* @param[in] len value length (bytes)
*
* @return 0 on success, negative errorcode on error
*/
int kvs_write(const struct kvs *kvs, const char *key, const void *value,
uint32_t len);
/**
* @brief delete a key in the kvs
*
* @param[in] kvs pointer to the kvs
* @param[in] key
*
* @return 0 on success, negative errorcode on error
*/
int kvs_delete(const struct kvs *kvs, const char *key);
/**
* @brief walk over entries in kvs and issue a cb for each entry that starts
* with the specified key. Walking can be stopped by returning KVS_DONE
* from the callback.
*
* @param[in] kvs pointer to the kvs
* @param[in] key
* @param[in] cb callback function
* @param[in] arg callback function argument
*
* @return 0 on success, negative errorcode on error
*/
int kvs_walk(const struct kvs *kvs, const char *key,
int (*cb)(struct kvs_ent *ent, void *arg), void *arg);
/**
* @brief walk over entries in kvs and issue a cb for each entry that starts
* with the specified key, the cb is only called for the last added
* entry. Walking can be stopped by returning KVS_DONE from the callback.
*
* @param[in] kvs pointer to the kvs
* @param[in] key
* @param[in] cb callback function
* @param[in] arg callback function argument
*
* @return 0 on success, negative errorcode on error
*/
int kvs_walk_unique(const struct kvs *kvs, const char *key,
int (*cb)(struct kvs_ent *ent, void *arg), void *arg);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* KVS_H_ */