legacy/src/data_structure/smap/smap.c (299 lines of code) (raw):

#include "smap.h" #include <cc_debug.h> #define SM_BODY(_sm) ((char *)(_sm) + SMAP_HEADER_SIZE) #define SCAN_THRESHOLD 64 static inline char * _position(char *body, uint32_t esize, uint32_t idx) { return body + esize * idx; } /* false if key is out of range for entry size */ static inline bool _validate_range(uint16_t ksize, uint64_t key) { switch (ksize) { case 8: return true; case 4: return key <= UINT32_MAX; case 2: return key <= UINT16_MAX; case 1: return key <= UINT8_MAX; default: NOT_REACHED(); return false; } } static inline uint64_t _get_key(char *p, uint16_t ksize) { switch (ksize) { case 8: return *((uint64_t *)p); case 4: return *((uint32_t *)p); case 2: return *((uint16_t *)p); case 1: return *((uint8_t *)p); default: NOT_REACHED(); return 0; } } static inline void _set_key(char *p, uint16_t ksize, uint64_t key) { switch (ksize) { case 8: *((uint64_t *)p) = key; break; case 4: *((uint32_t *)p) = key; break; case 2: *((uint16_t *)p) = key; break; case 1: *((uint8_t *)p) = key; break; default: NOT_REACHED(); } } static inline bool _should_scan(uint32_t nentry, uint32_t esize) { return nentry * esize <= SCAN_THRESHOLD; } /* returns true if an exact match is found, false otherwise. * If a match is found, the index of the element is stored in idx; * otherwise, idx contains the index of the insertion spot */ static inline bool _linear_search(uint32_t *idx, char *body, uint32_t nentry, uint32_t esize, uint16_t ksize, uint64_t key) { uint32_t i; *idx = 0; if (nentry == 0) { return false; } for (i = 0; i < nentry; ++i, ++*idx) { if (key <= _get_key(_position(body, esize, i), ksize)) { break; } } if (key == _get_key(_position(body, esize, *idx), ksize)) { return true; } else { return false; } } static inline bool _binary_search(uint32_t *idx, char *body, uint32_t nentry, uint32_t esize, uint16_t ksize, uint64_t key) { uint32_t id = 0, imin, imax; uint32_t curr; *idx = 0; if (nentry == 0) { return false; } if (key == _get_key(_position(body, esize, 0), ksize)) { return true; } if (key < _get_key(_position(body, esize, 0), ksize)) { return false; } if (key > _get_key(_position(body, esize, nentry - 1), ksize)) { *idx = nentry; return false; } imin = 1; imax = nentry - 1; while (imin <= imax) { id = (imin + imax) / 2; curr = _get_key(_position(body, esize, id), ksize); if (key == curr) { *idx = id; return true; } if (key > curr) { imin = id + 1; } else { if (key <= _get_key(_position(body, esize, id - 1), ksize)) { imax = id - 1; } else { break; } } } *idx = id; return false; } static inline bool _locate(uint32_t *idx, char *body, uint32_t nentry, uint32_t esize, uint16_t ksize, uint64_t key) { /* optimize for inserting at the end, which is dominant in many use cases */ if (nentry == 0 || _get_key(body + esize * (nentry - 1), ksize) < key) { *idx = nentry; return false; } if (_should_scan(nentry, esize)) { /* linear scan */ return _linear_search(idx, body, nentry, esize, ksize, key); } else { /* otherwise, binary search */ return _binary_search(idx, body, nentry, esize, ksize, key); } } smap_rstatus_e smap_init(smap_p sm, uint16_t ksize, uint16_t vsize) { if (sm == NULL) { log_debug("NULL pointer encountered for sm"); return SMAP_ERROR; } if (ksize != 1 && ksize != 2 && ksize != 4 && ksize != 8) { log_debug("%"PRIu32" is not a valid key size", ksize); return SMAP_EINVALID; } SM_NENTRY(sm) = 0; SM_KSIZE(sm) = ksize; SM_VSIZE(sm) = vsize; return SMAP_OK; } smap_rstatus_e smap_keyval(uint64_t *key, struct bstring *val, const smap_p sm, uint32_t idx) { uint32_t esize, ksize, nentry; char *entry; if (key == NULL || val == NULL || sm == NULL) { log_debug("NULL pointer encountered for sm %p, key %p, or val %p", sm, key, val); return SMAP_ERROR; } nentry = smap_nentry(sm); idx += (idx < 0) * nentry; if (idx < 0 || idx >= nentry) { return SMAP_EOOB; } esize = smap_esize(sm); ksize = smap_ksize(sm); entry = _position(SM_BODY(sm), esize, idx); *key = _get_key(entry, ksize); val->len = smap_vsize(sm); val->data = entry + ksize; return SMAP_OK; } /* caller should discard keys in idx if function returns ENOTFOUND */ smap_rstatus_e smap_index(uint32_t *idx, const smap_p sm, uint64_t key) { uint32_t esize; uint16_t ksize; bool found; if (sm == NULL || idx == NULL) { log_debug("NULL pointer encountered for sm %p or key %p", sm, key); return SMAP_ERROR; } ksize = smap_ksize(sm); if (!_validate_range(ksize, key)) { log_debug("%"PRIu64" out of range for %"PRIu16" byte integer", key, ksize); return SMAP_EINVALID; } esize = smap_esize(sm); found = _locate(idx, SM_BODY(sm), smap_nentry(sm), esize, ksize, key); if (found) { return SMAP_OK; } else { return SMAP_ENOTFOUND; } } smap_rstatus_e smap_insert(smap_p sm, uint64_t key, const struct bstring *val) { bool found; char *body, *p; uint16_t ksize, vsize; uint32_t idx, esize, nentry; if (sm == NULL) { log_debug("NULL pointer encountered for sm"); return SMAP_ERROR; } ksize = smap_ksize(sm); if (!_validate_range(ksize, key)) { log_debug("%"PRIu64" out of range for %"PRIu16" byte integer", key, ksize); return SMAP_EINVALID; } vsize = smap_vsize(sm); if (val->len != vsize) { log_debug("value size %"PRIu16" is different for %"PRIu16" map initial", val->len, vsize); return SMAP_EINVALID; } body = SM_BODY(sm); nentry = smap_nentry(sm); esize = smap_esize(sm); found = _locate(&idx, body, nentry, esize, ksize, key); if (found) { return SMAP_EDUP; } p = _position(body, esize, idx); cc_memmove(p + esize, p, esize * (nentry - idx)); _set_key(p, ksize, key); cc_memcpy(p + ksize, val->data, vsize); /* copy val */ SM_NENTRY(sm)++; return SMAP_OK; } smap_rstatus_e smap_remove(smap_p sm, uint64_t key) { bool found; char *body, *p; uint16_t ksize; uint32_t idx, esize, nentry; if (sm == NULL) { log_debug("NULL pointer encountered for sm"); return SMAP_ERROR; } ksize = smap_ksize(sm); if (!_validate_range(ksize, key)) { log_debug("%"PRIu64" out of range for %"PRIu16" byte integer", key, ksize); return SMAP_EINVALID; } body = SM_BODY(sm); nentry = smap_nentry(sm); esize = smap_esize(sm); found = _locate(&idx, body, nentry, esize, ksize, key); if (found) { p = _position(body, esize, idx); cc_memmove(p, p + esize, esize * (nentry - idx - 1)); SM_NENTRY(sm)--; return SMAP_OK; } return SMAP_ENOTFOUND; } smap_rstatus_e smap_truncate(smap_p sm, int64_t count) { char *body; uint32_t esize, nentry; if (sm == NULL) { log_debug("NULL pointer encountered for sm"); return SMAP_ERROR; } if (count == 0) { return SMAP_OK; } body = SM_BODY(sm); esize = smap_esize(sm); nentry = smap_nentry(sm); /* if abs(count) >= num entries in the array, remove all */ if (count >= nentry || -count >= nentry) { SM_NENTRY(sm) = 0; return SMAP_OK; } if (count > 0) { /* only need to move data if truncating from left */ cc_memmove(body, body + esize * count, esize * (nentry - count)); SM_NENTRY(sm) -= count; } else { SM_NENTRY(sm) += count; } return SMAP_OK; }