// SPDX-License-Identifier: LGPL-3.0-or-later #include "impl.h" #include #include static size_t hkvs_slab_size_to_count(size_t elt_size, size_t size) { size_t block_size = (elt_size + 1) * HKVS_SLAB_BLOCK; size_t blocks = size / block_size; size %= block_size; size_t rest = 0; if(size > HKVS_SLAB_BLOCK) { rest = (size - HKVS_SLAB_BLOCK) / elt_size; } return blocks * HKVS_SLAB_BLOCK + rest; } static size_t hkvs_slab_count_to_size(size_t elt_size, size_t count) { size_t size; if(__builtin_mul_overflow(count, elt_size + 1, &size)) return SIZE_MAX; if(__builtin_add_overflow(size, (-count) % HKVS_SLAB_BLOCK, &size)) return SIZE_MAX; return size; } int hkvs_slab_init(hkvs_slab *s, hkvs_io *io, char *param, size_t elt_size) { s->param = param; s->elt_size = elt_size; uint64_t fi = hkvs_read_u64(param + HKVS_PARAM_SLAB_FI); if(fi == 0) { s->addr = NULL; s->map_size = 0; return 0; } int r = io->open(io, fi, &s->addr, &s->map_size); if(r < 0) return r; uint64_t alloc_count = hkvs_read_u64(param + HKVS_PARAM_SLAB_ALLOC); if(alloc_count > hkvs_slab_size_to_count(elt_size, s->map_size)) { io->close(io, fi, s->addr, s->map_size); return -EBADMSG; } return 0; } int hkvs_slab_alloc(hkvs *c, hkvs_slab *s, size_t *pi) { size_t alloc_count = hkvs_slab_size(s); uint64_t at_64 = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FREE); if(at_64 < alloc_count) { size_t at = (size_t)at_64; char *p = hkvs_slab_at(s, at); for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) { uint64_t v = hkvs_read_u64(p + i * 8); if(v <= at) return -EBADMSG; if(v < alloc_count) { size_t id = (size_t)v; hkvs_write_u64(p + i * 8, UINT64_MAX); *pi = id; return 0; } } at_64 = hkvs_read_u64(p); hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, at_64); *pi = at; return 0; } if(at_64 != UINT64_MAX) { hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, UINT64_MAX); } size_t cap_count = hkvs_slab_size_to_count(s->elt_size, s->map_size); hkvs_assume(alloc_count <= cap_count); if(alloc_count == cap_count) { size_t grow_count = cap_count + (cap_count >> 3) + 1; size_t grow_size; while(true) { grow_size = hkvs_slab_count_to_size(s->elt_size, grow_count); if(grow_size != 0) break; size_t dec = (grow_count - cap_count) / 2; if(dec == 0) return -EFBIG; grow_count -= dec; } hkvs_io *io = c->io; uint64_t fi = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FI); if(fi == 0) { int r = io->create(io, &fi, &s->addr, grow_size); if(r < 0) return r; hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FI, fi); } else { int r = io->resize(io, fi, &s->addr, s->map_size, grow_size); if(r < 0) return r; } s->map_size = grow_size; } hkvs_write_u64(s->param + HKVS_PARAM_SLAB_ALLOC, alloc_count + 1); *pi = alloc_count; return 0; } void hkvs_slab_discard(hkvs_slab *s, size_t id) { size_t alloc_count = hkvs_slab_size(s); hkvs_assume(id < alloc_count); if(id == alloc_count - 1) { do { alloc_count--; } while(alloc_count > 0 && hkvs_slab_mark(s, alloc_count - 1) == 0); hkvs_write_u64(s->param + HKVS_PARAM_SLAB_ALLOC, alloc_count); return; } hkvs_slab_set_mark(s, id, 0); uint64_t free_64 = hkvs_read_u64(s->param + HKVS_PARAM_SLAB_FREE); if(id < free_64) { hkvs_write_u64(s->param + HKVS_PARAM_SLAB_FREE, id); char *p = hkvs_slab_at(s, id); hkvs_write_u64(p, free_64); for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) { hkvs_write_u64(p + i * 8, UINT64_MAX); } return; } char *p = hkvs_slab_at(s, (size_t)free_64); uint64_t v = hkvs_read_u64(p + 8); if(v >= alloc_count) { size_t i = 2; while(true) { v = hkvs_read_u64(p + i * 8); if(v < alloc_count) break; i++; if(i == HKVS_SLAB_FREEPTRS) { hkvs_write_u64(p + 8, id); return; } } while(v < id) { hkvs_write_u64(p + (i - 1) * 8, v); i++; if(i == HKVS_SLAB_FREEPTRS) break; v = hkvs_read_u64(p + i * 8); } hkvs_write_u64(p + (i - 1) * 8, id); return; } if(hkvs_read_u64(p + (HKVS_SLAB_FREEPTRS - 1) * 8) >= alloc_count) { size_t i = 1; while(v < id) { i++; v = hkvs_read_u64(p + i * 8); } uint64_t v2 = id; while(true) { hkvs_write_u64(p + i * 8, v2); if(v >= alloc_count) break; v2 = v; i++; v = hkvs_read_u64(p + i * 8); } return; } if(v < id) { uint64_t v2 = id; id = (size_t)v; size_t i = 2; do { v = hkvs_read_u64(p + i * 8); if(v >= v2) break; hkvs_write_u64(p + (i - 1) * 8, v); i++; } while(i < HKVS_SLAB_FREEPTRS); hkvs_write_u64(p + (i - 1) * 8, v2); } char *p2 = hkvs_slab_at(s, id); memcpy(p2, p, HKVS_SLAB_FREEPTRS * 8); hkvs_write_u64(p, id); for(size_t i = 1; i < HKVS_SLAB_FREEPTRS; i++) { hkvs_write_u64(p + i * 8, UINT64_MAX); } return; }