pax_global_header00006660000000000000000000000064121530524400014506gustar00rootroot0000000000000052 comment=8afc2d30fe33e2d152376ba56d41cebda1fbf22c erlang-cherly-0.12.8+dfsg/000077500000000000000000000000001215305244000152515ustar00rootroot00000000000000erlang-cherly-0.12.8+dfsg/.gitignore000066400000000000000000000000561215305244000172420ustar00rootroot00000000000000ebin priv/ *.o *.swp .eunit/ c_src/test/suite erlang-cherly-0.12.8+dfsg/.travis.yml000066400000000000000000000001611215305244000173600ustar00rootroot00000000000000# # Cherly # language: erlang script: "make && make eunit" notifications: email: false otp_release: - R14B04 erlang-cherly-0.12.8+dfsg/LICENSE000066400000000000000000000006341215305244000162610ustar00rootroot00000000000000---------------------------------------------------------------------------- "THE BEER-WARE LICENSE" (Revision 42): Cliff Moon wrote this project. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Cliff Moon. ---------------------------------------------------------------------------- erlang-cherly-0.12.8+dfsg/Makefile000066400000000000000000000006311215305244000167110ustar00rootroot00000000000000.PHONY: deps test REBAR := ./rebar all: @$(REBAR) update-deps @$(REBAR) get-deps @$(REBAR) compile @$(REBAR) xref skip_deps=true @$(REBAR) eunit skip_deps=true compile: @$(REBAR) compile skip_deps=true xref: @$(REBAR) xref skip_deps=true eunit: @$(REBAR) eunit skip_deps=true clean: @$(REBAR) clean skip_deps=true distclean: @$(REBAR) delete-deps @$(REBAR) clean qc: @$(REBAR) qc skip_deps=true erlang-cherly-0.12.8+dfsg/README.markdown000066400000000000000000000022261215305244000177540ustar00rootroot00000000000000Cherly ======= Cherly (sher-lee) was originally developed by Cliff Moon for erlang to deal with in memory caching based on LRU. Its functionality and performance were awesome, but as time goes on, its implementation gradually obsoletes and it's hard to maintain. To overcome these problems, I forked and made Cherly improve with original purposes. Main improvements are described below. * Replaced the hash storing structure (originally used Judy hash) with the combination of Open addressing and Tree structure based on golang's hash implementation. This structure is very scalable and stable. * Implemented slab allocator on off-heap. * Replaced implemantations of port driver with NIFs. * Rebarized Dependencies ======= * Runtime * Erlang >= R14B * [Check](http://check.sourceforge.net/) for C Unit tests Installation ======== * Install `Check` from package systems * deb `sudo apt-get install check` * rpm `sudo yum install check` * "Cherly" uses the "rebar" build system. Makefile so that simply running "make" at the top level should work. * [rebar](https://github.com/basho/rebar) * "Cherly" requires Erlang R14B or later. erlang-cherly-0.12.8+dfsg/c_src/000077500000000000000000000000001215305244000163425ustar00rootroot00000000000000erlang-cherly-0.12.8+dfsg/c_src/alg.c000066400000000000000000000030111215305244000172440ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "runtime.h" #include "hashmap.h" #include "type.h" void runtime_memhash(uintptr *h, uintptr s, void *a) { byte *b; uintptr hash; b = a; if(sizeof(hash) == 4) hash = 2860486313U; else hash = 33054211828000289ULL; while(s > 0) { if(sizeof(hash) == 4) hash = (hash ^ *b) * 3267000013UL; else hash = (hash ^ *b) * 23344194077549503ULL; b++; s--; } *h ^= hash; } void runtime_memequal(bool *eq, uintptr s, void *a, void *b) { byte *ba, *bb, *aend; if(a == b) { *eq = 1; return; } ba = a; bb = b; aend = ba+s; while(ba != aend) { if(*ba != *bb) { *eq = 0; return; } ba++; bb++; } *eq = 1; return; } void runtime_strhash(uintptr *h, uintptr s, void *a) { runtime_memhash(h, ((String*)a)->len, ((String*)a)->str); } void runtime_strequal(bool *eq, uintptr s, void *a, void *b) { int32 alen; alen = ((String*)a)->len; if(alen != ((String*)b)->len) { *eq = false; return; } runtime_memequal(eq, alen, ((String*)a)->str, ((String*)b)->str); } void runtime_strcopy(uintptr s, void *a, void *b) { if(b == nil) { ((String*)a)->str = 0; ((String*)a)->len = 0; return; } ((String*)a)->str = ((String*)b)->str; ((String*)a)->len = ((String*)b)->len; } Alg StrAlg = { runtime_strhash, runtime_strequal, runtime_strcopy }; Type StrType = { sizeof(String), &StrAlg }; MapType StrMapType = { &StrType, &StrType }; erlang-cherly-0.12.8+dfsg/c_src/cherly.c000066400000000000000000000104171215305244000177770ustar00rootroot00000000000000#include #include #include "cherly.h" #include "common.h" static void cherly_eject_callback(cherly_t *cherly, char *key, int length); /** * Initialize LRU-Storage */ void cherly_init(cherly_t *cherly, int options, unsigned long long max_size) { cherly->hm = runtime_makemap_c(&StrMapType, max_size); memset(&cherly->slab, 0, sizeof(slabs_t)); slabs_init(&cherly->slab, max_size, 1.5, false); cherly->lru = lru_create(); cherly->size = 0; cherly->items_length = 0; cherly->max_size = max_size; } /** * Insert an object into LRU-Storage */ // node -> item -> value bool cherly_put(cherly_t *cherly, void *key, int length, void *value, int size, DestroyCallback destroy) { lru_item_t * item; String skey, sval; bool exists; // Prepare put-operation size_t bufsiz = sizeof(size_t) + length + 1 + size; void* buf = slabs_alloc(&cherly->slab, bufsiz); if (buf == NULL) { // retry cherly->size -= lru_eject_by_size(cherly->lru, SETTING_ITEM_SIZE_MAX, (EjectionCallback)cherly_eject_callback, cherly); buf = slabs_alloc(&cherly->slab, bufsiz); if (buf == NULL) return false; } *((size_t*)buf) = bufsiz; char* bufkey = (char*)((char*)buf + sizeof(size_t)); skey.str = (byte*)bufkey; skey.len = length; memcpy(bufkey, key, length); runtime_mapaccess(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval, &exists); if (exists) { item = (lru_item_t*)sval.str; cherly_remove(cherly, lru_item_key(item), lru_item_keylen(item)); } if (cherly->size + bufsiz > cherly->max_size) { cherly->size -= lru_eject_by_size(cherly->lru, (length + size) - (cherly->max_size - cherly->size), (EjectionCallback)cherly_eject_callback, cherly); } void* bufval = (void*)(bufkey + length + 1); memcpy(bufval, value, size); // Insert an object into lru-storage item = lru_insert(cherly->lru, bufkey, length, bufval, size, destroy); if (item == NULL) return false; // After put-operation sval.str = (byte*)item; runtime_mapassign(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval); cherly->size += lru_item_size(item); cherly->items_length++; return true; } /** * Retrieve an object from LRU-Storage */ void* cherly_get(cherly_t *cherly, void *key, int length, int* vallen) { lru_item_t * item; String skey, sval; bool exists; // Prepare get-operation skey.str = (byte*)key; skey.len = length; // Retrieve an object runtime_mapaccess(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval, &exists); if (!exists) { return nil; } else { item = (lru_item_t *)sval.str; lru_touch(cherly->lru, item); *vallen = lru_item_vallen(item); return lru_item_value(item); } } /** * Free a stored memory */ static inline void cherly_slab_free(slabs_t* slab, char* key) { size_t* psize = (size_t*)key; psize--; slabs_free(slab, (void*)psize, *psize); } /** * Callback */ static void cherly_eject_callback(cherly_t *cherly, char *key, int length) { lru_item_t *item; String skey, sval; bool exists; int32 ret; skey.str = (byte*)key; skey.len = length; runtime_mapaccess(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval, &exists); if (!exists) { return; } item = (lru_item_t*)sval.str; cherly_slab_free(&cherly->slab, lru_item_key(item)); ret = runtime_mapassign(&StrMapType, cherly->hm, (byte*)&skey, nil); if (ret) { cherly->items_length--; cherly->size -= lru_item_size(item); } } /** * Remove an object from LRU-Storage */ void cherly_remove(cherly_t *cherly, void *key, int length) { lru_item_t *item; String skey, sval; bool exists; skey.str = (byte*)key; skey.len = length; runtime_mapaccess(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval, &exists); if (!exists) { return; } item = (lru_item_t *)sval.str; cherly_slab_free(&cherly->slab, lru_item_key(item)); lru_remove_and_destroy(cherly->lru, item); cherly->size -= lru_item_size(item); cherly->items_length--; runtime_mapassign(&StrMapType, cherly->hm, (byte*)&skey, nil); } /** * Destroy LRU-Storage */ void cherly_destroy(cherly_t *cherly) { runtime_mapdestroy(cherly->hm); lru_destroy(cherly->lru); } erlang-cherly-0.12.8+dfsg/c_src/cherly.h000066400000000000000000000014301215305244000177770ustar00rootroot00000000000000#ifndef __CHERLY__ #define __CHERLY__ #include "runtime.h" #include "lru.h" #include "slabs.h" #define cherly_size(cherly) ((cherly)->size) #define cherly_items_length(cherly) ((cherly)->items_length) #define cherly_max_size(cherly) ((cherly)->max_size) typedef struct _cherly_t { Hmap* hm; slabs_t slab; lru_t *lru; unsigned long long size; unsigned long long items_length; unsigned long long max_size; } cherly_t; void cherly_init(cherly_t *cherly, int options, unsigned long long max_size); void * cherly_get(cherly_t *cherly, void * key, int length, int* vallen); bool cherly_put(cherly_t *cherly, void * key, int length, void *value, int size, DestroyCallback); void cherly_remove(cherly_t *cherly, void * key, int length); void cherly_destroy(cherly_t *cherly); #endif erlang-cherly-0.12.8+dfsg/c_src/cherly_nif.c000066400000000000000000000147411215305244000206370ustar00rootroot00000000000000#include "erl_nif.h" #include #include #include "cherly.h" #include "common.h" #define CHERLY_RES_TYPE "cherly_res" static ERL_NIF_TERM cherly_nif_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM cherly_nif_stop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM cherly_nif_get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM cherly_nif_put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM cherly_nif_remove(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM cherly_nif_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM cherly_nif_items(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ErlNifFunc nif_funcs[] = { {"start", 1, cherly_nif_init}, {"stop", 1, cherly_nif_stop}, {"get" , 2, cherly_nif_get}, {"put" , 3, cherly_nif_put}, {"remove", 2, cherly_nif_remove}, {"size", 1, cherly_nif_size}, {"items" , 1, cherly_nif_items} }; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_error; static ERL_NIF_TERM atom_oom; static ERL_NIF_TERM atom_not_found; /** * Initialize */ static ERL_NIF_TERM cherly_nif_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ErlNifUInt64 max_size; ERL_NIF_TERM term; ErlNifResourceType* pert; cherly_t* obj; if (argc < 1) { return enif_make_badarg(env); } if (!enif_get_uint64(env, argv[0], &max_size)) { return enif_make_badarg(env); } pert = (ErlNifResourceType*)enif_priv_data(env); obj = enif_alloc_resource(pert, sizeof(cherly_t)); term = enif_make_resource(env, obj); cherly_init(obj, 0, max_size); enif_release_resource(obj); return enif_make_tuple2(env, atom_ok, term); } /** * Stop */ static ERL_NIF_TERM cherly_nif_stop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { cherly_t *obj; ErlNifResourceType* pert; if (argc < 1) { return enif_make_badarg(env); } pert = (ErlNifResourceType*)enif_priv_data(env); if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { return enif_make_badarg(env); } cherly_destroy(obj); return atom_ok; } /** * Retrieve an object from LRU-Storage */ static ERL_NIF_TERM cherly_nif_get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { cherly_t *obj; int vallen; void* value; ErlNifResourceType* pert; ErlNifBinary keybin; ErlNifBinary bin; if (argc < 2) { return enif_make_badarg(env); } pert = (ErlNifResourceType*)enif_priv_data(env); if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { return enif_make_badarg(env); } if (!enif_inspect_binary(env, argv[1], &keybin)) { return enif_make_badarg(env); } if (keybin.size <= 0) { return enif_make_badarg(env); } value = cherly_get(obj, keybin.data, keybin.size, &vallen); if (value == NULL) { return atom_not_found; } if (!enif_alloc_binary(vallen, &bin)) { return enif_make_badarg(env); } memcpy(bin.data, value, vallen); return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &bin)); } /** * Insert an object into LRU-Storage */ static ERL_NIF_TERM cherly_nif_put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { cherly_t *obj; ErlNifResourceType* pert; ErlNifBinary keybin; ErlNifBinary bin; bool ret; if (argc < 3) { return enif_make_badarg(env); } pert = (ErlNifResourceType*)enif_priv_data(env); if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { return enif_make_badarg(env); } if (!enif_inspect_binary(env, argv[1], &keybin)) { return enif_make_badarg(env); } if (keybin.size <= 0) { return enif_make_badarg(env); } if (!enif_inspect_binary(env, argv[2], &bin)) { return enif_make_badarg(env); } ret = cherly_put(obj, keybin.data, keybin.size, bin.data, bin.size, NULL); return ret ? atom_ok : enif_make_tuple2(env, atom_error, atom_oom); } /** * Remove an object from LRU-Storage */ static ERL_NIF_TERM cherly_nif_remove(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { cherly_t *obj; ErlNifResourceType* pert; ErlNifBinary keybin; if (argc < 2) { return enif_make_badarg(env); } pert = (ErlNifResourceType*)enif_priv_data(env); if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { return enif_make_badarg(env); } if (!enif_inspect_binary(env, argv[1], &keybin)) { return enif_make_badarg(env); } if (keybin.size <= 0) { return enif_make_badarg(env); } cherly_remove(obj, keybin.data, keybin.size); return atom_ok; } /** * Retrieve summary of size of stored objects */ static ERL_NIF_TERM cherly_nif_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { cherly_t *obj; ErlNifResourceType* pert; ErlNifUInt64 size; if (argc < 1) { return enif_make_badarg(env); } pert = (ErlNifResourceType*)enif_priv_data(env); if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { return enif_make_badarg(env); } size = cherly_size(obj); return enif_make_tuple2(env, atom_ok, enif_make_uint64(env, size)); } /** * Retrieve total of objects */ static ERL_NIF_TERM cherly_nif_items(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { cherly_t *obj; ErlNifResourceType* pert; ErlNifUInt64 len; if (argc < 1) { return enif_make_badarg(env); } pert = (ErlNifResourceType*)enif_priv_data(env); if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { return enif_make_badarg(env); } len = cherly_items_length(obj); return enif_make_tuple2(env, atom_ok, enif_make_uint64(env, len)); } /** * When calling onload or uggrade */ static int onload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { ErlNifResourceFlags erf = ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER; ErlNifResourceType* pert = enif_open_resource_type(env, NULL, CHERLY_RES_TYPE, NULL, erf, &erf); if (pert == NULL) { return 1; } *priv_data = (void*)pert; atom_ok = enif_make_atom(env, "ok"); atom_error = enif_make_atom(env, "error"); atom_oom = enif_make_atom(env, "out of memory"); atom_not_found = enif_make_atom(env, "not_found"); return 0; } /** * Onload */ int cherly_nif_onload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { return onload(env, priv_data, load_info); } /** * Upgrade */ int cherly_nif_upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) { return onload(env, priv_data, load_info); } ERL_NIF_INIT(cherly, nif_funcs, cherly_nif_onload, NULL, cherly_nif_upgrade, NULL) erlang-cherly-0.12.8+dfsg/c_src/common.h000066400000000000000000000002511215305244000200010ustar00rootroot00000000000000#ifndef __COMMON_H__ #define __COMMON_H__ #ifdef DEBUG #define dprintf(format, args...) printf (format , ## args) #else #define dprintf(format, args...) #endif #endiferlang-cherly-0.12.8+dfsg/c_src/double_link.c000066400000000000000000000045761215305244000210110ustar00rootroot00000000000000#include "double_link.h" #include #include "common.h" d_list_t * d_list_create() { d_list_t * list = malloc(sizeof(d_list_t)); if (list == NULL) return NULL; list->head = NULL; list->tail = NULL; list->size = 0; return list; } void d_list_destroy(d_list_t *list) { d_node_t * node = list->head; d_node_t * tmp = NULL; while (NULL != node) { tmp = node->next; d_node_destroy(node); node = tmp; } free(list); } void d_list_push(d_list_t *list, d_node_t *node) { if (NULL == list->head && NULL == list->tail) { list->head = node; list->tail = node; node->previous = NULL; node->next = NULL; } else { node->previous = NULL; list->head->previous = node; node->next = list->head; list->head = node; } list->size++; } d_node_t * d_list_pop(d_list_t *list) { d_node_t * node = list->head; if (NULL == node) { return NULL; } list->head = node->next; list->size--; if (NULL != list->head) { list->head->previous = NULL; } else { list->tail = NULL; } return node; } void d_list_unshift(d_list_t *list, d_node_t *node) { if (NULL == list->head && NULL == list->tail) { list->head = node; list->tail = node; node->previous = NULL; node->next = NULL; } else { node->previous = list->tail; list->tail->next = node; node->next = NULL; list->tail = node; } list->size++; } d_node_t * d_list_shift(d_list_t *list) { d_node_t * node = list->tail; if (NULL == node) { return NULL; } list->tail = node->previous; list->size--; if (NULL != list->tail) { list->tail->next = NULL; } else { list->head = NULL; } return node; } void d_list_remove(d_list_t *list, d_node_t *node) { d_node_t *previous; d_node_t *next; if (list->head == node) { d_list_pop(list); return; } else if (list->tail == node) { d_list_shift(list); return; } previous = node->previous; next = node->next; node->previous = NULL; node->next = NULL; if (NULL != previous) { previous->next = next; } if (NULL != next) { next->previous = previous; } } d_node_t * d_node_create(void *data) { d_node_t * node; node = malloc(sizeof(d_node_t)); if (node == NULL) return NULL; node->previous = NULL; node->next = NULL; node->data = data; return node; } void d_node_destroy(d_node_t * node) { free(node); } erlang-cherly-0.12.8+dfsg/c_src/double_link.h000066400000000000000000000012651215305244000210060ustar00rootroot00000000000000#ifndef __DOUBLE_LINK_H__ #define __DOUBLE_LINK_H__ typedef struct _d_node_t { struct _d_node_t * previous; struct _d_node_t * next; void * data; } d_node_t; typedef struct _d_list_t { d_node_t * head; d_node_t * tail; unsigned long size; } d_list_t; #define d_list_size(list) ((list)->size) d_list_t * d_list_create(); void d_list_destroy(d_list_t * list); void d_list_push(d_list_t *list, d_node_t *node); d_node_t* d_list_pop(d_list_t *list); void d_list_unshift(d_list_t *list, d_node_t *node); d_node_t* d_list_shift(d_list_t *list); void d_list_remove(d_list_t *list, d_node_t *node); d_node_t * d_node_create(void * data); void d_node_destroy(d_node_t* node); #endiferlang-cherly-0.12.8+dfsg/c_src/hashmap.c000066400000000000000000000546311215305244000201400ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include #include #include #include #include "runtime.h" #include "hashmap.h" #include "type.h" struct Hmap { /* a hash table; initialize with hash_init() */ uint32 count; /* elements in table - must be first */ uint8 datasize; /* amount of data to store in entry */ uint8 max_power; /* max power of 2 to create sub-tables */ uint8 indirectval; /* storing pointers to values */ uint8 valoff; /* offset of value in key+value data block */ int32 changes; /* inc'ed whenever a subtable is created/grown */ struct hash_subtable *st; /* first-level table */ }; struct hash_entry { hash_hash_t hash; /* hash value of data */ byte data[1]; /* user data has "datasize" bytes */ }; struct hash_subtable { uint8 power; /* bits used to index this table */ uint8 used; /* bits in hash used before reaching this table */ uint8 datasize; /* bytes of client data in an entry */ uint8 max_probes; /* max number of probes when searching */ int16 limit_bytes; /* max_probes * (datasize+sizeof (hash_hash_t)) */ struct hash_entry *last; /* points to last element of entry[] */ struct hash_entry entry[1]; /* 2**power+max_probes-1 elements of elemsize bytes */ }; #define HASH_DATA_EQ(eq, t, h,x,y) ((eq)=0, (*t->key->alg->equal) (&(eq), t->key->size, (x), (y)), (eq)) #define HASH_REHASH 0x2 /* an internal flag */ /* the number of bits used is stored in the flags word too */ #define HASH_USED(x) ((x) >> 2) #define HASH_MAKE_USED(x) ((x) << 2) #define HASH_LOW 6 #define HASH_ONE (((hash_hash_t)1) << HASH_LOW) #define HASH_MASK (HASH_ONE - 1) #define HASH_ADJUST(x) (((x) < HASH_ONE) << HASH_LOW) #define HASH_BITS (sizeof (hash_hash_t) * 8) #define HASH_SUBHASH HASH_MASK #define HASH_NIL 0 #define HASH_NIL_MEMSET 0 #define HASH_OFFSET(base, byte_offset) \ ((struct hash_entry *) (((byte *) (base)) + (byte_offset))) #define HASH_MAX_PROBES 15 /* max entries to probe before rehashing */ /* return a hash layer with 2**power empty entries */ static struct hash_subtable * hash_subtable_new (Hmap *h, int32 power, int32 used) { int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); int32 bytes = elemsize << power; struct hash_subtable *st; int32 limit_bytes = HASH_MAX_PROBES * elemsize; int32 max_probes = HASH_MAX_PROBES; if (bytes < limit_bytes) { limit_bytes = bytes; max_probes = 1 << power; } bytes += limit_bytes - elemsize; st = malloc (offsetof (struct hash_subtable, entry[0]) + bytes); st->power = power; st->used = used; st->datasize = h->datasize; st->max_probes = max_probes; st->limit_bytes = limit_bytes; st->last = HASH_OFFSET (st->entry, bytes) - 1; memset (st->entry, HASH_NIL_MEMSET, bytes); return (st); } static void init_sizes (int64 hint, int32 *init_power, int32 *max_power) { int32 log = 0; int32 i; for (i = 32; i != 0; i >>= 1) { if ((hint >> (log + i)) != 0) { log += i; } } log += 1 + (((hint << 3) >> log) >= 11); /* round up for utilization */ if (log <= 14) { *init_power = log; } else { *init_power = 12; } *max_power = 12; } static void hash_init (Hmap *h, int32 datasize, int64 hint) { int32 init_power; int32 max_power; if(datasize < sizeof (void *)) datasize = sizeof (void *); datasize = runtime_rnd(datasize, sizeof (void *)); init_sizes (hint, &init_power, &max_power); h->datasize = datasize; h->max_power = max_power; assert (h->datasize == datasize); assert (h->max_power == max_power); assert (sizeof (void *) <= h->datasize || h->max_power == 255); h->count = 0; h->changes = 0; h->st = hash_subtable_new (h, init_power, 0); } static void hash_remove_n (struct hash_subtable *st, struct hash_entry *dst_e, int32 n) { int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); struct hash_entry *src_e = HASH_OFFSET (dst_e, n * elemsize); struct hash_entry *last_e = st->last; int32 shift = HASH_BITS - (st->power + st->used); int32 index_mask = (((hash_hash_t)1) << st->power) - 1; int32 dst_i = (((byte *) dst_e) - ((byte *) st->entry)) / elemsize; int32 src_i = dst_i + n; hash_hash_t hash; int32 skip; int32 bytes; while (dst_e != src_e) { if (src_e <= last_e) { struct hash_entry *cp_e = src_e; int32 save_dst_i = dst_i; while (cp_e <= last_e && (hash = cp_e->hash) != HASH_NIL && ((hash >> shift) & index_mask) <= dst_i) { cp_e = HASH_OFFSET (cp_e, elemsize); dst_i++; } bytes = ((byte *) cp_e) - (byte *) src_e; memmove (dst_e, src_e, bytes); dst_e = HASH_OFFSET (dst_e, bytes); src_e = cp_e; src_i += dst_i - save_dst_i; if (src_e <= last_e && (hash = src_e->hash) != HASH_NIL) { skip = ((hash >> shift) & index_mask) - dst_i; } else { skip = src_i - dst_i; } } else { skip = src_i - dst_i; } bytes = skip * elemsize; memset (dst_e, HASH_NIL_MEMSET, bytes); dst_e = HASH_OFFSET (dst_e, bytes); dst_i += skip; } } static int32 hash_insert_internal (MapType*, struct hash_subtable **pst, int32 flags, hash_hash_t hash, Hmap *h, void *data, void **pres); static void hash_conv (MapType *t, Hmap *h, struct hash_subtable *st, int32 flags, hash_hash_t hash, struct hash_entry *e) { int32 new_flags = (flags + HASH_MAKE_USED (st->power)) | HASH_REHASH; int32 shift = HASH_BITS - HASH_USED (new_flags); hash_hash_t prefix_mask = (-(hash_hash_t)1) << shift; int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); void *dummy_result; struct hash_entry *de; int32 index_mask = (1 << st->power) - 1; hash_hash_t e_hash; struct hash_entry *pe = HASH_OFFSET (e, -elemsize); while (e != st->entry && (e_hash = pe->hash) != HASH_NIL && (e_hash & HASH_MASK) != HASH_SUBHASH) { e = pe; pe = HASH_OFFSET (pe, -elemsize); } de = e; while (e <= st->last && (e_hash = e->hash) != HASH_NIL && (e_hash & HASH_MASK) != HASH_SUBHASH) { struct hash_entry *target_e = HASH_OFFSET (st->entry, ((e_hash >> shift) & index_mask) * elemsize); struct hash_entry *ne = HASH_OFFSET (e, elemsize); hash_hash_t current = e_hash & prefix_mask; if (de < target_e) { memset (de, HASH_NIL_MEMSET, ((byte *) target_e) - (byte *) de); de = target_e; } if ((hash & prefix_mask) == current || (ne <= st->last && (e_hash = ne->hash) != HASH_NIL && (e_hash & prefix_mask) == current)) { struct hash_subtable *new_st = hash_subtable_new (h, 1, HASH_USED (new_flags)); int32 rc = hash_insert_internal (t, &new_st, new_flags, e->hash, h, e->data, &dummy_result); assert (rc == 0); memcpy(dummy_result, e->data, h->datasize); e = ne; while (e <= st->last && (e_hash = e->hash) != HASH_NIL && (e_hash & prefix_mask) == current) { assert ((e_hash & HASH_MASK) != HASH_SUBHASH); rc = hash_insert_internal (t, &new_st, new_flags, e_hash, h, e->data, &dummy_result); assert (rc == 0); memcpy(dummy_result, e->data, h->datasize); e = HASH_OFFSET (e, elemsize); } memset (de->data, HASH_NIL_MEMSET, h->datasize); *(struct hash_subtable **)de->data = new_st; de->hash = current | HASH_SUBHASH; } else { if (e != de) { memcpy (de, e, elemsize); } e = HASH_OFFSET (e, elemsize); } de = HASH_OFFSET (de, elemsize); } if (e != de) { hash_remove_n (st, de, (((byte *) e) - (byte *) de) / elemsize); } } static void hash_grow (MapType *t, Hmap *h, struct hash_subtable **pst, int32 flags) { struct hash_subtable *old_st = *pst; int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); *pst = hash_subtable_new (h, old_st->power + 1, HASH_USED (flags)); struct hash_entry *last_e = old_st->last; struct hash_entry *e; void *dummy_result; int32 used = 0; flags |= HASH_REHASH; for (e = old_st->entry; e <= last_e; e = HASH_OFFSET (e, elemsize)) { hash_hash_t hash = e->hash; if (hash != HASH_NIL) { int32 rc = hash_insert_internal (t, pst, flags, e->hash, h, e->data, &dummy_result); assert (rc == 0); memcpy(dummy_result, e->data, h->datasize); used++; } } free (old_st); } static int32 hash_lookup (MapType *t, Hmap *h, void *data, void **pres) { int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); hash_hash_t hash; struct hash_subtable *st = h->st; int32 used = 0; hash_hash_t e_hash; struct hash_entry *e; struct hash_entry *end_e; bool eq; hash = 0; (*t->key->alg->hash) (&hash, t->key->size, data); hash &= ~HASH_MASK; hash += HASH_ADJUST (hash); for (;;) { int32 shift = HASH_BITS - (st->power + used); int32 index_mask = (1 << st->power) - 1; int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ e_hash = e->hash; if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ break; } used += st->power; st = *(struct hash_subtable **)e->data; } end_e = HASH_OFFSET (e, st->limit_bytes); while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { e = HASH_OFFSET (e, elemsize); } while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { if (HASH_DATA_EQ (eq, t, h, data, e->data)) { /* a match */ *pres = e->data; return (1); } e = HASH_OFFSET (e, elemsize); } *pres = 0; return (0); } static int32 hash_remove (MapType *t, Hmap *h, void *data) { int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); hash_hash_t hash; struct hash_subtable *st = h->st; int32 used = 0; hash_hash_t e_hash; struct hash_entry *e; struct hash_entry *end_e; bool eq; hash = 0; (*t->key->alg->hash) (&hash, t->key->size, data); hash &= ~HASH_MASK; hash += HASH_ADJUST (hash); for (;;) { int32 shift = HASH_BITS - (st->power + used); int32 index_mask = (1 << st->power) - 1; int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ e_hash = e->hash; if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ break; } used += st->power; st = *(struct hash_subtable **)e->data; } end_e = HASH_OFFSET (e, st->limit_bytes); while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { e = HASH_OFFSET (e, elemsize); } while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { if (HASH_DATA_EQ (eq, t, h, data, e->data)) { /* a match */ if (h->indirectval) free (*(void**)((byte*)e->data + h->valoff)); hash_remove_n (st, e, 1); h->count--; return (1); } e = HASH_OFFSET (e, elemsize); } return (0); } static int32 hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_hash_t hash, Hmap *h, void *data, void **pres) { int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); bool eq; if ((flags & HASH_REHASH) == 0) { hash += HASH_ADJUST (hash); hash &= ~HASH_MASK; } for (;;) { struct hash_subtable *st = *pst; int32 shift = HASH_BITS - (st->power + HASH_USED (flags)); int32 index_mask = (1 << st->power) - 1; int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ struct hash_entry *start_e = HASH_OFFSET (st->entry, i * elemsize); /* start_e is the pointer to element i */ struct hash_entry *e = start_e; /* e is going to range over [start_e, end_e) */ struct hash_entry *end_e; hash_hash_t e_hash = e->hash; if ((e_hash & HASH_MASK) == HASH_SUBHASH) { /* a subtable */ pst = (struct hash_subtable **) e->data; flags += HASH_MAKE_USED (st->power); continue; } end_e = HASH_OFFSET (start_e, st->limit_bytes); while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { e = HASH_OFFSET (e, elemsize); i++; } if (e != end_e && e_hash != HASH_NIL) { /* ins_e ranges over the elements that may match */ struct hash_entry *ins_e = e; int32 ins_i = i; hash_hash_t ins_e_hash; while (ins_e != end_e && ((e_hash = ins_e->hash) ^ hash) < HASH_SUBHASH) { if (HASH_DATA_EQ (eq, t, h, data, ins_e->data)) { /* a match */ *pres = ins_e->data; return (1); } assert (e_hash != hash || (flags & HASH_REHASH) == 0); hash += (e_hash == hash); /* adjust hash if it collides */ ins_e = HASH_OFFSET (ins_e, elemsize); ins_i++; if (e_hash <= hash) { /* set e to insertion point */ e = ins_e; i = ins_i; } } /* set ins_e to the insertion point for the new element */ ins_e = e; ins_i = i; ins_e_hash = 0; /* move ins_e to point at the end of the contiguous block, but stop if any element can't be moved by one up */ while (ins_e <= st->last && (ins_e_hash = ins_e->hash) != HASH_NIL && ins_i + 1 - ((ins_e_hash >> shift) & index_mask) < st->max_probes && (ins_e_hash & HASH_MASK) != HASH_SUBHASH) { ins_e = HASH_OFFSET (ins_e, elemsize); ins_i++; } if (e == end_e || ins_e > st->last || ins_e_hash != HASH_NIL) { e = end_e; /* can't insert; must grow or convert to subtable */ } else { /* make space for element */ memmove (HASH_OFFSET (e, elemsize), e, ((byte *) ins_e) - (byte *) e); } } if (e != end_e) { e->hash = hash; *pres = e->data; return (0); } h->changes++; if (st->power < h->max_power) { hash_grow (t, h, pst, flags); } else { hash_conv (t, h, st, flags, hash, start_e); } } } static int32 hash_insert (MapType *t, Hmap *h, void *data, void **pres) { uintptr hash; int32 rc; hash = 0; (*t->key->alg->hash) (&hash, t->key->size, data); rc = hash_insert_internal (t, &h->st, 0, hash, h, data, pres); h->count += (rc == 0); /* increment count if element didn't previously exist */ return (rc); } static uint32 hash_count (Hmap *h) { return (h->count); } static void iter_restart (struct hash_iter *it, struct hash_subtable *st, int32 used) { int32 elemsize = it->elemsize; hash_hash_t last_hash = it->last_hash; struct hash_entry *e; hash_hash_t e_hash; struct hash_iter_sub *sub = &it->subtable_state[it->i]; struct hash_entry *last; for (;;) { int32 shift = HASH_BITS - (st->power + used); int32 index_mask = (1 << st->power) - 1; int32 i = (last_hash >> shift) & index_mask; last = st->last; e = HASH_OFFSET (st->entry, i * elemsize); sub->start = st->entry; sub->last = last; if ((e->hash & HASH_MASK) != HASH_SUBHASH) { break; } sub->e = HASH_OFFSET (e, elemsize); sub = &it->subtable_state[++(it->i)]; used += st->power; st = *(struct hash_subtable **)e->data; } while (e <= last && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { e = HASH_OFFSET (e, elemsize); } sub->e = e; } static void * hash_next (struct hash_iter *it) { int32 elemsize; struct hash_iter_sub *sub; struct hash_entry *e; struct hash_entry *last; hash_hash_t e_hash; if (it->changes != it->h->changes) { /* hash table's structure changed; recompute */ if (~it->last_hash == 0) return (0); it->changes = it->h->changes; it->i = 0; iter_restart (it, it->h->st, 0); } elemsize = it->elemsize; Again: e_hash = 0; sub = &it->subtable_state[it->i]; e = sub->e; last = sub->last; if (e != sub->start && it->last_hash != HASH_OFFSET (e, -elemsize)->hash) { struct hash_entry *start = HASH_OFFSET (e, -(elemsize * HASH_MAX_PROBES)); struct hash_entry *pe = HASH_OFFSET (e, -elemsize); hash_hash_t last_hash = it->last_hash; if (start < sub->start) { start = sub->start; } while (e != start && ((e_hash = pe->hash) == HASH_NIL || last_hash < e_hash)) { e = pe; pe = HASH_OFFSET (pe, -elemsize); } while (e <= last && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { e = HASH_OFFSET (e, elemsize); } } for (;;) { while (e <= last && (e_hash = e->hash) == HASH_NIL) { e = HASH_OFFSET (e, elemsize); } if (e > last) { if (it->i == 0) { if(!it->cycled) { // Wrap to zero and iterate up until it->cycle. it->cycled = true; it->last_hash = 0; it->subtable_state[0].e = it->h->st->entry; it->subtable_state[0].start = it->h->st->entry; it->subtable_state[0].last = it->h->st->last; goto Again; } // Set last_hash to impossible value and // break it->changes, so that check at top of // hash_next will be used if we get called again. it->last_hash = ~(hash_hash_t)0; it->changes--; return (0); } else { it->i--; sub = &it->subtable_state[it->i]; e = sub->e; last = sub->last; } } else if ((e_hash & HASH_MASK) != HASH_SUBHASH) { if(it->cycled && e->hash > it->cycle) { // Already returned this. // Set last_hash to impossible value and // break it->changes, so that check at top of // hash_next will be used if we get called again. it->last_hash = ~(hash_hash_t)0; it->changes--; return (0); } it->last_hash = e->hash; sub->e = HASH_OFFSET (e, elemsize); return (e->data); } else { struct hash_subtable *st = *(struct hash_subtable **)e->data; sub->e = HASH_OFFSET (e, elemsize); it->i++; assert (it->i < sizeof (it->subtable_state) / sizeof (it->subtable_state[0])); sub = &it->subtable_state[it->i]; sub->e = e = st->entry; sub->start = st->entry; sub->last = last = st->last; } } } static void hash_iter_init (MapType *t, Hmap *h, struct hash_iter *it) { it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]); it->changes = h->changes; it->i = 0; it->h = h; it->t = t; it->last_hash = 0; it->subtable_state[0].e = h->st->entry; it->subtable_state[0].start = h->st->entry; it->subtable_state[0].last = h->st->last; // fastrand1 returns 31 useful bits. // We don't care about not having a bottom bit but we // do want top bits. if(sizeof(void*) == 8) it->cycle = (uint64)runtime_fastrand1()<<33 | (uint64)runtime_fastrand1()<<2; else it->cycle = runtime_fastrand1()<<1; it->cycled = false; it->last_hash = it->cycle; iter_restart(it, it->h->st, 0); } static void clean_st (struct hash_subtable *st, int32 *slots, int32 *used) { int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); struct hash_entry *e = st->entry; struct hash_entry *last = st->last; int32 lslots = (((byte *) (last+1)) - (byte *) e) / elemsize; int32 lused = 0; while (e <= last) { hash_hash_t hash = e->hash; if ((hash & HASH_MASK) == HASH_SUBHASH) { clean_st (*(struct hash_subtable **)e->data, slots, used); } else { lused += (hash != HASH_NIL); } e = HASH_OFFSET (e, elemsize); } free (st); *slots += lslots; *used += lused; } void runtime_mapdestroy (Hmap *h) { int32 slots = 0; int32 used = 0; clean_st (h->st, &slots, &used); free (h); } static void hash_visit_internal (struct hash_subtable *st, int32 used, int32 level, void (*data_visit) (void *arg, int32 level, void *data), void *arg) { int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); struct hash_entry *e = st->entry; int32 shift = HASH_BITS - (used + st->power); int32 i = 0; while (e <= st->last) { int32 index = ((e->hash >> (shift - 1)) >> 1) & ((1 << st->power) - 1); if ((e->hash & HASH_MASK) == HASH_SUBHASH) { (*data_visit) (arg, level, e->data); hash_visit_internal (*(struct hash_subtable **)e->data, used + st->power, level + 1, data_visit, arg); } else { (*data_visit) (arg, level, e->data); } if (e->hash != HASH_NIL) { assert (i < index + st->max_probes); assert (index <= i); } e = HASH_OFFSET (e, elemsize); i++; } } void hash_visit (Hmap *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg) { hash_visit_internal (h->st, 0, 0, data_visit, arg); } // /// interfaces to go runtime // // hash requires < 256 bytes of data (key+value) stored inline. // Only basic types can be key - biggest is complex128 (16 bytes). // Leave some room to grow, just in case. enum { MaxValsize = 256 - 64 }; static void** hash_indirect(Hmap *h, void *p) { if(h->indirectval) p = *(void**)p; return p; } static int32 debug = 0; // makemap(typ *Type, hint uint32) (hmap *map[any]any); Hmap* runtime_makemap_c(MapType *typ, int64 hint) { Hmap *h; int32 valsize_in_hash; Type *key, *val; key = typ->key; val = typ->elem; h = malloc(sizeof(*h)); valsize_in_hash = val->size; if (val->size > MaxValsize) { h->indirectval = 1; valsize_in_hash = sizeof(void*); } // Align value inside data so that mark-sweep gc can find it. h->valoff = key->size; if(valsize_in_hash >= sizeof(void*)) h->valoff = runtime_rnd(key->size, sizeof(void*)); hash_init(h, h->valoff+valsize_in_hash, hint); // these calculations are compiler dependent. // figure out offsets of map call arguments. /* if(debug) { printf("makemap: map=%p; keysize=%lu; valsize=%lu; keyalg=%p; valalg=%p\n", h, key->size, val->size, key->alg, val->alg); } */ return h; } void runtime_mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres) { byte *res; Type *elem; elem = t->elem; if(h == nil) { elem->alg->copy(elem->size, av, nil); *pres = false; return; } res = nil; if(hash_lookup(t, h, ak, (void**)&res)) { *pres = true; elem->alg->copy(elem->size, av, hash_indirect(h, res+h->valoff)); } else { *pres = false; elem->alg->copy(elem->size, av, nil); } } int32 runtime_mapassign(MapType *t, Hmap *h, byte *ak, byte *av) { byte *res; int32 hit; if(av == nil) { hit = hash_remove(t, h, ak); if(debug) { printf("mapassign: map=%p key=%s",(void*)h , ((String*)ak)->str); } return hit; } res = nil; hit = hash_insert(t, h, ak, (void**)&res); if(!hit && h->indirectval) *(void**)(res+h->valoff) = malloc(t->elem->size); t->key->alg->copy(t->key->size, res, ak); t->elem->alg->copy(t->elem->size, hash_indirect(h, res+h->valoff), av); if(debug) { printf("mapassign: map=%p key=%s val=%s",(void*)h, ((String*)ak)->str, ((String*)av)->str); } return hit; } // mapiterinit(mapType *type, hmap *map[any]any, hiter *any); void runtime_mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) { if(h == nil) { it->data = nil; return; } hash_iter_init(t, h, it); it->data = hash_next(it); } // mapiternext(hiter *any); void runtime_mapiternext(struct hash_iter *it) { it->data = hash_next(it); } bool runtime_mapiterkey(struct hash_iter *it, void *ak) { byte *res; Type *key; res = it->data; if(res == nil) return false; key = it->t->key; key->alg->copy(key->size, ak, res); return true; } void runtime_mapiterkeyvalue(struct hash_iter *it, void *ak, void *av) { Hmap *h; byte *res; MapType *t; t = it->t; res = it->data; h = it->h; t->key->alg->copy(t->key->size, ak, res); t->elem->alg->copy(t->elem->size, av, hash_indirect(h, res+h->valoff)); } erlang-cherly-0.12.8+dfsg/c_src/hashmap.h000066400000000000000000000140361215305244000201400ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef __HASHMAP__ #define __HASHMAP__ #include "runtime.h" /* A hash table. Example, hashing nul-terminated char*s: hash_hash_t str_hash (void *v) { char *s; hash_hash_t hash = 0; for (s = *(char **)v; *s != 0; s++) { hash = (hash ^ *s) * 2654435769U; } return (hash); } int str_eq (void *a, void *b) { return (strcmp (*(char **)a, *(char **)b) == 0); } void str_del (void *arg, void *data) { *(char **)arg = *(char **)data; } struct hash *h = hash_new (sizeof (char *), &str_hash, &str_eq, &str_del, 3, 12, 15); ... 3=> 2**3 entries initial size ... 12=> 2**12 entries before sprouting sub-tables ... 15=> number of adjacent probes to attempt before growing Example lookup: char *key = "foobar"; char **result_ptr; if (hash_lookup (h, &key, (void **) &result_ptr)) { printf ("found in table: %s\n", *result_ptr); } else { printf ("not found in table\n"); } Example insertion: char *key = strdup ("foobar"); char **result_ptr; if (hash_lookup (h, &key, (void **) &result_ptr)) { printf ("found in table: %s\n", *result_ptr); printf ("to overwrite, do *result_ptr = key\n"); } else { printf ("not found in table; inserted as %s\n", *result_ptr); assert (*result_ptr == key); } Example deletion: char *key = "foobar"; char *result; if (hash_remove (h, &key, &result)) { printf ("key found and deleted from table\n"); printf ("called str_del (&result, data) to copy data to result: %s\n", result); } else { printf ("not found in table\n"); } Example iteration over the elements of *h: char **data; struct hash_iter it; hash_iter_init (h, &it); for (data = hash_next (&it); data != 0; data = hash_next (&it)) { printf ("%s\n", *data); } */ /* #define malloc runtime·mal #define memset(a,b,c) runtime·memclr((byte*)(a), (uint32)(c)) #define memcpy(a,b,c) runtime·memmove((byte*)(a),(byte*)(b),(uint32)(c)) #define assert(a) if(!(a)) runtime·throw("hashmap assert") #define free(x) runtime·free(x) #define memmove(a,b,c) runtime·memmove(a, b, c) */ struct Hmap; /* opaque */ struct hash_subtable; /* opaque */ struct hash_entry; /* opaque */ typedef uintptr hash_hash_t; struct hash_iter { uint8* data; /* returned from next */ int32 elemsize; /* size of elements in table */ int32 changes; /* number of changes observed last time */ int32 i; /* stack pointer in subtable_state */ bool cycled; /* have reached the end and wrapped to 0 */ hash_hash_t last_hash; /* last hash value returned */ hash_hash_t cycle; /* hash value where we started */ struct Hmap *h; /* the hash table */ MapType *t; /* the map type */ struct hash_iter_sub { struct hash_entry *e; /* pointer into subtable */ struct hash_entry *start; /* start of subtable */ struct hash_entry *last; /* last entry in subtable */ } subtable_state[4]; /* Should be large enough unless the hashing is so bad that many distinct data values hash to the same hash value. */ }; /* Return a hashtable h 2**init_power empty entries, each with "datasize" data bytes. (*data_hash)(a) should return the hash value of data element *a. (*data_eq)(a,b) should return whether the data at "a" and the data at "b" are equal. (*data_del)(arg, a) will be invoked when data element *a is about to be removed from the table. "arg" is the argument passed to "hash_remove()". Growing is accomplished by resizing if the current tables size is less than a threshold, and by adding subtables otherwise. hint should be set the expected maximum size of the table. "datasize" should be in [sizeof (void*), ..., 255]. If you need a bigger "datasize", store a pointer to another piece of memory. */ //struct hash *hash_new (int32 datasize, // hash_hash_t (*data_hash) (void *), // int32 (*data_eq) (void *, void *), // void (*data_del) (void *, void *), // int64 hint); /* Lookup *data in *h. If the data is found, return 1 and place a pointer to the found element in *pres. Otherwise return 0 and place 0 in *pres. */ //int32 hash_lookup (struct hash *h, void *data, void **pres); /* Lookup *data in *h. If the data is found, execute (*data_del) (arg, p) where p points to the data in the table, then remove it from *h and return 1. Otherwise return 0. */ //int32 hash_remove (struct hash *h, void *data, void *arg); /* Lookup *data in *h. If the data is found, return 1, and place a pointer to the found element in *pres. Otherwise, return 0, allocate a region for the data to be inserted, and place a pointer to the inserted element in *pres; it is the caller's responsibility to copy the data to be inserted to the pointer returned in *pres in this case. If using garbage collection, it is the caller's responsibility to add references for **pres if HASH_ADDED is returned. */ //int32 hash_insert (struct hash *h, void *data, void **pres); /* Return the number of elements in the table. */ //uint32 hash_count (struct hash *h); /* The following call is useful only if not using garbage collection on the table. Remove all sub-tables associated with *h. This undoes the effects of hash_init(). If other memory pointed to by user data must be freed, the caller is responsible for doiing do by iterating over *h first; see hash_iter_init()/hash_next(). */ //void hash_destroy (struct hash *h); /*----- iteration -----*/ /* Initialize *it from *h. */ //void hash_iter_init (struct hash *h, struct hash_iter *it); /* Return the next used entry in the table which which *it was initialized. */ //void *hash_next (struct hash_iter *it); /*---- test interface ----*/ /* Call (*data_visit) (arg, level, data) for every data entry in the table, whether used or not. "level" is the subtable level, 0 means first level. */ /* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */ //void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg); #endif erlang-cherly-0.12.8+dfsg/c_src/lru.c000066400000000000000000000040601215305244000173100ustar00rootroot00000000000000#include #include #include "common.h" #include "lru.h" #include "double_link.h" static void lru_destroy_item(lru_item_t *item); lru_t * lru_create() { lru_t * lru = malloc(sizeof(lru_t)); if (lru == NULL) return NULL; lru->list = d_list_create(); return lru; } void lru_destroy(lru_t *lru) { d_node_t *node; lru_item_t *item; node = lru->list->head; while (NULL != node) { item = (lru_item_t*)node->data; lru_destroy_item(item); node = node->next; } d_list_destroy(lru->list); free(lru); } int lru_eject_by_size(lru_t *lru, int size, EjectionCallback eject, void * container) { int ejected = 0; lru_item_t *item; d_node_t *node; d_node_t *next; dprintf("ejecting %d bytes\n", size); while(ejected < size) { node = d_list_shift(lru->list); if (NULL == node) { break; } item = (lru_item_t*)node->data; ejected += lru_item_size(item); if (NULL != eject) { (*eject)(container, item->key, item->keylen); } lru_destroy_item(item); next = node->next; d_node_destroy(node); node = next; } return ejected; } lru_item_t * lru_insert(lru_t *lru, char* key, int keylen, void * value, int size, DestroyCallback destroy) { lru_item_t *item; item = malloc(sizeof(lru_item_t)); if (item == NULL) return NULL; item->key = key; item->keylen = keylen; item->value = value; item->vallen = size; item->destroy = destroy; item->node = d_node_create(item); d_list_push(lru->list, item->node); return item; } void lru_touch(lru_t *lru, lru_item_t *item) { d_list_remove(lru->list, item->node); d_list_push(lru->list, item->node); } void lru_remove_and_destroy(lru_t *lru, lru_item_t *item) { d_list_remove(lru->list, item->node); d_node_destroy(item->node); lru_destroy_item(item); } static void lru_destroy_item(lru_item_t *item) { if (NULL != item->destroy) { (*(item->destroy))(item->key, item->keylen, item->value, item->vallen); dprintf("invoke destroy callback %s %p\n", item->key, item->value); } free(item); } erlang-cherly-0.12.8+dfsg/c_src/lru.h000066400000000000000000000022401215305244000173130ustar00rootroot00000000000000#ifndef __LRU__ #define __LRU__ #include "double_link.h" //the destroy callback, this is needed to free memory for shit typedef void (*DestroyCallback)(char*, int, void*, int); //this is the callback for LRU ejection, so that upper layers can cleanup //first arg is the containing struct. can this be cleaner? typedef void (*EjectionCallback)(void*, char*, int); typedef struct _lru_t { d_list_t *list; } lru_t; typedef struct _lru_item_t { char * key; int keylen; void * value; int vallen; d_node_t * node; DestroyCallback destroy; } lru_item_t; #define lru_item_key(item) ((item)->key) #define lru_item_keylen(item) ((item)->keylen) #define lru_item_value(item) ((item)->value) #define lru_item_vallen(item) ((item)->vallen) #define lru_item_size(item) (lru_item_keylen(item) + lru_item_vallen(item)) lru_t * lru_create(); void lru_destroy(lru_t *lru); int lru_eject_by_size(lru_t *lru, int size, EjectionCallback cb, void * container); lru_item_t * lru_insert(lru_t *lru, char* key, int keylen, void * value, int size, DestroyCallback destroy); void lru_touch(lru_t *lru, lru_item_t *item); void lru_remove_and_destroy(lru_t *lru, lru_item_t *item); #endiferlang-cherly-0.12.8+dfsg/c_src/runtime.c000066400000000000000000000016651215305244000202010ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "runtime.h" enum { maxround = sizeof(uintptr), }; static uint32 fastrand; int32 runtime_mcmp(byte *s1, byte *s2, uint32 n) { uint32 i; byte c1, c2; for(i=0; i c2) return +1; } return 0; } byte* runtime_mchr(byte *p, byte c, byte *ep) { for(; p < ep; p++) if(*p == c) return p; return nil; } uint32 runtime_rnd(uint32 n, uint32 m) { uint32 r; if(m > maxround) m = maxround; r = n % m; if(r) n += m-r; return n; } int32 runtime_atoi(byte *p) { int32 n; n = 0; while('0' <= *p && *p <= '9') n = n*10 + *p++ - '0'; return n; } uint32 runtime_fastrand1(void) { uint32 x; x = fastrand; x += x; if(x & 0x80000000L) x ^= 0x88888eefUL; fastrand = x; return x; } erlang-cherly-0.12.8+dfsg/c_src/runtime.h000066400000000000000000000052031215305244000201760ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef __RUNTIME__ #define __RUNTIME__ /* * basic types */ typedef signed char int8; typedef unsigned char uint8; typedef signed short int16; typedef unsigned short uint16; typedef signed int int32; typedef unsigned int uint32; typedef signed long long int int64; typedef unsigned long long int uint64; typedef float float32; typedef double float64; #if defined(__ia64) || defined(__x86_64) || defined(__amd64) typedef uint64 uintptr; typedef int64 intptr; #else typedef uint32 uintptr; typedef int32 intptr; #endif /* * get rid of C types * the / / / forces a syntax error immediately, * which will show "last name: XXunsigned". */ //#define unsigned XXunsigned / / / //#define signed XXsigned / / / //#define char XXchar / / / //#define short XXshort / / / //#define int XXint / / / //#define long XXlong / / / //#define float XXfloat / / / //#define double XXdouble / / / #define nelem(x) (sizeof(x)/sizeof((x)[0])) #define nil ((void*)0) #define offsetof(s,m) (uint32)(&(((s*)0)->m)) /* * defined types */ typedef uint8 bool; typedef uint8 byte; typedef struct String String; typedef struct Type Type; typedef struct MapType MapType; typedef struct Hmap Hmap; typedef struct hash_iter hash_iter; enum { true = 1, false = 0, }; struct String { byte* str; int32 len; }; typedef struct Alg Alg; struct Alg { void (*hash)(uintptr*, uintptr, void*); void (*equal)(bool*, uintptr, void*, void*); //void (*print)(uintptr, void*); void (*copy)(uintptr, void*, void*); }; extern Alg StrAlg; extern Type StrType; extern MapType StrMapType; void runtime_memhash(uintptr*, uintptr, void*); void runtime_strhash(uintptr*, uintptr, void*); void runtime_memequal(bool*, uintptr, void*, void*); void runtime_strequal(bool*, uintptr, void*, void*); void runtime_strcopy(uintptr, void*, void*); uint32 runtime_rnd(uint32, uint32); byte* runtime_mchr(byte*, byte, byte*); int32 runtime_mcmp(byte*, byte*, uint32); int32 runtime_atoi(byte*); uint32 runtime_fastrand1(void); int32 runtime_mapassign(MapType*, Hmap*, byte*, byte*); void runtime_mapaccess(MapType*, Hmap*, byte*, byte*, bool*); void runtime_mapiternext(hash_iter*); bool runtime_mapiterkey(hash_iter*, void*); void runtime_mapiterkeyvalue(hash_iter*, void*, void*); void runtime_mapiterinit(MapType*, Hmap*, hash_iter*); Hmap* runtime_makemap_c(MapType*, int64); void runtime_mapdestroy(Hmap*); // for debug void hash_visit (Hmap*, void (*data_visit) (void*, int32 , void*), void*); #endif erlang-cherly-0.12.8+dfsg/c_src/slabs.c000066400000000000000000000367511215305244000176260ustar00rootroot00000000000000/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Slabs memory allocation, based on powers-of-N. Slabs are up to 1MB in size * and are divided into chunks. The chunk sizes start off at the size of the * "item" structure plus space for a small key and value. They increase by * a multiplier factor from there, up to half the maximum slab size. The last * slab size is always 1MB, since that's the maximum item size allowed by the * memcached protocol. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "slabs.h" /* * Forward Declarations */ static int do_slabs_newslab(slabs_t* pst, const unsigned int id); static void *memory_allocate(slabs_t* pst, size_t size); /* * slab pool management */ void* pool_new(slabs_t* pst) { void *ptr; slabheader_t *shp; if (pst->pool_freelist == NULL) { if (pst->mem_limit && (pst->mem_malloced + SETTING_ITEM_SIZE_MAX > pst->mem_limit)) { return NULL; } ptr = memory_allocate(pst, SETTING_ITEM_SIZE_MAX); if (!ptr) return NULL; pst->mem_malloced += SETTING_ITEM_SIZE_MAX; shp = (slabheader_t*)ptr; shp->next = NULL; pst->pool_freelist = ptr; } shp = pst->pool_freelist; pst->pool_freelist = shp->next; return (void*)shp; } void pool_free(slabs_t* pst, void* ptr) { slabheader_t *shp; shp = (slabheader_t*)ptr; shp->next = pst->pool_freelist; pst->pool_freelist = shp; } /* * slab list management per slabclass */ bool slab_add(slabs_t* pst, slabclass_t* psct, void* ptr) { size_t need_byte; slablist_t* pslt = (slablist_t*)memory_allocate(pst, sizeof(slablist_t)); if (!pslt) return false; need_byte = (size_t)ceil(psct->perslab / 8.0); pslt->used_bitmap = (unsigned char*)memory_allocate(pst, need_byte); if (!pslt->used_bitmap) return false; memset(pslt->used_bitmap, 0, need_byte); pslt->ptr = ptr; pslt->next = psct->slab_list; psct->slab_list = pslt; return true; } void* slab_remove(slabs_t* pst, slabclass_t* psct, slablist_t* pslt_target) { void* pret; slablist_t* pslt = psct->slab_list; slablist_t* pprev = NULL; while (pslt != NULL) { if (pslt == pslt_target) { if (pprev) { pprev->next = pslt->next; } else { psct->slab_list = pslt->next; } pret = pslt->ptr; free(pslt->used_bitmap); free(pslt); return pret; } pprev = pslt; pslt = pslt->next; } return NULL; } slablist_t* slab_search(slabs_t* pst, slabclass_t* psct, char* ptr_in_slab) { slablist_t* pslt = psct->slab_list; char* pstart; char* pend; while (pslt != NULL) { pstart = (char*)pslt->ptr; pend = pstart + SETTING_ITEM_SIZE_MAX; if (ptr_in_slab >= pstart && ptr_in_slab <= pend) return pslt; pslt = pslt->next; } return NULL; } /* * slab free space management per slab */ #define SLABLIST_USED_IDX(pi, pbi, psct, pslt, ptr_in_slab) \ size_t byte_offset = (size_t)(ptr_in_slab - ((char*)pslt->ptr)); \ *pi = (size_t)(byte_offset / psct->size); \ *pbi = (size_t)round(index / 8) inline void slablist_used(slabclass_t* psct, slablist_t* pslt, char* ptr_in_slab) { size_t index; size_t bmp_index; SLABLIST_USED_IDX(&index, &bmp_index, psct, pslt, ptr_in_slab); unsigned char bitmask = (unsigned char)(1 << (index % 8)); pslt->used_bitmap[bmp_index] |= bitmask; } inline void slablist_unused(slabclass_t* psct, slablist_t* pslt, char* ptr_in_slab) { size_t index; size_t bmp_index; SLABLIST_USED_IDX(&index, &bmp_index, psct, pslt, ptr_in_slab); unsigned char bitmask = ~(unsigned char)(1 << (index % 8)); pslt->used_bitmap[bmp_index] &= bitmask; } inline bool slablist_is_empty(slabclass_t* psct, slablist_t* pslt) { unsigned char* pcurrent = (unsigned char*)pslt->used_bitmap; size_t need_byte = (size_t)ceil(psct->perslab / 8.0); while (need_byte > 0) { if (need_byte >= sizeof(unsigned int)) { if (*((unsigned int*)pcurrent)) return false; need_byte -= sizeof(unsigned int); pcurrent += sizeof(unsigned int); } else if (need_byte >= sizeof(unsigned short)) { if (*((unsigned short*)pcurrent)) return false; need_byte -= sizeof(unsigned short); pcurrent += sizeof(unsigned short); } else { if (*pcurrent) return false; need_byte -= sizeof(unsigned char); pcurrent += sizeof(unsigned char); } } return true; } /* * Figures out which slab class (chunk size) is required to store an item of * a given size. * * Given object size, return id to use when allocating/freeing memory for object * 0 means error: can't store such a large object */ static unsigned int slabs_clsid(slabs_t* pst, const size_t size) { int res = POWER_SMALLEST; if (size == 0) return 0; while (size > pst->slabclass[res].size) if (res++ == pst->power_largest) /* won't fit in the biggest slab */ return 0; return res; } /** * Determines the chunk sizes and initializes the slab class descriptors * accordingly. */ void slabs_init(slabs_t* pst, const size_t limit, const double factor, const bool prealloc) { int i = POWER_SMALLEST - 1; unsigned int size = sizeof(slabheader_t) + SETTING_CHUNK_SIZE; if (limit > 0 && limit < SETTING_ITEM_SIZE_MAX) { pst->mem_limit = SETTING_ITEM_SIZE_MAX; } else { pst->mem_limit = limit; } pst->pool_freelist = NULL; if (prealloc) { /* Allocate everything in a big chunk with malloc */ pst->mem_base = malloc(pst->mem_limit); if (pst->mem_base != NULL) { pst->mem_current = pst->mem_base; pst->mem_avail = pst->mem_limit; } else { fprintf(stderr, "Warning: Failed to allocate requested memory in" " one large chunk.\nWill allocate in smaller chunks\n"); } } memset(pst->slabclass, 0, sizeof(pst->slabclass)); while (++i < POWER_LARGEST && size <= SETTING_ITEM_SIZE_MAX / factor) { /* Make sure items are always n-byte aligned */ if (size % CHUNK_ALIGN_BYTES) size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); pst->slabclass[i].size = size; pst->slabclass[i].perslab = SETTING_ITEM_SIZE_MAX / pst->slabclass[i].size; size *= factor; if (SETTING_VERBOSE > 1) { fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, pst->slabclass[i].size, pst->slabclass[i].perslab); } } pst->power_largest = i; pst->slabclass[pst->power_largest].size = SETTING_ITEM_SIZE_MAX; pst->slabclass[pst->power_largest].perslab = 1; if (SETTING_VERBOSE > 1) { fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, pst->slabclass[i].size, pst->slabclass[i].perslab); fprintf(stderr, "pst:%p\n", pst); } } static int do_slabs_newslab(slabs_t* pst, const unsigned int id) { slabclass_t *p = &pst->slabclass[id]; void* ptr = pool_new(pst); if (ptr == NULL) return 0; p->end_page_ptr = ptr; p->end_page_free = p->perslab; bool ret = slab_add(pst, p, ptr); if (!ret) return 0; return 1; } static void *do_slabs_alloc(slabs_t* pst, const size_t size, unsigned int id) { slabclass_t *p; void *ret = NULL; slabheader_t *it = NULL; slablist_t* pslt = NULL; if (id < POWER_SMALLEST || id > pst->power_largest) { //MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0); return NULL; } p = &pst->slabclass[id]; //printf("alloc slab:%p class:%u ent_page_p:%p sl_curr:%u \n", pst, p->size, p->end_page_ptr, p->sl_curr); /* fail unless we have space at the end of a recently allocated page, we have something on our freelist, or we could allocate a new page */ if (! (p->end_page_ptr != 0 || p->sl_curr != 0 || do_slabs_newslab(pst, id) != 0)) { /* We don't have more memory available */ ret = NULL; } else if (p->sl_curr != 0) { /* return off our freelist */ it = (slabheader_t*)p->slots; p->slots = it->next; if (it->next) it->next->prev = 0; p->sl_curr--; ret = (void *)it; pslt = slab_search(pst, p, (char*)ret); slablist_used(p, pslt, (char*)ret); } else { /* if we recently allocated a whole page, return from that */ assert(p->end_page_ptr != NULL); ret = p->end_page_ptr; if (--p->end_page_free != 0) { p->end_page_ptr = ((caddr_t)p->end_page_ptr) + p->size; } else { p->end_page_ptr = 0; } pslt = slab_search(pst, p, (char*)ret); slablist_used(p, pslt, (char*)ret); } if (ret) { p->requested += size; //MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret); } else { //MEMCACHED_SLABS_ALLOCATE_FAILED(size, id); } //printf("alloc ps:%p sid:%u p:%p np:%p cnt:%u used:%lu rest:%u \n", pst, id, ret, p->end_page_ptr, p->sl_curr, p->requested, p->end_page_free); return ret; } static void do_slabs_free(slabs_t* pst, void *ptr, const size_t size, unsigned int id) { slabclass_t *p; slabheader_t* it; slablist_t* pslt; bool ret; void* ppool; //assert(((item *)ptr)->slabs_clsid == 0); assert(id >= POWER_SMALLEST && id <= pst->power_largest); if (id < POWER_SMALLEST || id > pst->power_largest) return; //MEMCACHED_SLABS_FREE(size, id, ptr); p = &pst->slabclass[id]; it = (slabheader_t*)ptr; //it->it_flags |= ITEM_SLABBED; it->prev = 0; it->next = p->slots; if (it->next) it->next->prev = it; p->slots = it; p->sl_curr++; p->requested -= size; //printf("free slab:%p class:%u ent_page_p:%p sl_curr:%u \n", pst, p->size, p->end_page_ptr, p->sl_curr); pslt = slab_search(pst, p, (char*)ptr); slablist_unused(p, pslt, (char*)ptr); ret = slablist_is_empty(p, pslt); if (ret) { // release slab from freelist(slots) slabheader_t* pit = it; slabheader_t* pprev = NULL; while (pit != NULL) { slablist_t* pslt_curr = slab_search(pst, p, (char*)pit); if (pslt == pslt_curr) { if (pprev) { pprev->next = pit->next; } else { p->slots = pit->next; } p->sl_curr--; } else { pprev = pit; } pit = pit->next; } // release slab from end_page(end_page_ptr, end_page_free) slablist_t* pslt_endp = slab_search(pst, p, (char*)p->end_page_ptr); if (pslt_endp == pslt) { p->end_page_ptr = NULL; p->end_page_free = 0; } // release slab from slab_list ppool = slab_remove(pst, p, pslt); pool_free(pst, ppool); } return; } /* static int nz_strcmp(int nzlength, const char *nz, const char *z) { int zlength=strlen(z); return (zlength == nzlength) && (strncmp(nz, z, zlength) == 0) ? 0 : -1; } bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c) { bool ret = true; if (add_stats != NULL) { if (!stat_type) { STATS_LOCK(); APPEND_STAT("bytes", "%llu", (unsigned long long)stats.curr_bytes); APPEND_STAT("curr_items", "%u", stats.curr_items); APPEND_STAT("total_items", "%u", stats.total_items); APPEND_STAT("evictions", "%llu", (unsigned long long)stats.evictions); APPEND_STAT("reclaimed", "%llu", (unsigned long long)stats.reclaimed); STATS_UNLOCK(); } else if (nz_strcmp(nkey, stat_type, "items") == 0) { item_stats(add_stats, c); } else if (nz_strcmp(nkey, stat_type, "slabs") == 0) { slabs_stats(add_stats, c); } else if (nz_strcmp(nkey, stat_type, "sizes") == 0) { item_stats_sizes(add_stats, c); } else { ret = false; } } else { ret = false; } return ret; } static void do_slabs_stats(ADD_STAT add_stats, void *c) { int i, total; struct thread_stats thread_stats; threadlocal_stats_aggregate(&thread_stats); total = 0; for(i = POWER_SMALLEST; i <= power_largest; i++) { slabclass_t *p = &slabclass[i]; if (p->slabs != 0) { uint32_t perslab, slabs; slabs = p->slabs; perslab = p->perslab; char key_str[STAT_KEY_LEN]; char val_str[STAT_VAL_LEN]; int klen = 0, vlen = 0; APPEND_NUM_STAT(i, "chunk_size", "%u", p->size); APPEND_NUM_STAT(i, "chunks_per_page", "%u", perslab); APPEND_NUM_STAT(i, "total_pages", "%u", slabs); APPEND_NUM_STAT(i, "total_chunks", "%u", slabs * perslab); APPEND_NUM_STAT(i, "used_chunks", "%u", slabs*perslab - p->sl_curr - p->end_page_free); APPEND_NUM_STAT(i, "free_chunks", "%u", p->sl_curr); APPEND_NUM_STAT(i, "free_chunks_end", "%u", p->end_page_free); APPEND_NUM_STAT(i, "mem_requested", "%llu", (unsigned long long)p->requested); APPEND_NUM_STAT(i, "get_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].get_hits); APPEND_NUM_STAT(i, "cmd_set", "%llu", (unsigned long long)thread_stats.slab_stats[i].set_cmds); APPEND_NUM_STAT(i, "delete_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].delete_hits); APPEND_NUM_STAT(i, "incr_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].incr_hits); APPEND_NUM_STAT(i, "decr_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].decr_hits); APPEND_NUM_STAT(i, "cas_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].cas_hits); APPEND_NUM_STAT(i, "cas_badval", "%llu", (unsigned long long)thread_stats.slab_stats[i].cas_badval); APPEND_NUM_STAT(i, "touch_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].touch_hits); total++; } } APPEND_STAT("active_slabs", "%d", total); APPEND_STAT("total_malloced", "%llu", (unsigned long long)mem_malloced); add_stats(NULL, 0, NULL, 0, c); } */ static void *memory_allocate(slabs_t* pst, size_t size) { void *ret; if (pst->mem_base == NULL) { /* We are not using a preallocated large memory chunk */ ret = malloc(size); } else { ret = pst->mem_current; if (size > pst->mem_avail) { return NULL; } /* mem_current pointer _must_ be aligned!!! */ if (size % CHUNK_ALIGN_BYTES) { size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); } pst->mem_current = ((char*)pst->mem_current) + size; if (size < pst->mem_avail) { pst->mem_avail -= size; } else { pst->mem_avail = 0; } } return ret; } void *slabs_alloc(slabs_t* pst, size_t size) { void *ret; size += sizeof(slabheader_t); unsigned int id = slabs_clsid(pst, size); ret = do_slabs_alloc(pst, size, id); if (ret == NULL) { return NULL; } return (void*)((char*)ret + sizeof(slabheader_t)); } void slabs_free(slabs_t* pst, void *ptr, size_t size) { void *header; size += sizeof(slabheader_t); unsigned int id = slabs_clsid(pst, size); header = (void*)((char*)ptr - sizeof(slabheader_t)); do_slabs_free(pst, header, size, id); } /* void slabs_stats(ADD_STAT add_stats, void *c) { pthread_mutex_lock(&slabs_lock); do_slabs_stats(add_stats, c); pthread_mutex_unlock(&slabs_lock); } */ erlang-cherly-0.12.8+dfsg/c_src/slabs.h000066400000000000000000000061371215305244000176260ustar00rootroot00000000000000/* slabs memory allocation */ #ifndef SLABS_H #define SLABS_H #include "runtime.h" #define SETTING_CHUNK_SIZE 128 #define SETTING_ITEM_SIZE_MAX 1024 * 1024 * 4 #define POWER_LARGEST 200 #define MAX_NUMBER_OF_SLAB_CLASSES (POWER_LARGEST + 1) #define POWER_SMALLEST 1 #define CHUNK_ALIGN_BYTES 8 #define SETTING_VERBOSE 2 #define MAX_NUMBER_OF_SLAB_CLASSES (POWER_LARGEST + 1) typedef struct slabheader { struct slabheader *next; struct slabheader *prev; } slabheader_t; typedef struct slablist { void *ptr; unsigned char *used_bitmap; // using perslab/8 struct slablist *next; } slablist_t; typedef struct { unsigned int size; /* sizes of items */ unsigned int perslab; /* how many items per slab */ void *slots; /* list of item ptrs */ unsigned int sl_curr; /* total free items in list */ void *end_page_ptr; /* pointer to next free item at end of page, or 0 */ unsigned int end_page_free; /* number of items remaining at end of last alloced page */ unsigned int slabs; /* how many slabs were allocated for this class */ //@TODO void **slab_list; /* array of slab pointers */ slablist_t *slab_list; /* array of slab pointers */ unsigned int list_size; /* size of prev array */ unsigned int killing; /* index+1 of dying slab, or zero if none */ size_t requested; /* The number of requested bytes */ } slabclass_t; // per thread typedef struct { slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES]; size_t mem_limit; size_t mem_malloced; int power_largest; void *mem_base; void *mem_current; size_t mem_avail; void *pool_freelist; } slabs_t; void* pool_new(slabs_t* pst); void pool_free(slabs_t* pst, void* ptr); bool slab_add(slabs_t* pst, slabclass_t* psct, void* ptr); void* slab_remove(slabs_t* pst, slabclass_t* psct, slablist_t* pslt_target); slablist_t* slab_search(slabs_t* pst, slabclass_t* psct, char* ptr_in_slab); void slablist_used(slabclass_t* psct, slablist_t* pslt, char* ptr_in_slab); void slablist_unused(slabclass_t* psct, slablist_t* pslt, char* ptr_in_slab); bool slablist_is_empty(slabclass_t* psct, slablist_t* pslt); /** Init the subsystem. 1st argument is the limit on no. of bytes to allocate, 0 if no limit. 2nd argument is the growth factor; each slab will use a chunk size equal to the previous slab's chunk size times this factor. 3rd argument specifies if the slab allocator should allocate all memory up front (if true), or allocate memory in chunks as it is needed (if false) */ void slabs_init(slabs_t* pst, const size_t limit, const double factor, const bool prealloc); /** * Given object size, return id to use when allocating/freeing memory for object * 0 means error: can't store such a large object */ //unsigned int slabs_clsid(slabs_t* pst, const size_t size); /** Allocate object of given length. 0 on error */ /*@null@*/ void *slabs_alloc(slabs_t* pst, const size_t size); /** Free previously allocated object */ void slabs_free(slabs_t* pst, void *ptr, size_t size); /** Fill buffer with stats */ /*@null@*/ //void slabs_stats(ADD_STAT add_stats, void *c); #endif erlang-cherly-0.12.8+dfsg/c_src/test/000077500000000000000000000000001215305244000173215ustar00rootroot00000000000000erlang-cherly-0.12.8+dfsg/c_src/test/check_cherly.c000066400000000000000000000061311215305244000221110ustar00rootroot00000000000000#include #include "common.h" #include #include "cherly.h" static slabs_t slab; static void print_visitor(void *arg, int32 level, void *data) { int i; String* key = (String*)data; String* val = (String*)((byte*)data+runtime_rnd(sizeof(String), sizeof(void*))); if (key->str == nil) return; printf("%s\n", (const char*)arg); for (i = 0; i < level; i++) printf("\t"); printf("mapassign: level=%d key=%s val=%s pk=%p pv=%p \n", level, key->str, val->str, key, val); } START_TEST(basic_get_and_put) { cherly_t cherly; char* key = "exist"; char* stuff = "stuff"; int len; void* buf = slabs_alloc(&slab, 20); printf("basic: buf=%p \n", buf); memset(buf, 0, 20); memcpy(buf, key, 5); void* vbuf = ((char*)buf)+6; memcpy(vbuf, stuff, 5); cherly_init(&cherly, 0, 120); fail_unless(NULL == cherly_get(&cherly, "noexist", 7, &len)); cherly_put(&cherly, buf, 5, vbuf, 5, NULL); hash_visit(cherly.hm, print_visitor, "basic"); fail_unless(NULL == cherly_get(&cherly, "noexist", 7, &len)); void* ret = cherly_get(&cherly, key, 5, &len); fail_unless(memcmp(ret, vbuf, len) == 0); cherly_destroy(&cherly); //slabs_free(&slab, buf, 20); } END_TEST START_TEST(put_already_there) { cherly_t cherly; char* stuff = "stuff"; char* otherstuff = "blah"; void* buf = slabs_alloc(&slab, 20); printf("basic: buf=%p \n", buf); memset(buf, 0, 20); memcpy(buf, stuff, 5); void* nbuf = ((char*)buf)+6; memcpy(nbuf, otherstuff, 4); cherly_init(&cherly, 0, 120); cherly_put(&cherly, "exist", 5, buf, 5, NULL); fail_unless(10 == cherly_size(&cherly)); cherly_put(&cherly, "exist", 5, nbuf, 4, NULL); hash_visit(cherly.hm, print_visitor, "put already"); cherly_destroy(&cherly); } END_TEST START_TEST(put_beyond_limit) { cherly_t cherly; cherly_init(&cherly, 0, 12); void* buf = slabs_alloc(&slab, 10); printf("basic: buf=%p \n", buf); memset(buf, 0, 10); memcpy(buf, "one", 3); cherly_put(&cherly, "one", 3, buf, 3, NULL); hash_visit(cherly.hm, print_visitor, "put1"); slabs_free(&slab, buf, 10); void* buf2 = slabs_alloc(&slab, 10); printf("basic: buf2=%p \n", buf2); memset(buf2, 0, 10); memcpy(buf2, "two", 3); cherly_put(&cherly, "two", 3, buf2, 3, NULL); hash_visit(cherly.hm, print_visitor, "put2"); slabs_free(&slab, buf2, 10); void* buf3 = slabs_alloc(&slab, 10); printf("basic: buf3=%p \n", buf3); memset(buf3, 0, 10); memcpy(buf3, "three", 5); cherly_put(&cherly, "three", 5, buf3, 5, NULL); hash_visit(cherly.hm, print_visitor, "put3"); fail_unless(1 == cherly_items_length(&cherly), "cherly length was %d", cherly_items_length(&cherly)); fail_unless(10 == cherly_size(&cherly), "cherly size was %d", cherly_size(&cherly)); } END_TEST Suite * cherly_suite(void) { Suite *s = suite_create("cherly.c"); /* Core test case */ memset(&slab, 0, sizeof(slab)); slabs_init(&slab, 0, 1.5, false); TCase *tc_core = tcase_create("Core"); tcase_add_test(tc_core, basic_get_and_put); tcase_add_test(tc_core, put_already_there); tcase_add_test(tc_core, put_beyond_limit); suite_add_tcase(s, tc_core); return s; } erlang-cherly-0.12.8+dfsg/c_src/test/check_double_link.c000066400000000000000000000013121215305244000231060ustar00rootroot00000000000000#include "double_link.h" #include "stdlib.h" #include #include "common.h" START_TEST(create_and_remove) { d_list_t *list; d_node_t *node; int i; list = d_list_create(); node = d_node_create(NULL); fail_unless(node != NULL, NULL); d_list_push(list, node); fail_unless(1 == d_list_size(list), NULL); for(i=0; i<5; i++) { node = d_node_create(NULL); d_list_push(list, node); } fail_unless(6 == d_list_size(list), NULL); } END_TEST Suite * double_link_suite(void) { Suite *s = suite_create ("double_link.c"); /* Core test case */ TCase *tc_core = tcase_create ("Core"); tcase_add_test (tc_core, create_and_remove); suite_add_tcase (s, tc_core); return s; }erlang-cherly-0.12.8+dfsg/c_src/test/check_lru.c000066400000000000000000000034321215305244000214260ustar00rootroot00000000000000#include "lru.h" #include "stdlib.h" #include "stdio.h" #include #include "common.h" START_TEST(create_and_insert) { lru_t *lru; lru = lru_create(); fail_unless(NULL != lru->list); lru_insert(lru, "key", 3, "value", 5, NULL); fail_unless(NULL != lru->list->head); lru_destroy(lru); } END_TEST START_TEST(touch) { lru_t *lru; lru_item_t * one; lru = lru_create(); one = lru_insert(lru, "one", 3, "one", 3, NULL); lru_insert(lru, "two", 3, "two", 3, NULL); lru_insert(lru, "three", 5, "three", 5, NULL); lru_touch(lru, one); fail_unless(one == (lru_item_t*)lru->list->head->data); lru_destroy(lru); } END_TEST START_TEST(eject_one) { lru_t *lru; int ejected = 0; lru = lru_create(); lru_insert(lru, "one", 3, "one", 3, NULL); lru_insert(lru, "two", 3, "two", 3, NULL); lru_insert(lru, "three", 5, "three", 5, NULL); ejected = lru_eject_by_size(lru, 3, NULL, NULL); dprintf("ejected %d\n", ejected); fail_unless(ejected > 0); } END_TEST START_TEST(eject_multiple) { lru_t *lru; int ejected = 0; lru_item_t *three; mark_point(); lru = lru_create(); mark_point(); lru_insert(lru, "one", 3, "one", 3, NULL); lru_insert(lru, "two", 3, "two", 3, NULL); three = lru_insert(lru, "three", 5, "three", 5, NULL); ejected = lru_eject_by_size(lru, 12, NULL, NULL); dprintf("test ejected %d\n", ejected); fail_unless((lru_item_t*)lru->list->head->data == three); fail_unless(ejected == 12); lru_destroy(lru); } END_TEST Suite * lru_suite(void) { Suite *s = suite_create("lru.c"); TCase *tc_core = tcase_create("Core"); tcase_add_test(tc_core, create_and_insert); tcase_add_test(tc_core, touch); tcase_add_test(tc_core, eject_one); tcase_add_test(tc_core, eject_multiple); suite_add_tcase(s, tc_core); return s; }erlang-cherly-0.12.8+dfsg/c_src/test/check_slabs.c000066400000000000000000000164511215305244000217350ustar00rootroot00000000000000#include #include #include #include "common.h" #include "slabs.h" static slabs_t slab; START_TEST(pool) { void* ps1 = pool_new(&slab); fail_unless(NULL != ps1); fail_unless(NULL == slab.pool_freelist); void* ps2 = pool_new(&slab); fail_unless(NULL != ps2); fail_unless(NULL == slab.pool_freelist); pool_free(&slab, ps1); fail_unless(ps1 == slab.pool_freelist); pool_free(&slab, ps2); fail_unless(ps2 == slab.pool_freelist); slabheader_t *shp = (slabheader_t*)slab.pool_freelist; fail_unless(ps1 == shp->next); void* ps3 = pool_new(&slab); fail_unless(ps2 == ps3); fail_unless(ps1 == slab.pool_freelist); void* ps4 = pool_new(&slab); fail_unless(ps1 == ps4); fail_unless(NULL == slab.pool_freelist); void* ps5 = pool_new(&slab); fail_unless(NULL != ps5); fail_unless(NULL == slab.pool_freelist); } END_TEST START_TEST(slablist) { slabclass_t* psct = &slab.slabclass[1]; // size:144 perslab:58254 void* ps = pool_new(&slab); bool ret = slab_add(&slab, psct, ps); fail_unless(ret); fail_unless(NULL != psct->slab_list); fail_unless(ps == psct->slab_list->ptr); fail_unless(NULL == psct->slab_list->next); size_t need_byte = (size_t)ceil(psct->perslab / 8); void* pv = malloc(need_byte); memset(pv, 0, need_byte); fail_unless(0 == memcmp(pv, psct->slab_list->used_bitmap, need_byte)); void* ps2 = pool_new(&slab); ret = slab_add(&slab, psct, ps2); fail_unless(ret); fail_unless(NULL != psct->slab_list); fail_unless(ps2 == psct->slab_list->ptr); fail_unless(NULL != psct->slab_list->next); fail_unless(NULL == psct->slab_list->next->next); void* ps3 = pool_new(&slab); ret = slab_add(&slab, psct, ps3); fail_unless(ret); slablist_t* pslt = slab_search(&slab, psct, ((char*)ps + 4)); fail_unless(pslt == psct->slab_list->next->next); pslt = slab_search(&slab, psct, ((char*)ps2)); fail_unless(pslt == psct->slab_list->next); pslt = slab_search(&slab, psct, ((char*)ps3 + SETTING_ITEM_SIZE_MAX)); fail_unless(pslt == psct->slab_list); pslt = slab_search(&slab, psct, 0); fail_unless(pslt == NULL); void* ps4 = slab_remove(&slab, psct, psct->slab_list->next); fail_unless(ps4 == ps2); fail_unless(psct->slab_list->ptr == ps3); fail_unless(psct->slab_list->next->ptr == ps); fail_unless(psct->slab_list->next->next == NULL); void* ps5 = slab_remove(&slab, psct, psct->slab_list); fail_unless(ps5 == ps3); fail_unless(psct->slab_list->ptr == ps); fail_unless(psct->slab_list->next == NULL); } END_TEST START_TEST(slab_used_bitmap) { slabclass_t* psct = &slab.slabclass[1]; // size:144 perslab:58254 void* ps = pool_new(&slab); slab_add(&slab, psct, ps); slablist_t* pslt = psct->slab_list; fail_unless(slablist_is_empty(psct, pslt)); char* pc1 = (char*)(pslt->ptr) + psct->size * 8; // should point to the head of 2byte slablist_used(psct, pslt, pc1); fail_unless(pslt->used_bitmap[1] == 1); fail_unless(!slablist_is_empty(psct, pslt)); char* pc2 = (char*)(pslt->ptr) + psct->size * 9; slablist_used(psct, pslt, pc2); fail_unless(pslt->used_bitmap[1] == 3); fail_unless(!slablist_is_empty(psct, pslt)); slablist_unused(psct, pslt, pc1); fail_unless(pslt->used_bitmap[1] == 2); fail_unless(!slablist_is_empty(psct, pslt)); slablist_unused(psct, pslt, pc2); fail_unless(pslt->used_bitmap[1] == 0); fail_unless(slablist_is_empty(psct, pslt)); } END_TEST START_TEST(slab_it) { // The freed slab exist at both(slots, end_page) slab.pool_freelist = NULL; void* p1 = slabs_alloc(&slab, 1000000); void* p2 = slabs_alloc(&slab, 1000000); slabs_free(&slab, p1, 1000000); fail_unless(slab.pool_freelist == NULL); slabclass_t* psct = &slab.slabclass[14]; fail_unless(psct->slab_list != NULL); fail_unless(psct->slab_list->next == NULL); fail_unless(psct->sl_curr == 1); fail_unless(psct->end_page_ptr != NULL); fail_unless(psct->end_page_free == 1); slabs_free(&slab, p2, 1000000); fail_unless(slab.pool_freelist != NULL); fail_unless(psct->slab_list == NULL); fail_unless(psct->sl_curr == 0); fail_unless(psct->end_page_ptr == NULL); fail_unless(psct->end_page_free == 0); // The freed slab exist at end_page void* p3 = slabs_alloc(&slab, 1000000); fail_unless(p1 == p3); fail_unless(slab.pool_freelist == NULL); fail_unless(psct->slots == NULL); slabs_free(&slab, p3, 1000000); fail_unless(slab.pool_freelist != NULL); slabheader_t *shp = (slabheader_t*)slab.pool_freelist; fail_unless(shp->next == NULL); fail_unless(psct->slab_list == NULL); fail_unless(psct->sl_curr == 0); fail_unless(psct->end_page_ptr == NULL); fail_unless(psct->end_page_free == 0); // The freed slab exist at slots void* p11 = slabs_alloc(&slab, 1000000); void* p12 = slabs_alloc(&slab, 1000000); void* p13 = slabs_alloc(&slab, 1000000); void* p14 = slabs_alloc(&slab, 1000000); // second slab start void* p15 = slabs_alloc(&slab, 1000000); void* p16 = slabs_alloc(&slab, 1000000); fail_unless(psct->end_page_ptr == NULL); fail_unless(psct->end_page_free == 0); fail_unless(psct->slab_list != NULL); fail_unless(psct->slab_list->next != NULL); fail_unless(psct->slab_list->next->next == NULL); slabs_free(&slab, p11, 1000000); slabs_free(&slab, p13, 1000000); slabs_free(&slab, p14, 1000000); slabs_free(&slab, p15, 1000000); fail_unless(psct->slots != NULL); slabs_free(&slab, p16, 1000000); shp = (slabheader_t*)psct->slots; fail_unless((shp + 1) == p13); fail_unless((shp->next + 1) == p11); fail_unless(shp->next->next == NULL); fail_unless(psct->end_page_ptr == NULL); fail_unless(psct->end_page_free == 0); shp = (slabheader_t*)slab.pool_freelist; fail_unless(shp->next == NULL); void* p17 = slabs_alloc(&slab, 1000000); fail_unless(p17 == p13); } END_TEST START_TEST(slab_exceed_limit) { void* p1; int i; slabs_t slab_ex; const size_t EXPECTED_MAX_ITEM = 4; const size_t ALLOC_SIZE = SETTING_ITEM_SIZE_MAX - sizeof(slabheader_t); memset(&slab_ex, 0, sizeof(slabs_t)); fprintf(stderr, "do exceed pst:%p\n", &slab_ex); slabs_init(&slab_ex, SETTING_ITEM_SIZE_MAX * EXPECTED_MAX_ITEM, 2, false); for (i = 0; i < EXPECTED_MAX_ITEM; i++) { p1 = slabs_alloc(&slab_ex, ALLOC_SIZE); fail_unless(p1 != NULL); } void* p2 = slabs_alloc(&slab_ex, ALLOC_SIZE); fail_unless(p2 == NULL); slabs_free(&slab_ex, p1, ALLOC_SIZE); // success if request size belongs to the above same slab void* p3 = slabs_alloc(&slab_ex, ALLOC_SIZE); fail_unless(p3 != NULL); // fail if request size belongs to a different slab void* p4 = slabs_alloc(&slab_ex, 512); fail_unless(p4 == NULL); } END_TEST Suite* slabs_suite(void) { Suite *s = suite_create("slabs.c"); /* Core test case */ memset(&slab, 0, sizeof(slab)); slabs_init(&slab, 0, 2, false); TCase *tc_core = tcase_create("Core"); tcase_add_test(tc_core, pool); tcase_add_test(tc_core, slablist); tcase_add_test(tc_core, slab_used_bitmap); tcase_add_test(tc_core, slab_it); tcase_add_test(tc_core, slab_exceed_limit); suite_add_tcase(s, tc_core); return s; } erlang-cherly-0.12.8+dfsg/c_src/test/suite.c000066400000000000000000000010301215305244000206100ustar00rootroot00000000000000#include #include #include Suite * cherly_suite(); Suite * double_link_suite(); Suite * lru_suite(); Suite * slabs_suite(); int main (void) { int number_failed; SRunner *sr = srunner_create(cherly_suite()); srunner_add_suite(sr, double_link_suite()); srunner_add_suite(sr, lru_suite()); srunner_add_suite(sr, slabs_suite()); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); srunner_free(sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } erlang-cherly-0.12.8+dfsg/c_src/type.h000066400000000000000000000004471215305244000175010ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #ifndef __TYPE_H__ #define __TYPE_H__ struct Type { uintptr size; Alg *alg; }; struct MapType { Type *key; Type *elem; }; #endif erlang-cherly-0.12.8+dfsg/include/000077500000000000000000000000001215305244000166745ustar00rootroot00000000000000erlang-cherly-0.12.8+dfsg/include/cherly.hrl000066400000000000000000000021771215305244000207000ustar00rootroot00000000000000%%====================================================================== %% %% Cherly %% %% Copyright (c) 2012 Stoic, Inc. %% %% This file is provided to you under the Apache License, %% Version 2.0 (the "License"); you may not use this file %% except in compliance with the License. You may obtain %% a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, %% software distributed under the License is distributed on an %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY %% KIND, either express or implied. See the License for the %% specific language governing permissions and limitations %% under the License. %% %% --------------------------------------------------------------------- %% ECache %% @doc %% @end %%====================================================================== -author("Yosuke Hara"). %% records. -record(cache_stats, { gets = 0 :: integer(), puts = 0 :: integer(), dels = 0 :: integer(), hits = 0 :: integer(), records = 0 :: integer(), cached_size = 0 :: integer() }). erlang-cherly-0.12.8+dfsg/rebar.config000066400000000000000000000021521215305244000175330ustar00rootroot00000000000000{require_otp_vsn, "R14B04|R15B02|R15B03|R16B"}. {erl_opts, [{d, 'NOTEST'}, warn_obsolete_guard, warn_unused_import, warnings_as_errors, warn_shadow_vars, warn_export_vars, warn_export_all]}. {xref_checks, [undefined_function_calls]}. {cover_enabled, true}. {clean_files, []}. {port_specs, [ {"priv/cherly.so", ["c_src/*.c"]} %% for UT: {"c_src/test/suite", ["c_src/alg.c", "c_src/cherly.c" , "c_src/double_link.c", "c_src/hashmap.c", "c_src/lru.c", "c_src/runtime.c", "c_src/slabs.c", "c_src/test/*.c"]} ]}. {port_env, [ {"CFLAGS", "$CFLAGS -fPIC -g -O2"}, {"DRV_CFLAGS", "$DRV_CFLAGS -Wall -Wno-unused-function -Wno-strict-aliasing -Wno-pointer-to-int-cast -Ic_src"}, {"DRV_LDFLAGS", "$DRV_LDFLAGS -lm"}, {"EXE_CFLAGS", "$EXE_CFLAGS -Wall -Wno-unused-function -Wno-strict-aliasing -Wno-pointer-to-int-cast -Ic_src/ -I/usr/include"}, {"EXE_LDFLAGS", "$EXE_LDFLAGS -lm -lcheck"} ]}. {post_hooks, [ %% for UT: {eunit, "c_src/test/suite"} ]}. erlang-cherly-0.12.8+dfsg/rebar.config.develop000066400000000000000000000021261215305244000211710ustar00rootroot00000000000000{require_otp_vsn, "R14B04|R15B02|R15B03|R16B"}. {erl_opts, [{d, 'NOTEST'}, warn_obsolete_guard, warn_unused_import, warnings_as_errors, warn_shadow_vars, warn_export_vars, warn_export_all]}. {xref_checks, [undefined_function_calls]}. {cover_enabled, true}. {clean_files, []}. {port_specs, [ {"priv/cherly.so", ["c_src/*.c"]}, {"c_src/test/suite", ["c_src/alg.c", "c_src/cherly.c" , "c_src/double_link.c", "c_src/hashmap.c", "c_src/lru.c", "c_src/runtime.c", "c_src/slabs.c", "c_src/test/*.c"]} ]}. {port_env, [ {"CFLAGS", "$CFLAGS -fPIC -g -O2"}, {"DRV_CFLAGS", "$DRV_CFLAGS -Wall -Wno-unused-function -Wno-strict-aliasing -Wno-pointer-to-int-cast -Ic_src"}, {"DRV_LDFLAGS", "$DRV_LDFLAGS -lm"}, {"EXE_CFLAGS", "$EXE_CFLAGS -Wall -Wno-unused-function -Wno-strict-aliasing -Wno-pointer-to-int-cast -Ic_src/ -I/usr/include"}, {"EXE_LDFLAGS", "$EXE_LDFLAGS -lm -lcheck"} ]}. {post_hooks, [ {eunit, "c_src/test/suite"} ]}. erlang-cherly-0.12.8+dfsg/src/000077500000000000000000000000001215305244000160405ustar00rootroot00000000000000erlang-cherly-0.12.8+dfsg/src/cherly.app.src000066400000000000000000000003251215305244000206160ustar00rootroot00000000000000{application, cherly, [{description, "Cherly caching library"}, {mod, {cherly_app, []}}, {vsn, "0.12.8"}, {modules, [cherly, cherly_app]}, {registered, []}, {applications, [kernel, stdlib]} ]}. erlang-cherly-0.12.8+dfsg/src/cherly.erl000066400000000000000000000043351215305244000200370ustar00rootroot00000000000000%%%------------------------------------------------------------------- %%% File: cherly.erl %%% @author Cliff Moon [] %%% @copyright 2009 Cliff Moon See LICENSE file %%% @doc %%% %%% @end %%% %%% @since 2009-02-22 by Cliff Moon %%% @since 2012-02-22 by Yoshiyuki Kanno %%%------------------------------------------------------------------- -module(cherly). -author('cliff@moonpolysoft.com'). -author('Yoshiyuki Kanno'). -export([start/1, put/3, get/2, remove/2, size/1, items/1, stop/1]). -on_load(init/0). %% @doc Initialize %% -spec(init() -> ok). init() -> SoName = case code:priv_dir(?MODULE) of {error, bad_name} -> case code:which(?MODULE) of Filename when is_list(Filename) -> filename:join([filename:dirname(Filename),"../priv", "cherly"]); _ -> filename:join("../priv", "cherly") end; Dir -> filename:join(Dir, "cherly") end, erlang:load_nif(SoName, 0). %% @doc Launch cherly %% -spec(start(integer()) -> {ok, any()}). start(_Size) -> exit(nif_library_not_loaded). %% @doc Insert an object into the cherly %% -spec(put(any(), binary(), binary()) -> ok | {error, any()}). put(_Res, _Key, _Value) -> exit(nif_library_not_loaded). %% @doc Retrieve an object from the cherly %% -spec(get(any(), binary()) -> {ok, binary()} | not_found | {error, any()}). get(_Res, _Key) -> exit(nif_library_not_loaded). %% @doc Remove an object from the cherly %% -spec(remove(any(), binary()) -> ok | {error, any()}). remove(_Res, _Key) -> exit(nif_library_not_loaded). %% @doc Retrieve size of cached objects %% -spec(size(any()) -> {ok, integer()} | {error, any()}). size(_Res) -> exit(nif_library_not_loaded). %% @doc Retrieve total of cached objects %% -spec(items(any()) -> {ok, integer()} | {error, any()}). items(_Res) -> exit(nif_library_not_loaded). %% @doc Halt the cherly %% -spec(stop(any()) -> ok | {error, any()}). stop(_Res) -> exit(nif_library_not_loaded). erlang-cherly-0.12.8+dfsg/src/cherly_app.erl000066400000000000000000000033011215305244000206670ustar00rootroot00000000000000%%%------------------------------------------------------------------- %%% File: cherly_app.erl %%% @author Cliff Moon [] %%% @copyright 2009 Cliff Moon %%% @doc %%% %%% @end %%% %%% @since 2009-03-11 by Cliff Moon %%%------------------------------------------------------------------- -module(cherly_app). -author('cliff@powerset.com'). -behaviour(application). %% Application callbacks -export([start/2, stop/1]). %%==================================================================== %% Application callbacks %%==================================================================== %%-------------------------------------------------------------------- %% @spec start(Type, StartArgs) -> {ok, Pid} | %% {ok, Pid, State} | %% {error, Reason} %% @doc This function is called whenever an application %% is started using application:start/1,2, and should start the processes %% of the application. If the application is structured according to the %% OTP design principles as a supervision tree, this means starting the %% top supervisor of the tree. %% @end %%-------------------------------------------------------------------- start(_Type, []) -> cherly_sup:start_link(). %%-------------------------------------------------------------------- %% @spec stop(State) -> void() %% @doc This function is called whenever an application %% has stopped. It is intended to be the opposite of Module:start/2 and %% should do any necessary cleaning up. The return value is ignored. %% @end %%-------------------------------------------------------------------- stop({_, C}) -> cherly_sup:stop(), exit(C, shutdown), ok. erlang-cherly-0.12.8+dfsg/src/cherly_server.erl000066400000000000000000000201751215305244000214250ustar00rootroot00000000000000%%====================================================================== %% %% Cherly %% %% Copyright (c) 2012 Rakuten, Inc. %% %% This file is provided to you under the Apache License, %% Version 2.0 (the "License"); you may not use this file %% except in compliance with the License. You may obtain %% a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, %% software distributed under the License is distributed on an %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY %% KIND, either express or implied. See the License for the %% specific language governing permissions and limitations %% under the License. %% %% --------------------------------------------------------------------- %% Cherly Server %% @doc %% @end %%====================================================================== -module(cherly_server). -author("Yosuke Hara"). -behaviour(gen_server). -include("cherly.hrl"). -include_lib("eunit/include/eunit.hrl"). %% API -export([start_link/2, stop/1, get/2, put/3, delete/2, stats/1, items/1, size/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -record(state, {handler, total_cache_size = 0 :: integer(), stats_gets = 0 :: integer(), stats_puts = 0 :: integer(), stats_dels = 0 :: integer(), stats_hits = 0 :: integer() }). %%-------------------------------------------------------------------- %% API %%-------------------------------------------------------------------- %% Function: {ok,Pid} | ignore | {error, Error} %% Description: Starts the server. start_link(Id, CacheSize) -> gen_server:start_link({local, Id}, ?MODULE, [CacheSize], []). %% Function: -> ok %% Description: Manually stops the server. stop(Pid) -> gen_server:cast(Pid, stop). %% @doc Retrieve a value associated with a specified key %% -spec(get(atom(), binary()) -> undefined | binary() | {error, any()}). get(Id, Key) -> gen_server:call(Id, {get, Key}). %% @doc Insert a key-value pair into the cherly %% -spec(put(atom(), binary(), binary()) -> ok | {error, any()}). put(Id, Key, Value) -> gen_server:call(Id, {put, Key, Value}). %% @doc Remove a key-value pair by a specified key into the cherly -spec(delete(atom(), binary()) -> ok | {error, any()}). delete(Id, Key) -> gen_server:call(Id, {delete, Key}). %% @doc Return server's state -spec(stats(atom()) -> any()). stats(Id) -> gen_server:call(Id, {stats}). %% @doc Return server's items -spec(items(atom()) -> any()). items(Id) -> gen_server:call(Id, {items}). %% @doc Return server's summary of cache size -spec(size(atom()) -> any()). size(Id) -> gen_server:call(Id, {size}). %%==================================================================== %% GEN_SERVER CALLBACKS %%==================================================================== init([CacheSize]) -> {ok, Handler} = cherly:start(CacheSize), {ok, #state{total_cache_size = CacheSize, handler = Handler}}. handle_call({get, Key}, _From, #state{handler = Handler, stats_gets = Gets, stats_hits = Hits} = State) -> case catch cherly:get(Handler, Key) of {ok, Value} -> {reply, {ok, Value}, State#state{stats_gets = Gets + 1, stats_hits = Hits + 1}}; not_found -> {reply, not_found, State#state{stats_gets = Gets + 1}}; {error, Cause} -> error_logger:error_msg("~p,~p,~p,~p~n", [{module, ?MODULE_STRING}, {function, "handle_call/3"}, {line, ?LINE}, {body, Cause}]), {reply, {error, Cause}, State}; {'EXIT', Cause} -> error_logger:error_msg("~p,~p,~p,~p~n", [{module, ?MODULE_STRING}, {function, "handle_call/3"}, {line, ?LINE}, {body, Cause}]), {reply, {error, Cause}, State} end; handle_call({put, Key, Val}, _From, #state{handler = Handler, stats_puts = Puts} = State) -> case catch cherly:put(Handler, Key, Val) of ok -> {reply, ok, State#state{stats_puts = Puts + 1}}; {'EXIT', Cause} -> error_logger:error_msg("~p,~p,~p,~p~n", [{module, ?MODULE_STRING}, {function, "handle_call/3"}, {line, ?LINE}, {body, Cause}]), {reply, {error, Cause}, State}; {error, Cause} -> error_logger:error_msg("~p,~p,~p,~p~n", [{module, ?MODULE_STRING}, {function, "handle_call/3"}, {line, ?LINE}, {body, Cause}]), {reply, {error, Cause}, State} end; handle_call({delete, Key}, _From, State = #state{handler = Handler, stats_dels = Dels}) -> case catch cherly:remove(Handler, Key) of ok -> {reply, ok, State#state{stats_dels = Dels + 1}}; {'EXIT', Cause} -> error_logger:error_msg("~p,~p,~p,~p~n", [{module, ?MODULE_STRING}, {function, "handle_call/3"}, {line, ?LINE}, {body, Cause}]), {reply, {error, Cause}, State}; {error, Cause} -> error_logger:error_msg("~p,~p,~p,~p~n", [{module, ?MODULE_STRING}, {function, "handle_call/3"}, {line, ?LINE}, {body, Cause}]), {reply, {error, Cause}, State} end; handle_call({stats}, _From, State = #state{handler = Handler, stats_hits = Hits, stats_gets = Gets, stats_puts = Puts, stats_dels = Dels}) -> {ok, Items} = cherly:items(Handler), {ok, Size} = cherly:size(Handler), Stats = #cache_stats{hits = Hits, gets = Gets, puts = Puts, dels = Dels, records = Items, cached_size = Size}, {reply, {ok, Stats}, State}; handle_call({items}, _From, #state{handler = Handler} = State) -> Reply = cherly:items(Handler), {reply, Reply, State}; handle_call({size}, _From, #state{handler = Handler} = State) -> Reply = cherly:size(Handler), {reply, Reply, State}; handle_call(_Request, _From, State) -> {reply, undefined, State}. handle_cast(stop, State) -> {stop, normal, State}; handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. %% ---------------------------------------------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() %% Description: This function is called by a gen_server when it is about to terminate. When it returns, %% the gen_server terminates with Reason. The return value is ignored. %% ---------------------------------------------------------------------------------------------------------- terminate(_Reason, _State) -> terminated. %% ---------------------------------------------------------------------------------------------------------- %% Function: code_change(OldVsn, State, Extra) -> {ok, NewState} %% Description: Convert process state when code is changed. %% ---------------------------------------------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. erlang-cherly-0.12.8+dfsg/src/cherly_sup.erl000066400000000000000000000051021215305244000207170ustar00rootroot00000000000000%%====================================================================== %% %% Cherly %% %% Copyright (c) 2012 Rakuten, Inc. %% %% This file is provided to you under the Apache License, %% Version 2.0 (the "License"); you may not use this file %% except in compliance with the License. You may obtain %% a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, %% software distributed under the License is distributed on an %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY %% KIND, either express or implied. See the License for the %% specific language governing permissions and limitations %% under the License. %% %% --------------------------------------------------------------------- %% Cherly Supervisor %% @doc %% @end %%====================================================================== -module(cherly_sup). -author("Yosuke Hara"). -behaviour(supervisor). -include_lib("eunit/include/eunit.hrl"). %% External API -export([start_link/0, stop/0]). %% Callbacks -export([init/1]). -define(MAX_RESTART, 5). -define(MAX_TIME, 60). -define(SHUTDOWN_WAITING_TIME, 2000). -ifdef(TEST). -define(DEF_TOTA_CACHE_SIZE, 1024 * 1024). %% 1MB -else. -define(DEF_TOTA_CACHE_SIZE, 1024 * 1024 * 1024). %% 1GB -endif. %%----------------------------------------------------------------------- %% External API %%----------------------------------------------------------------------- %% @spec (Params) -> ok %% @doc start link. %% @end start_link() -> TotalCacheSize = case application:get_env(cherly, total_cache_size) of {ok, Value1} when is_integer(Value1) -> Value1; _ -> ?DEF_TOTA_CACHE_SIZE end, supervisor:start_link({local, ?MODULE}, ?MODULE, [TotalCacheSize]). %% @spec () -> ok | %% not_started %% @doc stop process. %% @end stop() -> case whereis(?MODULE) of Pid when is_pid(Pid) == true -> exit(Pid, shutdown), ok; _ -> not_started end. %% --------------------------------------------------------------------- %% Callbacks %% --------------------------------------------------------------------- %% @spec (Params) -> ok %% @doc stop process. %% @end %% @private init([TotalCacheSize]) -> {ok, {{one_for_one, ?MAX_RESTART, ?MAX_TIME}, [{cherly_server, {cherly_server, start_link, [TotalCacheSize]}, permanent, ?SHUTDOWN_WAITING_TIME, worker, [cherly_server]}]}}. erlang-cherly-0.12.8+dfsg/test/000077500000000000000000000000001215305244000162305ustar00rootroot00000000000000erlang-cherly-0.12.8+dfsg/test/basho_bench_driver_cherly.config000066400000000000000000000004301215305244000245700ustar00rootroot00000000000000{mode, max}. {duration, 10}. {concurrent, 1}. {driver, basho_bench_driver_cherly}. {key_generator, {int_to_bin,{uniform_int, 5000000}}}. {value_generator, {fixed_bin, 1024}}. {operations, [{get, 1}, {put, 1}]}. {code_paths, ["deps/cherly"]}. {cache_capacity, 1000000000}. erlang-cherly-0.12.8+dfsg/test/basho_bench_driver_cherly.erl000066400000000000000000000056371215305244000241230ustar00rootroot00000000000000%% ------------------------------------------------------------------- %% %% Cherly - Benchmarking Suite %% %% Copyright (c) 2012 Rakuten, Inc. %% %% This file is provided to you under the Apache License, %% Version 2.0 (the "License"); you may not use this file %% except in compliance with the License. You may obtain %% a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, %% software distributed under the License is distributed on an %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY %% KIND, either express or implied. See the License for the %% specific language governing permissions and limitations %% under the License. %% %% ------------------------------------------------------------------- -module(basho_bench_driver_cherly). -author("Yosuke Hara"). -export([new/1, run/4]). -record(state, {handler :: binary(), check_integrity :: boolean()}). %% ==================================================================== %% API %% ==================================================================== new(_Id) -> case code:which(cherly) of non_existing -> io:format("Cherly-benchmark requires cherly to be available on code path.\n"); _ -> void end, CacheCapacity = basho_bench_config:get( cache_capacity, 1073741824), %% default:1GB io:format("Cache capacity: ~w\n", [CacheCapacity]), CI = basho_bench_config:get( check_integrity, false), %% should be false when doing benchmark io:format("Check Integrity: ~p\n", [CI]), {ok, C} = cherly:start(CacheCapacity), {ok, #state{handler = C, check_integrity = CI}}. run(get, KeyGen, _ValueGen, #state{handler = C, check_integrity = CI} = State) -> Key = KeyGen(), case cherly:get(C, Key) of {ok, Value} -> case CI of true -> LocalMD5 = erlang:get(Key), RemoteMD5 = erlang:md5(Value), case RemoteMD5 =:= LocalMD5 of true -> {ok, State}; false -> {error, checksum_error} end; false -> {ok, State} end; not_found -> {ok, State}; {error, Reason} -> {error, Reason, State} end; run(put, KeyGen, ValueGen, #state{handler = C, check_integrity = CI} = State) -> Key = KeyGen(), Val = ValueGen(), case cherly:put(C, Key, Val) of ok -> case CI of true -> LocalMD5 = erlang:md5(Val), erlang:put(Key, LocalMD5); false -> void end, {ok, State}; {error, Reason} -> {error, Reason, State}; Other -> io:format("put unexpected result:~p \n", [Other]), {error, Other, State} end. erlang-cherly-0.12.8+dfsg/test/test_cherly.erl000066400000000000000000000175601215305244000212720ustar00rootroot00000000000000%%==================================================================== %% %% Cherly %% %% Copyright (c) 2012 Stoic, Inc. %% %% This file is provided to you under the Apache License, %% Version 2.0 (the "License"); you may not use this file %% except in compliance with the License. You may obtain %% a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, %% software distributed under the License is distributed on an %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY %% KIND, either express or implied. See the License for the %% specific language governing permissions and limitations %% under the License. %% %% ------------------------------------------------------------------- %% Cherly Unit Test %% @doc %% @end %%==================================================================== -module(test_cherly). -author('Yoshiyuki Kanno'). -author('Yosuke Hara'). -include("cherly.hrl"). -include_lib("eunit/include/eunit.hrl"). -export([succ/1, fast_acc/3, time_to_epoch_float/1]). %%-------------------------------------------------------------------- %% TEST FUNCTIONS %%-------------------------------------------------------------------- -ifdef(EUNIT). simple_test() -> {ok, C} = cherly:start(120), K = <<"key">>, V = <<"value">>, Len = byte_size(K) + byte_size(V), cherly:put(C, K, V), ?assertEqual({ok, V}, cherly:get(C, <<"key">>)), ?assertEqual({ok, Len}, cherly:size(C)), cherly:stop(C). put_plural_objects_test() -> {ok, C} = cherly:start(10000), Keys = ["A","B","C","D","E","F", "G","H","I","J","K","L", "M","N","O","P","Q","R", "S","T","U","V","W","X", "Y","Z","1","2","3","4", "5","6","7","8","9","0"], lists:foreach(fun(K) -> cherly:put(C, list_to_binary(K), <<"LEOFS">>) end, Keys), lists:foreach(fun(K) -> {ok, <<"LEOFS">>} = cherly:get(C, list_to_binary(K)) end, Keys), Items = length(Keys), Size = Items + (Items * 5), ?assertEqual({ok, Items}, cherly:items(C)), ?assertEqual({ok, Size}, cherly:size(C)), cherly:stop(C). put_term_key_test() -> {ok, C} = cherly:start(1000), K = term_to_binary({1234567890, "server/erlang"}), V = <<"LEOFS">>, Len = byte_size(K) + byte_size(V), ok = cherly:put(C, K, V), {ok, V} = cherly:get(C, K), ?assertEqual({ok, 1}, cherly:items(C)), ?assertEqual({ok, Len}, cherly:size(C)), cherly:stop(C). put_including_null_key_test() -> {ok, C} = cherly:start(1000), H = <<"abcdefghijklmnopqrstuvwxyz">>, T = <<0:64>>, K = <>, V = <<"LEOFS">>, Len = byte_size(K) + byte_size(V), ok = cherly:put(C, K, V), {ok, V} = cherly:get(C, K), ?assertEqual({ok, 1}, cherly:items(C)), ?assertEqual({ok, Len}, cherly:size(C)), cherly:stop(C). put_get_and_remove_test() -> {ok, C} = cherly:start(120), K = <<"key">>, V = <<"value">>, ?assertEqual(not_found, cherly:get(C, K)), cherly:put(C, K, V), ?assertEqual({ok, V}, cherly:get(C, K)), cherly:remove(C, K), ?assertEqual(not_found, cherly:get(C, K)), ?assertEqual({ok, 0}, cherly:size(C)), cherly:stop(C). put_with_lru_eject_test() -> {ok, C} = cherly:start(70), V = <<"value">>, lists:foldl(fun(_, Str) -> Mod = list_to_binary(succ(Str)), ?debugVal(Mod), cherly:put(C, Mod, V), binary_to_list(Mod) end, "abc", lists:seq(1, 10)), ?debugVal(cherly:size(C)), %% ?assertEqual({ok, 8}, cherly:items(C)), cherly:stop(C). what_goes_in_must_come_out_test() -> {ok, C} = cherly:start(120), K = <<"key">>, cherly:put(C, K, list_to_binary([<<"val1">>, <<"val2">>])), ?assertEqual({ok, list_to_binary([<<"val1">>, <<"val2">>])}, cherly:get(C, K)), cherly:stop(C). big_stuff_that_goes_in_must_come_out_test() -> {ok, C} = cherly:start(1048576), K = <<"key">>, V1 = <<0:524288>>, V2 = <<1:524288>>, cherly:put(C, K, list_to_binary([V1, V2])), {ok, Ret} = cherly:get(C, K), ?assertEqual(list_to_binary([V1,V2]), Ret), cherly:stop(C). put_one_thing_in_no_list_big_test() -> {ok, C} = cherly:start(1048576), K = <<"key">>, V = <<0:524288>>, cherly:put(C, K, V), ?assertEqual({ok, V}, cherly:get(C, K)), cherly:stop(C). put_one_thing_in_no_list_small_test() -> {ok, C} = cherly:start(1048576), K = <<"key">>, V = <<1:8>>, cherly:put(C, K, V), ?assertEqual({ok, V}, cherly:get(C, K)), cherly:stop(C). remove_nonexistant_test() -> {ok, C} = cherly:start(120), K = <<"key">>, cherly:remove(C, K), ?assertEqual(not_found, cherly:get(C, K)), cherly:stop(C). put_bigger_thing_than_1MB_test() -> {ok, C} = cherly:start(1024 * 1024 * 5), K = <<"key">>, V = crypto:rand_bytes(1024 * 1024 * 2), cherly:put(C, K, V), cherly:remove(C, K), ?assertEqual(not_found, cherly:get(C, K)), {ok, 0} = cherly:items(C), {ok, 0} = cherly:size(C), cherly:stop(C). double_get_test() -> %% outputv modifies the iovec with a skipsize. That's fucking rad {ok, C} = cherly:start(1123123), K = <<"aczup">>, V = list_to_binary([<<131,108,0,0,0,1,104,2,107,0,9,60,48,46,52,55,50,46,48, 62,99,49,46,50,51,54,53,51,49,53,54,49,57,53,57,56,55, 50,57,54,49,48,52,101,43,48,57,0,0,0,0,0,106>>, <<235,105,34,223,191,105,56,25,199,24,148,52,180,112, 198,246,56,150,15,175,56,34,38,120,99,41,59,53,204, 233,41,246,189,135,39,171,124,233,143,40,108,119,63, 130,237,8,121,35,97,121,172,20,149,241,129,191,2,211, 151,167,0,102,103,63,242,240,41,83,150,211,189,32,56, 65,217,241,234,237,58,216,34,245,253,153,140,190,186, 24,147,240,181,63,222,161,13,217,55,232,254,148>>]), cherly:put(C, K, V), ?assertEqual({ok, V}, cherly:get(C, K)), ?assertEqual({ok, V}, cherly:get(C, K)), cherly:stop(C). server_test() -> K = <<"KEY-1">>, V = <<"VALUE-1">>, ProcId = 'test_cherly', cherly_server:start_link(ProcId, (1024 * 1024)), ok = cherly_server:put(ProcId, K, V), {ok, V} = cherly_server:get(ProcId, K), {ok, 1} = cherly_server:items(ProcId), {ok, 12} = cherly_server:size(ProcId), {ok, Stats1} = cherly_server:stats(ProcId), ?assertEqual(#cache_stats{gets = 1, puts = 1, dels = 0, hits = 1, records = 1, cached_size = 12}, Stats1), ok = cherly_server:delete(ProcId, K), {ok, Stats2} = cherly_server:stats(ProcId), ?assertEqual(#cache_stats{gets = 1, puts = 1, dels = 1, hits = 1, records = 0, cached_size = 0}, Stats2), {ok, 0} = cherly_server:items(ProcId), {ok, 0} = cherly_server:size(ProcId), cherly_server:stop(ProcId), ok. %%-------------------------------------------------------------------- %% INNER FUNCTIONS %%-------------------------------------------------------------------- succ([]) -> []; succ(Str) -> succ_int(lists:reverse(Str), []). succ_int([Char|Str], Acc) -> if Char >= $z -> succ_int(Str, [$a|Acc]); true -> lists:reverse(lists:reverse([Char+1|Acc]) ++ Str) end. fast_acc(_, Acc, 0) -> Acc; fast_acc(Fun, Acc, N) -> fast_acc(Fun, Fun(Acc), N-1). time_to_epoch_float({Mega,Sec,Micro}) -> Mega * 1000000 + Sec + Micro / 1000000. -endif.