├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.markdown ├── c_src ├── alg.c ├── cherly.c ├── cherly.h ├── cherly_nif.c ├── common.h ├── double_link.c ├── double_link.h ├── hashmap.c ├── hashmap.h ├── lru.c ├── lru.h ├── runtime.c ├── runtime.h ├── slabs.c ├── slabs.h ├── test │ ├── check_cherly.c │ ├── check_double_link.c │ ├── check_lru.c │ ├── check_slabs.c │ └── suite.c └── type.h ├── include └── cherly.hrl ├── rebar ├── rebar.config ├── rebar.config.develop ├── src ├── cherly.app.src ├── cherly.erl ├── cherly_app.erl ├── cherly_server.erl └── cherly_sup.erl └── test ├── basho_bench_driver_cherly.config ├── basho_bench_driver_cherly.erl └── test_cherly.erl /.gitignore: -------------------------------------------------------------------------------- 1 | ebin 2 | priv/ 3 | *.o 4 | *.swp 5 | .eunit/ 6 | c_src/test/suite 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Cherly 3 | # 4 | language: erlang 5 | script: "make && make eunit" 6 | notifications: 7 | email: false 8 | otp_release: 9 | - R14B04 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | "THE BEER-WARE LICENSE" (Revision 42): 3 | Cliff Moon wrote this project. As long as you retain this notice you 4 | can do whatever you want with this stuff. If we meet some day, and you think 5 | this stuff is worth it, you can buy me a beer in return Cliff Moon. 6 | ---------------------------------------------------------------------------- 7 | 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: deps test 2 | 3 | REBAR := ./rebar 4 | all: 5 | @$(REBAR) update-deps 6 | @$(REBAR) get-deps 7 | @$(REBAR) compile 8 | @$(REBAR) xref skip_deps=true 9 | @$(REBAR) eunit skip_deps=true 10 | compile: 11 | @$(REBAR) compile skip_deps=true 12 | xref: 13 | @$(REBAR) xref skip_deps=true 14 | eunit: 15 | @$(REBAR) eunit skip_deps=true 16 | clean: 17 | @$(REBAR) clean skip_deps=true 18 | distclean: 19 | @$(REBAR) delete-deps 20 | @$(REBAR) clean 21 | qc: 22 | @$(REBAR) qc skip_deps=true 23 | 24 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Cherly 2 | ======= 3 | 4 | Cherly (sher-lee) was originally developed by Cliff Moon for erlang to deal with in memory caching based on LRU. 5 | Its functionality and performance were awesome, 6 | but as time goes on, its implementation gradually obsoletes and it's hard to maintain. 7 | To overcome these problems, I forked and made Cherly improve with original purposes. 8 | Main improvements are described below. 9 | 10 | * 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. 11 | 12 | * Implemented slab allocator on off-heap. 13 | 14 | * Replaced implemantations of port driver with NIFs. 15 | 16 | * Rebarized 17 | 18 | Dependencies 19 | ======= 20 | 21 | * Runtime 22 | * Erlang >= R14B 23 | * [Check](http://check.sourceforge.net/) for C Unit tests 24 | 25 | Installation 26 | ======== 27 | * Install `Check` from package systems 28 | * deb 29 | 30 | `sudo apt-get install check` 31 | 32 | * rpm 33 | 34 | `sudo yum install check` 35 | 36 | * "Cherly" uses the "rebar" build system. Makefile so that simply running "make" at the top level should work. 37 | * [rebar](https://github.com/basho/rebar) 38 | * "Cherly" requires Erlang R14B or later. 39 | -------------------------------------------------------------------------------- /c_src/alg.c: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include "runtime.h" 6 | #include "hashmap.h" 7 | #include "type.h" 8 | 9 | void 10 | runtime_memhash(uintptr *h, uintptr s, void *a) 11 | { 12 | byte *b; 13 | uintptr hash; 14 | 15 | b = a; 16 | if(sizeof(hash) == 4) 17 | hash = 2860486313U; 18 | else 19 | hash = 33054211828000289ULL; 20 | while(s > 0) { 21 | if(sizeof(hash) == 4) 22 | hash = (hash ^ *b) * 3267000013UL; 23 | else 24 | hash = (hash ^ *b) * 23344194077549503ULL; 25 | b++; 26 | s--; 27 | } 28 | *h ^= hash; 29 | } 30 | 31 | void 32 | runtime_memequal(bool *eq, uintptr s, void *a, void *b) 33 | { 34 | byte *ba, *bb, *aend; 35 | 36 | if(a == b) { 37 | *eq = 1; 38 | return; 39 | } 40 | ba = a; 41 | bb = b; 42 | aend = ba+s; 43 | while(ba != aend) { 44 | if(*ba != *bb) { 45 | *eq = 0; 46 | return; 47 | } 48 | ba++; 49 | bb++; 50 | } 51 | *eq = 1; 52 | return; 53 | } 54 | 55 | void 56 | runtime_strhash(uintptr *h, uintptr s, void *a) 57 | { 58 | runtime_memhash(h, ((String*)a)->len, ((String*)a)->str); 59 | } 60 | 61 | void 62 | runtime_strequal(bool *eq, uintptr s, void *a, void *b) 63 | { 64 | int32 alen; 65 | 66 | alen = ((String*)a)->len; 67 | if(alen != ((String*)b)->len) { 68 | *eq = false; 69 | return; 70 | } 71 | runtime_memequal(eq, alen, ((String*)a)->str, ((String*)b)->str); 72 | } 73 | 74 | void 75 | runtime_strcopy(uintptr s, void *a, void *b) 76 | { 77 | if(b == nil) { 78 | ((String*)a)->str = 0; 79 | ((String*)a)->len = 0; 80 | return; 81 | } 82 | ((String*)a)->str = ((String*)b)->str; 83 | ((String*)a)->len = ((String*)b)->len; 84 | } 85 | 86 | Alg StrAlg = { runtime_strhash, runtime_strequal, runtime_strcopy }; 87 | Type StrType = { sizeof(String), &StrAlg }; 88 | MapType StrMapType = { &StrType, &StrType }; 89 | -------------------------------------------------------------------------------- /c_src/cherly.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cherly.h" 4 | #include "common.h" 5 | 6 | static void cherly_eject_callback(cherly_t *cherly, char *key, int length); 7 | 8 | /** 9 | * Initialize LRU-Storage 10 | */ 11 | void cherly_init(cherly_t *cherly, int options, unsigned long long max_size) { 12 | cherly->hm = runtime_makemap_c(&StrMapType, max_size); 13 | memset(&cherly->slab, 0, sizeof(slabs_t)); 14 | slabs_init(&cherly->slab, max_size, 1.5, false); 15 | 16 | cherly->lru = lru_create(); 17 | cherly->size = 0; 18 | cherly->items_length = 0; 19 | cherly->max_size = max_size; 20 | } 21 | 22 | 23 | /** 24 | * Insert an object into LRU-Storage 25 | */ 26 | // node -> item -> value 27 | bool cherly_put(cherly_t *cherly, void *key, int length, void *value, int size, DestroyCallback destroy) { 28 | lru_item_t * item; 29 | String skey, sval; 30 | bool exists; 31 | 32 | // Prepare put-operation 33 | size_t bufsiz = sizeof(size_t) + length + 1 + size; 34 | void* buf = slabs_alloc(&cherly->slab, bufsiz); 35 | if (buf == NULL) { 36 | // retry 37 | cherly->size -= lru_eject_by_size(cherly->lru, 38 | SETTING_ITEM_SIZE_MAX, 39 | (EjectionCallback)cherly_eject_callback, cherly); 40 | buf = slabs_alloc(&cherly->slab, bufsiz); 41 | if (buf == NULL) return false; 42 | } 43 | *((size_t*)buf) = bufsiz; 44 | char* bufkey = (char*)((char*)buf + sizeof(size_t)); 45 | 46 | skey.str = (byte*)bufkey; 47 | skey.len = length; 48 | 49 | memcpy(bufkey, key, length); 50 | runtime_mapaccess(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval, &exists); 51 | 52 | if (exists) { 53 | item = (lru_item_t*)sval.str; 54 | cherly_remove(cherly, lru_item_key(item), lru_item_keylen(item)); 55 | } 56 | if (cherly->size + bufsiz > cherly->max_size) { 57 | cherly->size -= lru_eject_by_size(cherly->lru, 58 | (length + size) - (cherly->max_size - cherly->size), 59 | (EjectionCallback)cherly_eject_callback, cherly); 60 | } 61 | 62 | void* bufval = (void*)(bufkey + length + 1); 63 | memcpy(bufval, value, size); 64 | 65 | // Insert an object into lru-storage 66 | item = lru_insert(cherly->lru, bufkey, length, bufval, size, destroy); 67 | if (item == NULL) return false; 68 | 69 | // After put-operation 70 | sval.str = (byte*)item; 71 | runtime_mapassign(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval); 72 | 73 | cherly->size += lru_item_size(item); 74 | cherly->items_length++; 75 | return true; 76 | 77 | } 78 | 79 | 80 | /** 81 | * Retrieve an object from LRU-Storage 82 | */ 83 | void* cherly_get(cherly_t *cherly, void *key, int length, int* vallen) { 84 | lru_item_t * item; 85 | String skey, sval; 86 | bool exists; 87 | 88 | // Prepare get-operation 89 | skey.str = (byte*)key; 90 | skey.len = length; 91 | 92 | // Retrieve an object 93 | runtime_mapaccess(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval, &exists); 94 | 95 | if (!exists) { 96 | return nil; 97 | } else { 98 | item = (lru_item_t *)sval.str; 99 | lru_touch(cherly->lru, item); 100 | *vallen = lru_item_vallen(item); 101 | 102 | return lru_item_value(item); 103 | } 104 | } 105 | 106 | 107 | /** 108 | * Free a stored memory 109 | */ 110 | static inline void cherly_slab_free(slabs_t* slab, char* key) { 111 | size_t* psize = (size_t*)key; 112 | psize--; 113 | slabs_free(slab, (void*)psize, *psize); 114 | } 115 | 116 | 117 | /** 118 | * Callback 119 | */ 120 | static void cherly_eject_callback(cherly_t *cherly, char *key, int length) { 121 | lru_item_t *item; 122 | String skey, sval; 123 | bool exists; 124 | int32 ret; 125 | 126 | skey.str = (byte*)key; 127 | skey.len = length; 128 | runtime_mapaccess(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval, &exists); 129 | 130 | if (!exists) { 131 | return; 132 | } 133 | 134 | item = (lru_item_t*)sval.str; 135 | cherly_slab_free(&cherly->slab, lru_item_key(item)); 136 | ret = runtime_mapassign(&StrMapType, cherly->hm, (byte*)&skey, nil); 137 | 138 | if (ret) { 139 | cherly->items_length--; 140 | cherly->size -= lru_item_size(item); 141 | } 142 | } 143 | 144 | 145 | /** 146 | * Remove an object from LRU-Storage 147 | */ 148 | void cherly_remove(cherly_t *cherly, void *key, int length) { 149 | lru_item_t *item; 150 | String skey, sval; 151 | bool exists; 152 | 153 | 154 | skey.str = (byte*)key; 155 | skey.len = length; 156 | runtime_mapaccess(&StrMapType, cherly->hm, (byte*)&skey, (byte*)&sval, &exists); 157 | 158 | if (!exists) { 159 | return; 160 | } 161 | 162 | item = (lru_item_t *)sval.str; 163 | cherly_slab_free(&cherly->slab, lru_item_key(item)); 164 | 165 | lru_remove_and_destroy(cherly->lru, item); 166 | cherly->size -= lru_item_size(item); 167 | cherly->items_length--; 168 | 169 | runtime_mapassign(&StrMapType, cherly->hm, (byte*)&skey, nil); 170 | } 171 | 172 | 173 | /** 174 | * Destroy LRU-Storage 175 | */ 176 | void cherly_destroy(cherly_t *cherly) { 177 | runtime_mapdestroy(cherly->hm); 178 | lru_destroy(cherly->lru); 179 | } 180 | 181 | -------------------------------------------------------------------------------- /c_src/cherly.h: -------------------------------------------------------------------------------- 1 | #ifndef __CHERLY__ 2 | #define __CHERLY__ 3 | 4 | #include "runtime.h" 5 | #include "lru.h" 6 | #include "slabs.h" 7 | 8 | #define cherly_size(cherly) ((cherly)->size) 9 | #define cherly_items_length(cherly) ((cherly)->items_length) 10 | #define cherly_max_size(cherly) ((cherly)->max_size) 11 | 12 | typedef struct _cherly_t { 13 | Hmap* hm; 14 | slabs_t slab; 15 | lru_t *lru; 16 | unsigned long long size; 17 | unsigned long long items_length; 18 | unsigned long long max_size; 19 | } cherly_t; 20 | 21 | void cherly_init(cherly_t *cherly, int options, unsigned long long max_size); 22 | void * cherly_get(cherly_t *cherly, void * key, int length, int* vallen); 23 | bool cherly_put(cherly_t *cherly, void * key, int length, void *value, int size, DestroyCallback); 24 | void cherly_remove(cherly_t *cherly, void * key, int length); 25 | void cherly_destroy(cherly_t *cherly); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /c_src/cherly_nif.c: -------------------------------------------------------------------------------- 1 | #include "erl_nif.h" 2 | #include 3 | #include 4 | #include "cherly.h" 5 | #include "common.h" 6 | 7 | #define CHERLY_RES_TYPE "cherly_res" 8 | 9 | static ERL_NIF_TERM cherly_nif_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 10 | static ERL_NIF_TERM cherly_nif_stop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 11 | static ERL_NIF_TERM cherly_nif_get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 12 | static ERL_NIF_TERM cherly_nif_put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 13 | static ERL_NIF_TERM cherly_nif_remove(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 14 | static ERL_NIF_TERM cherly_nif_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 15 | static ERL_NIF_TERM cherly_nif_items(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 16 | 17 | static ErlNifFunc nif_funcs[] = 18 | { 19 | {"start", 1, cherly_nif_init}, 20 | {"stop", 1, cherly_nif_stop}, 21 | {"get" , 2, cherly_nif_get}, 22 | {"put" , 3, cherly_nif_put}, 23 | {"remove", 2, cherly_nif_remove}, 24 | {"size", 1, cherly_nif_size}, 25 | {"items" , 1, cherly_nif_items} 26 | }; 27 | 28 | static ERL_NIF_TERM atom_ok; 29 | static ERL_NIF_TERM atom_error; 30 | static ERL_NIF_TERM atom_oom; 31 | static ERL_NIF_TERM atom_not_found; 32 | 33 | 34 | /** 35 | * Initialize 36 | */ 37 | static ERL_NIF_TERM cherly_nif_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 38 | ErlNifUInt64 max_size; 39 | ERL_NIF_TERM term; 40 | ErlNifResourceType* pert; 41 | cherly_t* obj; 42 | 43 | if (argc < 1) { 44 | return enif_make_badarg(env); 45 | } 46 | 47 | if (!enif_get_uint64(env, argv[0], &max_size)) { 48 | return enif_make_badarg(env); 49 | } 50 | 51 | pert = (ErlNifResourceType*)enif_priv_data(env); 52 | obj = enif_alloc_resource(pert, sizeof(cherly_t)); 53 | 54 | term = enif_make_resource(env, obj); 55 | cherly_init(obj, 0, max_size); 56 | enif_release_resource(obj); 57 | 58 | return enif_make_tuple2(env, atom_ok, term); 59 | } 60 | 61 | 62 | /** 63 | * Stop 64 | */ 65 | static ERL_NIF_TERM cherly_nif_stop(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 66 | cherly_t *obj; 67 | ErlNifResourceType* pert; 68 | 69 | if (argc < 1) { 70 | return enif_make_badarg(env); 71 | } 72 | 73 | pert = (ErlNifResourceType*)enif_priv_data(env); 74 | 75 | if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { 76 | return enif_make_badarg(env); 77 | } 78 | 79 | cherly_destroy(obj); 80 | return atom_ok; 81 | } 82 | 83 | 84 | /** 85 | * Retrieve an object from LRU-Storage 86 | */ 87 | static ERL_NIF_TERM cherly_nif_get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 88 | cherly_t *obj; 89 | int vallen; 90 | void* value; 91 | 92 | ErlNifResourceType* pert; 93 | ErlNifBinary keybin; 94 | ErlNifBinary bin; 95 | 96 | if (argc < 2) { 97 | return enif_make_badarg(env); 98 | } 99 | 100 | pert = (ErlNifResourceType*)enif_priv_data(env); 101 | if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { 102 | return enif_make_badarg(env); 103 | } 104 | 105 | if (!enif_inspect_binary(env, argv[1], &keybin)) { 106 | return enif_make_badarg(env); 107 | } 108 | 109 | if (keybin.size <= 0) { 110 | return enif_make_badarg(env); 111 | } 112 | 113 | value = cherly_get(obj, keybin.data, keybin.size, &vallen); 114 | 115 | if (value == NULL) { 116 | return atom_not_found; 117 | } 118 | 119 | if (!enif_alloc_binary(vallen, &bin)) { 120 | return enif_make_badarg(env); 121 | } 122 | 123 | memcpy(bin.data, value, vallen); 124 | return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &bin)); 125 | } 126 | 127 | 128 | /** 129 | * Insert an object into LRU-Storage 130 | */ 131 | static ERL_NIF_TERM cherly_nif_put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 132 | cherly_t *obj; 133 | ErlNifResourceType* pert; 134 | ErlNifBinary keybin; 135 | ErlNifBinary bin; 136 | bool ret; 137 | 138 | if (argc < 3) { 139 | return enif_make_badarg(env); 140 | } 141 | 142 | pert = (ErlNifResourceType*)enif_priv_data(env); 143 | 144 | if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { 145 | return enif_make_badarg(env); 146 | } 147 | if (!enif_inspect_binary(env, argv[1], &keybin)) { 148 | return enif_make_badarg(env); 149 | } 150 | if (keybin.size <= 0) { 151 | return enif_make_badarg(env); 152 | } 153 | if (!enif_inspect_binary(env, argv[2], &bin)) { 154 | return enif_make_badarg(env); 155 | } 156 | 157 | ret = cherly_put(obj, keybin.data, keybin.size, bin.data, bin.size, NULL); 158 | return ret ? atom_ok : enif_make_tuple2(env, atom_error, atom_oom); 159 | } 160 | 161 | 162 | /** 163 | * Remove an object from LRU-Storage 164 | */ 165 | static ERL_NIF_TERM cherly_nif_remove(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 166 | cherly_t *obj; 167 | ErlNifResourceType* pert; 168 | ErlNifBinary keybin; 169 | 170 | if (argc < 2) { 171 | return enif_make_badarg(env); 172 | } 173 | 174 | pert = (ErlNifResourceType*)enif_priv_data(env); 175 | 176 | if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { 177 | return enif_make_badarg(env); 178 | } 179 | 180 | if (!enif_inspect_binary(env, argv[1], &keybin)) { 181 | return enif_make_badarg(env); 182 | } 183 | if (keybin.size <= 0) { 184 | return enif_make_badarg(env); 185 | } 186 | 187 | cherly_remove(obj, keybin.data, keybin.size); 188 | return atom_ok; 189 | } 190 | 191 | 192 | /** 193 | * Retrieve summary of size of stored objects 194 | */ 195 | static ERL_NIF_TERM cherly_nif_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 196 | cherly_t *obj; 197 | ErlNifResourceType* pert; 198 | ErlNifUInt64 size; 199 | 200 | if (argc < 1) { 201 | return enif_make_badarg(env); 202 | } 203 | 204 | pert = (ErlNifResourceType*)enif_priv_data(env); 205 | 206 | if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { 207 | return enif_make_badarg(env); 208 | } 209 | 210 | size = cherly_size(obj); 211 | return enif_make_tuple2(env, atom_ok, enif_make_uint64(env, size)); 212 | } 213 | 214 | 215 | /** 216 | * Retrieve total of objects 217 | */ 218 | static ERL_NIF_TERM cherly_nif_items(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 219 | cherly_t *obj; 220 | ErlNifResourceType* pert; 221 | ErlNifUInt64 len; 222 | 223 | if (argc < 1) { 224 | return enif_make_badarg(env); 225 | } 226 | 227 | pert = (ErlNifResourceType*)enif_priv_data(env); 228 | 229 | if (!enif_get_resource(env, argv[0], pert, (void**)&obj)) { 230 | return enif_make_badarg(env); 231 | } 232 | 233 | len = cherly_items_length(obj); 234 | return enif_make_tuple2(env, atom_ok, enif_make_uint64(env, len)); 235 | } 236 | 237 | 238 | /** 239 | * When calling onload or uggrade 240 | */ 241 | static int onload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { 242 | ErlNifResourceFlags erf = ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER; 243 | ErlNifResourceType* pert = enif_open_resource_type(env, NULL, CHERLY_RES_TYPE, NULL, erf, &erf); 244 | 245 | if (pert == NULL) { 246 | return 1; 247 | } 248 | 249 | *priv_data = (void*)pert; 250 | atom_ok = enif_make_atom(env, "ok"); 251 | atom_error = enif_make_atom(env, "error"); 252 | atom_oom = enif_make_atom(env, "out of memory"); 253 | atom_not_found = enif_make_atom(env, "not_found"); 254 | return 0; 255 | } 256 | 257 | /** 258 | * Onload 259 | */ 260 | int cherly_nif_onload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { 261 | return onload(env, priv_data, load_info); 262 | } 263 | 264 | 265 | /** 266 | * Upgrade 267 | */ 268 | int cherly_nif_upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) { 269 | return onload(env, priv_data, load_info); 270 | } 271 | 272 | 273 | ERL_NIF_INIT(cherly, nif_funcs, cherly_nif_onload, NULL, cherly_nif_upgrade, NULL) 274 | 275 | -------------------------------------------------------------------------------- /c_src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_H__ 2 | #define __COMMON_H__ 3 | 4 | #ifdef DEBUG 5 | #define dprintf(format, args...) printf (format , ## args) 6 | #else 7 | #define dprintf(format, args...) 8 | #endif 9 | 10 | #endif -------------------------------------------------------------------------------- /c_src/double_link.c: -------------------------------------------------------------------------------- 1 | #include "double_link.h" 2 | #include 3 | #include "common.h" 4 | 5 | d_list_t * d_list_create() { 6 | d_list_t * list = malloc(sizeof(d_list_t)); 7 | if (list == NULL) return NULL; 8 | list->head = NULL; 9 | list->tail = NULL; 10 | list->size = 0; 11 | return list; 12 | } 13 | 14 | void d_list_destroy(d_list_t *list) { 15 | d_node_t * node = list->head; 16 | d_node_t * tmp = NULL; 17 | while (NULL != node) { 18 | tmp = node->next; 19 | d_node_destroy(node); 20 | node = tmp; 21 | } 22 | free(list); 23 | } 24 | 25 | void d_list_push(d_list_t *list, d_node_t *node) { 26 | if (NULL == list->head && NULL == list->tail) { 27 | list->head = node; 28 | list->tail = node; 29 | node->previous = NULL; 30 | node->next = NULL; 31 | } else { 32 | node->previous = NULL; 33 | list->head->previous = node; 34 | node->next = list->head; 35 | list->head = node; 36 | } 37 | list->size++; 38 | } 39 | 40 | d_node_t * d_list_pop(d_list_t *list) { 41 | d_node_t * node = list->head; 42 | if (NULL == node) { 43 | return NULL; 44 | } 45 | 46 | list->head = node->next; 47 | list->size--; 48 | if (NULL != list->head) { 49 | list->head->previous = NULL; 50 | } else { 51 | list->tail = NULL; 52 | } 53 | return node; 54 | } 55 | 56 | void d_list_unshift(d_list_t *list, d_node_t *node) { 57 | if (NULL == list->head && NULL == list->tail) { 58 | list->head = node; 59 | list->tail = node; 60 | node->previous = NULL; 61 | node->next = NULL; 62 | } else { 63 | node->previous = list->tail; 64 | list->tail->next = node; 65 | node->next = NULL; 66 | list->tail = node; 67 | } 68 | list->size++; 69 | } 70 | 71 | d_node_t * d_list_shift(d_list_t *list) { 72 | d_node_t * node = list->tail; 73 | if (NULL == node) { 74 | return NULL; 75 | } 76 | 77 | list->tail = node->previous; 78 | list->size--; 79 | if (NULL != list->tail) { 80 | list->tail->next = NULL; 81 | } else { 82 | list->head = NULL; 83 | } 84 | return node; 85 | } 86 | 87 | void d_list_remove(d_list_t *list, d_node_t *node) { 88 | d_node_t *previous; 89 | d_node_t *next; 90 | 91 | if (list->head == node) { 92 | d_list_pop(list); 93 | return; 94 | } else if (list->tail == node) { 95 | d_list_shift(list); 96 | return; 97 | } 98 | 99 | previous = node->previous; 100 | next = node->next; 101 | 102 | node->previous = NULL; 103 | node->next = NULL; 104 | 105 | if (NULL != previous) { 106 | previous->next = next; 107 | } 108 | 109 | if (NULL != next) { 110 | next->previous = previous; 111 | } 112 | } 113 | 114 | d_node_t * d_node_create(void *data) { 115 | d_node_t * node; 116 | 117 | node = malloc(sizeof(d_node_t)); 118 | if (node == NULL) return NULL; 119 | node->previous = NULL; 120 | node->next = NULL; 121 | node->data = data; 122 | 123 | return node; 124 | } 125 | 126 | void d_node_destroy(d_node_t * node) { 127 | free(node); 128 | } 129 | -------------------------------------------------------------------------------- /c_src/double_link.h: -------------------------------------------------------------------------------- 1 | #ifndef __DOUBLE_LINK_H__ 2 | #define __DOUBLE_LINK_H__ 3 | 4 | typedef struct _d_node_t { 5 | struct _d_node_t * previous; 6 | struct _d_node_t * next; 7 | void * data; 8 | } d_node_t; 9 | 10 | typedef struct _d_list_t { 11 | d_node_t * head; 12 | d_node_t * tail; 13 | unsigned long size; 14 | } d_list_t; 15 | 16 | #define d_list_size(list) ((list)->size) 17 | 18 | 19 | d_list_t * d_list_create(); 20 | void d_list_destroy(d_list_t * list); 21 | void d_list_push(d_list_t *list, d_node_t *node); 22 | d_node_t* d_list_pop(d_list_t *list); 23 | void d_list_unshift(d_list_t *list, d_node_t *node); 24 | d_node_t* d_list_shift(d_list_t *list); 25 | void d_list_remove(d_list_t *list, d_node_t *node); 26 | 27 | d_node_t * d_node_create(void * data); 28 | void d_node_destroy(d_node_t* node); 29 | #endif -------------------------------------------------------------------------------- /c_src/hashmap.c: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "runtime.h" 10 | #include "hashmap.h" 11 | #include "type.h" 12 | 13 | struct Hmap { /* a hash table; initialize with hash_init() */ 14 | uint32 count; /* elements in table - must be first */ 15 | uint8 datasize; /* amount of data to store in entry */ 16 | uint8 max_power; /* max power of 2 to create sub-tables */ 17 | uint8 indirectval; /* storing pointers to values */ 18 | uint8 valoff; /* offset of value in key+value data block */ 19 | int32 changes; /* inc'ed whenever a subtable is created/grown */ 20 | struct hash_subtable *st; /* first-level table */ 21 | }; 22 | 23 | struct hash_entry { 24 | hash_hash_t hash; /* hash value of data */ 25 | byte data[1]; /* user data has "datasize" bytes */ 26 | }; 27 | 28 | struct hash_subtable { 29 | uint8 power; /* bits used to index this table */ 30 | uint8 used; /* bits in hash used before reaching this table */ 31 | uint8 datasize; /* bytes of client data in an entry */ 32 | uint8 max_probes; /* max number of probes when searching */ 33 | int16 limit_bytes; /* max_probes * (datasize+sizeof (hash_hash_t)) */ 34 | struct hash_entry *last; /* points to last element of entry[] */ 35 | struct hash_entry entry[1]; /* 2**power+max_probes-1 elements of elemsize bytes */ 36 | }; 37 | 38 | #define HASH_DATA_EQ(eq, t, h,x,y) ((eq)=0, (*t->key->alg->equal) (&(eq), t->key->size, (x), (y)), (eq)) 39 | 40 | #define HASH_REHASH 0x2 /* an internal flag */ 41 | /* the number of bits used is stored in the flags word too */ 42 | #define HASH_USED(x) ((x) >> 2) 43 | #define HASH_MAKE_USED(x) ((x) << 2) 44 | 45 | #define HASH_LOW 6 46 | #define HASH_ONE (((hash_hash_t)1) << HASH_LOW) 47 | #define HASH_MASK (HASH_ONE - 1) 48 | #define HASH_ADJUST(x) (((x) < HASH_ONE) << HASH_LOW) 49 | 50 | #define HASH_BITS (sizeof (hash_hash_t) * 8) 51 | 52 | #define HASH_SUBHASH HASH_MASK 53 | #define HASH_NIL 0 54 | #define HASH_NIL_MEMSET 0 55 | 56 | #define HASH_OFFSET(base, byte_offset) \ 57 | ((struct hash_entry *) (((byte *) (base)) + (byte_offset))) 58 | 59 | #define HASH_MAX_PROBES 15 /* max entries to probe before rehashing */ 60 | 61 | /* return a hash layer with 2**power empty entries */ 62 | static struct hash_subtable * 63 | hash_subtable_new (Hmap *h, int32 power, int32 used) 64 | { 65 | int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); 66 | int32 bytes = elemsize << power; 67 | struct hash_subtable *st; 68 | int32 limit_bytes = HASH_MAX_PROBES * elemsize; 69 | int32 max_probes = HASH_MAX_PROBES; 70 | 71 | if (bytes < limit_bytes) { 72 | limit_bytes = bytes; 73 | max_probes = 1 << power; 74 | } 75 | bytes += limit_bytes - elemsize; 76 | st = malloc (offsetof (struct hash_subtable, entry[0]) + bytes); 77 | st->power = power; 78 | st->used = used; 79 | st->datasize = h->datasize; 80 | st->max_probes = max_probes; 81 | st->limit_bytes = limit_bytes; 82 | st->last = HASH_OFFSET (st->entry, bytes) - 1; 83 | memset (st->entry, HASH_NIL_MEMSET, bytes); 84 | return (st); 85 | } 86 | 87 | static void 88 | init_sizes (int64 hint, int32 *init_power, int32 *max_power) 89 | { 90 | int32 log = 0; 91 | int32 i; 92 | 93 | for (i = 32; i != 0; i >>= 1) { 94 | if ((hint >> (log + i)) != 0) { 95 | log += i; 96 | } 97 | } 98 | log += 1 + (((hint << 3) >> log) >= 11); /* round up for utilization */ 99 | if (log <= 14) { 100 | *init_power = log; 101 | } else { 102 | *init_power = 12; 103 | } 104 | *max_power = 12; 105 | } 106 | 107 | static void 108 | hash_init (Hmap *h, int32 datasize, int64 hint) 109 | { 110 | int32 init_power; 111 | int32 max_power; 112 | 113 | if(datasize < sizeof (void *)) 114 | datasize = sizeof (void *); 115 | datasize = runtime_rnd(datasize, sizeof (void *)); 116 | init_sizes (hint, &init_power, &max_power); 117 | h->datasize = datasize; 118 | h->max_power = max_power; 119 | assert (h->datasize == datasize); 120 | assert (h->max_power == max_power); 121 | assert (sizeof (void *) <= h->datasize || h->max_power == 255); 122 | h->count = 0; 123 | h->changes = 0; 124 | h->st = hash_subtable_new (h, init_power, 0); 125 | } 126 | 127 | static void 128 | hash_remove_n (struct hash_subtable *st, struct hash_entry *dst_e, int32 n) 129 | { 130 | int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); 131 | struct hash_entry *src_e = HASH_OFFSET (dst_e, n * elemsize); 132 | struct hash_entry *last_e = st->last; 133 | int32 shift = HASH_BITS - (st->power + st->used); 134 | int32 index_mask = (((hash_hash_t)1) << st->power) - 1; 135 | int32 dst_i = (((byte *) dst_e) - ((byte *) st->entry)) / elemsize; 136 | int32 src_i = dst_i + n; 137 | hash_hash_t hash; 138 | int32 skip; 139 | int32 bytes; 140 | 141 | while (dst_e != src_e) { 142 | if (src_e <= last_e) { 143 | struct hash_entry *cp_e = src_e; 144 | int32 save_dst_i = dst_i; 145 | while (cp_e <= last_e && (hash = cp_e->hash) != HASH_NIL && 146 | ((hash >> shift) & index_mask) <= dst_i) { 147 | cp_e = HASH_OFFSET (cp_e, elemsize); 148 | dst_i++; 149 | } 150 | bytes = ((byte *) cp_e) - (byte *) src_e; 151 | memmove (dst_e, src_e, bytes); 152 | dst_e = HASH_OFFSET (dst_e, bytes); 153 | src_e = cp_e; 154 | src_i += dst_i - save_dst_i; 155 | if (src_e <= last_e && (hash = src_e->hash) != HASH_NIL) { 156 | skip = ((hash >> shift) & index_mask) - dst_i; 157 | } else { 158 | skip = src_i - dst_i; 159 | } 160 | } else { 161 | skip = src_i - dst_i; 162 | } 163 | bytes = skip * elemsize; 164 | memset (dst_e, HASH_NIL_MEMSET, bytes); 165 | dst_e = HASH_OFFSET (dst_e, bytes); 166 | dst_i += skip; 167 | } 168 | } 169 | 170 | static int32 171 | hash_insert_internal (MapType*, struct hash_subtable **pst, int32 flags, hash_hash_t hash, 172 | Hmap *h, void *data, void **pres); 173 | 174 | static void 175 | hash_conv (MapType *t, Hmap *h, 176 | struct hash_subtable *st, int32 flags, 177 | hash_hash_t hash, 178 | struct hash_entry *e) 179 | { 180 | int32 new_flags = (flags + HASH_MAKE_USED (st->power)) | HASH_REHASH; 181 | int32 shift = HASH_BITS - HASH_USED (new_flags); 182 | hash_hash_t prefix_mask = (-(hash_hash_t)1) << shift; 183 | int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); 184 | void *dummy_result; 185 | struct hash_entry *de; 186 | int32 index_mask = (1 << st->power) - 1; 187 | hash_hash_t e_hash; 188 | struct hash_entry *pe = HASH_OFFSET (e, -elemsize); 189 | 190 | while (e != st->entry && (e_hash = pe->hash) != HASH_NIL && (e_hash & HASH_MASK) != HASH_SUBHASH) { 191 | e = pe; 192 | pe = HASH_OFFSET (pe, -elemsize); 193 | } 194 | 195 | de = e; 196 | while (e <= st->last && 197 | (e_hash = e->hash) != HASH_NIL && 198 | (e_hash & HASH_MASK) != HASH_SUBHASH) { 199 | struct hash_entry *target_e = HASH_OFFSET (st->entry, ((e_hash >> shift) & index_mask) * elemsize); 200 | struct hash_entry *ne = HASH_OFFSET (e, elemsize); 201 | hash_hash_t current = e_hash & prefix_mask; 202 | if (de < target_e) { 203 | memset (de, HASH_NIL_MEMSET, ((byte *) target_e) - (byte *) de); 204 | de = target_e; 205 | } 206 | if ((hash & prefix_mask) == current || 207 | (ne <= st->last && (e_hash = ne->hash) != HASH_NIL && 208 | (e_hash & prefix_mask) == current)) { 209 | struct hash_subtable *new_st = hash_subtable_new (h, 1, HASH_USED (new_flags)); 210 | int32 rc = hash_insert_internal (t, &new_st, new_flags, e->hash, h, e->data, &dummy_result); 211 | assert (rc == 0); 212 | memcpy(dummy_result, e->data, h->datasize); 213 | e = ne; 214 | while (e <= st->last && (e_hash = e->hash) != HASH_NIL && (e_hash & prefix_mask) == current) { 215 | assert ((e_hash & HASH_MASK) != HASH_SUBHASH); 216 | rc = hash_insert_internal (t, &new_st, new_flags, e_hash, h, e->data, &dummy_result); 217 | assert (rc == 0); 218 | memcpy(dummy_result, e->data, h->datasize); 219 | e = HASH_OFFSET (e, elemsize); 220 | } 221 | memset (de->data, HASH_NIL_MEMSET, h->datasize); 222 | *(struct hash_subtable **)de->data = new_st; 223 | de->hash = current | HASH_SUBHASH; 224 | } else { 225 | if (e != de) { 226 | memcpy (de, e, elemsize); 227 | } 228 | e = HASH_OFFSET (e, elemsize); 229 | } 230 | de = HASH_OFFSET (de, elemsize); 231 | } 232 | if (e != de) { 233 | hash_remove_n (st, de, (((byte *) e) - (byte *) de) / elemsize); 234 | } 235 | } 236 | 237 | static void 238 | hash_grow (MapType *t, Hmap *h, struct hash_subtable **pst, int32 flags) 239 | { 240 | struct hash_subtable *old_st = *pst; 241 | int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); 242 | *pst = hash_subtable_new (h, old_st->power + 1, HASH_USED (flags)); 243 | struct hash_entry *last_e = old_st->last; 244 | struct hash_entry *e; 245 | void *dummy_result; 246 | int32 used = 0; 247 | 248 | flags |= HASH_REHASH; 249 | for (e = old_st->entry; e <= last_e; e = HASH_OFFSET (e, elemsize)) { 250 | hash_hash_t hash = e->hash; 251 | if (hash != HASH_NIL) { 252 | int32 rc = hash_insert_internal (t, pst, flags, e->hash, h, e->data, &dummy_result); 253 | assert (rc == 0); 254 | memcpy(dummy_result, e->data, h->datasize); 255 | used++; 256 | } 257 | } 258 | free (old_st); 259 | } 260 | 261 | static int32 262 | hash_lookup (MapType *t, Hmap *h, void *data, void **pres) 263 | { 264 | int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); 265 | hash_hash_t hash; 266 | struct hash_subtable *st = h->st; 267 | int32 used = 0; 268 | hash_hash_t e_hash; 269 | struct hash_entry *e; 270 | struct hash_entry *end_e; 271 | bool eq; 272 | 273 | hash = 0; 274 | (*t->key->alg->hash) (&hash, t->key->size, data); 275 | hash &= ~HASH_MASK; 276 | hash += HASH_ADJUST (hash); 277 | for (;;) { 278 | int32 shift = HASH_BITS - (st->power + used); 279 | int32 index_mask = (1 << st->power) - 1; 280 | int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ 281 | 282 | e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ 283 | e_hash = e->hash; 284 | if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ 285 | break; 286 | } 287 | used += st->power; 288 | st = *(struct hash_subtable **)e->data; 289 | } 290 | end_e = HASH_OFFSET (e, st->limit_bytes); 291 | while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { 292 | e = HASH_OFFSET (e, elemsize); 293 | } 294 | while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { 295 | if (HASH_DATA_EQ (eq, t, h, data, e->data)) { /* a match */ 296 | *pres = e->data; 297 | return (1); 298 | } 299 | e = HASH_OFFSET (e, elemsize); 300 | } 301 | *pres = 0; 302 | return (0); 303 | } 304 | 305 | static int32 306 | hash_remove (MapType *t, Hmap *h, void *data) 307 | { 308 | int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); 309 | hash_hash_t hash; 310 | struct hash_subtable *st = h->st; 311 | int32 used = 0; 312 | hash_hash_t e_hash; 313 | struct hash_entry *e; 314 | struct hash_entry *end_e; 315 | bool eq; 316 | 317 | hash = 0; 318 | (*t->key->alg->hash) (&hash, t->key->size, data); 319 | hash &= ~HASH_MASK; 320 | hash += HASH_ADJUST (hash); 321 | for (;;) { 322 | int32 shift = HASH_BITS - (st->power + used); 323 | int32 index_mask = (1 << st->power) - 1; 324 | int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ 325 | 326 | e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ 327 | e_hash = e->hash; 328 | if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ 329 | break; 330 | } 331 | used += st->power; 332 | st = *(struct hash_subtable **)e->data; 333 | } 334 | end_e = HASH_OFFSET (e, st->limit_bytes); 335 | while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { 336 | e = HASH_OFFSET (e, elemsize); 337 | } 338 | while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { 339 | if (HASH_DATA_EQ (eq, t, h, data, e->data)) { /* a match */ 340 | if (h->indirectval) 341 | free (*(void**)((byte*)e->data + h->valoff)); 342 | hash_remove_n (st, e, 1); 343 | h->count--; 344 | return (1); 345 | } 346 | e = HASH_OFFSET (e, elemsize); 347 | } 348 | return (0); 349 | } 350 | 351 | static int32 352 | hash_insert_internal (MapType *t, struct hash_subtable **pst, int32 flags, hash_hash_t hash, 353 | Hmap *h, void *data, void **pres) 354 | { 355 | int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); 356 | bool eq; 357 | 358 | if ((flags & HASH_REHASH) == 0) { 359 | hash += HASH_ADJUST (hash); 360 | hash &= ~HASH_MASK; 361 | } 362 | for (;;) { 363 | struct hash_subtable *st = *pst; 364 | int32 shift = HASH_BITS - (st->power + HASH_USED (flags)); 365 | int32 index_mask = (1 << st->power) - 1; 366 | int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ 367 | struct hash_entry *start_e = 368 | HASH_OFFSET (st->entry, i * elemsize); /* start_e is the pointer to element i */ 369 | struct hash_entry *e = start_e; /* e is going to range over [start_e, end_e) */ 370 | struct hash_entry *end_e; 371 | hash_hash_t e_hash = e->hash; 372 | 373 | if ((e_hash & HASH_MASK) == HASH_SUBHASH) { /* a subtable */ 374 | pst = (struct hash_subtable **) e->data; 375 | flags += HASH_MAKE_USED (st->power); 376 | continue; 377 | } 378 | end_e = HASH_OFFSET (start_e, st->limit_bytes); 379 | while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { 380 | e = HASH_OFFSET (e, elemsize); 381 | i++; 382 | } 383 | if (e != end_e && e_hash != HASH_NIL) { 384 | /* ins_e ranges over the elements that may match */ 385 | struct hash_entry *ins_e = e; 386 | int32 ins_i = i; 387 | hash_hash_t ins_e_hash; 388 | while (ins_e != end_e && ((e_hash = ins_e->hash) ^ hash) < HASH_SUBHASH) { 389 | if (HASH_DATA_EQ (eq, t, h, data, ins_e->data)) { /* a match */ 390 | *pres = ins_e->data; 391 | return (1); 392 | } 393 | assert (e_hash != hash || (flags & HASH_REHASH) == 0); 394 | hash += (e_hash == hash); /* adjust hash if it collides */ 395 | ins_e = HASH_OFFSET (ins_e, elemsize); 396 | ins_i++; 397 | if (e_hash <= hash) { /* set e to insertion point */ 398 | e = ins_e; 399 | i = ins_i; 400 | } 401 | } 402 | /* set ins_e to the insertion point for the new element */ 403 | ins_e = e; 404 | ins_i = i; 405 | ins_e_hash = 0; 406 | /* move ins_e to point at the end of the contiguous block, but 407 | stop if any element can't be moved by one up */ 408 | while (ins_e <= st->last && (ins_e_hash = ins_e->hash) != HASH_NIL && 409 | ins_i + 1 - ((ins_e_hash >> shift) & index_mask) < st->max_probes && 410 | (ins_e_hash & HASH_MASK) != HASH_SUBHASH) { 411 | ins_e = HASH_OFFSET (ins_e, elemsize); 412 | ins_i++; 413 | } 414 | if (e == end_e || ins_e > st->last || ins_e_hash != HASH_NIL) { 415 | e = end_e; /* can't insert; must grow or convert to subtable */ 416 | } else { /* make space for element */ 417 | memmove (HASH_OFFSET (e, elemsize), e, ((byte *) ins_e) - (byte *) e); 418 | } 419 | } 420 | if (e != end_e) { 421 | e->hash = hash; 422 | *pres = e->data; 423 | return (0); 424 | } 425 | h->changes++; 426 | if (st->power < h->max_power) { 427 | hash_grow (t, h, pst, flags); 428 | } else { 429 | hash_conv (t, h, st, flags, hash, start_e); 430 | } 431 | } 432 | } 433 | 434 | static int32 435 | hash_insert (MapType *t, Hmap *h, void *data, void **pres) 436 | { 437 | uintptr hash; 438 | int32 rc; 439 | 440 | hash = 0; 441 | (*t->key->alg->hash) (&hash, t->key->size, data); 442 | rc = hash_insert_internal (t, &h->st, 0, hash, h, data, pres); 443 | 444 | h->count += (rc == 0); /* increment count if element didn't previously exist */ 445 | return (rc); 446 | } 447 | 448 | static uint32 449 | hash_count (Hmap *h) 450 | { 451 | return (h->count); 452 | } 453 | 454 | static void 455 | iter_restart (struct hash_iter *it, struct hash_subtable *st, int32 used) 456 | { 457 | int32 elemsize = it->elemsize; 458 | hash_hash_t last_hash = it->last_hash; 459 | struct hash_entry *e; 460 | hash_hash_t e_hash; 461 | struct hash_iter_sub *sub = &it->subtable_state[it->i]; 462 | struct hash_entry *last; 463 | 464 | for (;;) { 465 | int32 shift = HASH_BITS - (st->power + used); 466 | int32 index_mask = (1 << st->power) - 1; 467 | int32 i = (last_hash >> shift) & index_mask; 468 | 469 | last = st->last; 470 | e = HASH_OFFSET (st->entry, i * elemsize); 471 | sub->start = st->entry; 472 | sub->last = last; 473 | 474 | if ((e->hash & HASH_MASK) != HASH_SUBHASH) { 475 | break; 476 | } 477 | sub->e = HASH_OFFSET (e, elemsize); 478 | sub = &it->subtable_state[++(it->i)]; 479 | used += st->power; 480 | st = *(struct hash_subtable **)e->data; 481 | } 482 | while (e <= last && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { 483 | e = HASH_OFFSET (e, elemsize); 484 | } 485 | sub->e = e; 486 | } 487 | 488 | static void * 489 | hash_next (struct hash_iter *it) 490 | { 491 | int32 elemsize; 492 | struct hash_iter_sub *sub; 493 | struct hash_entry *e; 494 | struct hash_entry *last; 495 | hash_hash_t e_hash; 496 | 497 | if (it->changes != it->h->changes) { /* hash table's structure changed; recompute */ 498 | if (~it->last_hash == 0) 499 | return (0); 500 | it->changes = it->h->changes; 501 | it->i = 0; 502 | iter_restart (it, it->h->st, 0); 503 | } 504 | elemsize = it->elemsize; 505 | 506 | Again: 507 | e_hash = 0; 508 | sub = &it->subtable_state[it->i]; 509 | e = sub->e; 510 | last = sub->last; 511 | 512 | if (e != sub->start && it->last_hash != HASH_OFFSET (e, -elemsize)->hash) { 513 | struct hash_entry *start = HASH_OFFSET (e, -(elemsize * HASH_MAX_PROBES)); 514 | struct hash_entry *pe = HASH_OFFSET (e, -elemsize); 515 | hash_hash_t last_hash = it->last_hash; 516 | if (start < sub->start) { 517 | start = sub->start; 518 | } 519 | while (e != start && ((e_hash = pe->hash) == HASH_NIL || last_hash < e_hash)) { 520 | e = pe; 521 | pe = HASH_OFFSET (pe, -elemsize); 522 | } 523 | while (e <= last && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { 524 | e = HASH_OFFSET (e, elemsize); 525 | } 526 | } 527 | 528 | for (;;) { 529 | while (e <= last && (e_hash = e->hash) == HASH_NIL) { 530 | e = HASH_OFFSET (e, elemsize); 531 | } 532 | if (e > last) { 533 | if (it->i == 0) { 534 | if(!it->cycled) { 535 | // Wrap to zero and iterate up until it->cycle. 536 | it->cycled = true; 537 | it->last_hash = 0; 538 | it->subtable_state[0].e = it->h->st->entry; 539 | it->subtable_state[0].start = it->h->st->entry; 540 | it->subtable_state[0].last = it->h->st->last; 541 | goto Again; 542 | } 543 | // Set last_hash to impossible value and 544 | // break it->changes, so that check at top of 545 | // hash_next will be used if we get called again. 546 | it->last_hash = ~(hash_hash_t)0; 547 | it->changes--; 548 | return (0); 549 | } else { 550 | it->i--; 551 | sub = &it->subtable_state[it->i]; 552 | e = sub->e; 553 | last = sub->last; 554 | } 555 | } else if ((e_hash & HASH_MASK) != HASH_SUBHASH) { 556 | if(it->cycled && e->hash > it->cycle) { 557 | // Already returned this. 558 | // Set last_hash to impossible value and 559 | // break it->changes, so that check at top of 560 | // hash_next will be used if we get called again. 561 | it->last_hash = ~(hash_hash_t)0; 562 | it->changes--; 563 | return (0); 564 | } 565 | it->last_hash = e->hash; 566 | sub->e = HASH_OFFSET (e, elemsize); 567 | return (e->data); 568 | } else { 569 | struct hash_subtable *st = 570 | *(struct hash_subtable **)e->data; 571 | sub->e = HASH_OFFSET (e, elemsize); 572 | it->i++; 573 | assert (it->i < sizeof (it->subtable_state) / 574 | sizeof (it->subtable_state[0])); 575 | sub = &it->subtable_state[it->i]; 576 | sub->e = e = st->entry; 577 | sub->start = st->entry; 578 | sub->last = last = st->last; 579 | } 580 | } 581 | } 582 | 583 | static void 584 | hash_iter_init (MapType *t, Hmap *h, struct hash_iter *it) 585 | { 586 | it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]); 587 | it->changes = h->changes; 588 | it->i = 0; 589 | it->h = h; 590 | it->t = t; 591 | it->last_hash = 0; 592 | it->subtable_state[0].e = h->st->entry; 593 | it->subtable_state[0].start = h->st->entry; 594 | it->subtable_state[0].last = h->st->last; 595 | 596 | // fastrand1 returns 31 useful bits. 597 | // We don't care about not having a bottom bit but we 598 | // do want top bits. 599 | if(sizeof(void*) == 8) 600 | it->cycle = (uint64)runtime_fastrand1()<<33 | (uint64)runtime_fastrand1()<<2; 601 | else 602 | it->cycle = runtime_fastrand1()<<1; 603 | it->cycled = false; 604 | it->last_hash = it->cycle; 605 | iter_restart(it, it->h->st, 0); 606 | } 607 | 608 | static void 609 | clean_st (struct hash_subtable *st, int32 *slots, int32 *used) 610 | { 611 | int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); 612 | struct hash_entry *e = st->entry; 613 | struct hash_entry *last = st->last; 614 | int32 lslots = (((byte *) (last+1)) - (byte *) e) / elemsize; 615 | int32 lused = 0; 616 | 617 | while (e <= last) { 618 | hash_hash_t hash = e->hash; 619 | if ((hash & HASH_MASK) == HASH_SUBHASH) { 620 | clean_st (*(struct hash_subtable **)e->data, slots, used); 621 | } else { 622 | lused += (hash != HASH_NIL); 623 | } 624 | e = HASH_OFFSET (e, elemsize); 625 | } 626 | free (st); 627 | *slots += lslots; 628 | *used += lused; 629 | } 630 | 631 | void 632 | runtime_mapdestroy (Hmap *h) 633 | { 634 | int32 slots = 0; 635 | int32 used = 0; 636 | 637 | clean_st (h->st, &slots, &used); 638 | free (h); 639 | } 640 | 641 | static void 642 | hash_visit_internal (struct hash_subtable *st, 643 | int32 used, int32 level, 644 | void (*data_visit) (void *arg, int32 level, void *data), 645 | void *arg) 646 | { 647 | int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); 648 | struct hash_entry *e = st->entry; 649 | int32 shift = HASH_BITS - (used + st->power); 650 | int32 i = 0; 651 | 652 | while (e <= st->last) { 653 | int32 index = ((e->hash >> (shift - 1)) >> 1) & ((1 << st->power) - 1); 654 | if ((e->hash & HASH_MASK) == HASH_SUBHASH) { 655 | (*data_visit) (arg, level, e->data); 656 | hash_visit_internal (*(struct hash_subtable **)e->data, 657 | used + st->power, level + 1, data_visit, arg); 658 | } else { 659 | (*data_visit) (arg, level, e->data); 660 | } 661 | if (e->hash != HASH_NIL) { 662 | assert (i < index + st->max_probes); 663 | assert (index <= i); 664 | } 665 | e = HASH_OFFSET (e, elemsize); 666 | i++; 667 | } 668 | } 669 | 670 | void 671 | hash_visit (Hmap *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg) 672 | { 673 | hash_visit_internal (h->st, 0, 0, data_visit, arg); 674 | } 675 | 676 | // 677 | /// interfaces to go runtime 678 | // 679 | 680 | // hash requires < 256 bytes of data (key+value) stored inline. 681 | // Only basic types can be key - biggest is complex128 (16 bytes). 682 | // Leave some room to grow, just in case. 683 | enum { 684 | MaxValsize = 256 - 64 685 | }; 686 | 687 | static void** 688 | hash_indirect(Hmap *h, void *p) 689 | { 690 | if(h->indirectval) 691 | p = *(void**)p; 692 | return p; 693 | } 694 | 695 | static int32 debug = 0; 696 | 697 | // makemap(typ *Type, hint uint32) (hmap *map[any]any); 698 | Hmap* 699 | runtime_makemap_c(MapType *typ, int64 hint) 700 | { 701 | Hmap *h; 702 | int32 valsize_in_hash; 703 | Type *key, *val; 704 | 705 | key = typ->key; 706 | val = typ->elem; 707 | 708 | h = malloc(sizeof(*h)); 709 | 710 | valsize_in_hash = val->size; 711 | if (val->size > MaxValsize) { 712 | h->indirectval = 1; 713 | valsize_in_hash = sizeof(void*); 714 | } 715 | 716 | // Align value inside data so that mark-sweep gc can find it. 717 | h->valoff = key->size; 718 | if(valsize_in_hash >= sizeof(void*)) 719 | h->valoff = runtime_rnd(key->size, sizeof(void*)); 720 | 721 | hash_init(h, h->valoff+valsize_in_hash, hint); 722 | 723 | // these calculations are compiler dependent. 724 | // figure out offsets of map call arguments. 725 | /* 726 | if(debug) { 727 | printf("makemap: map=%p; keysize=%lu; valsize=%lu; keyalg=%p; valalg=%p\n", 728 | h, key->size, val->size, key->alg, val->alg); 729 | } 730 | */ 731 | return h; 732 | } 733 | 734 | void 735 | runtime_mapaccess(MapType *t, Hmap *h, byte *ak, byte *av, bool *pres) 736 | { 737 | byte *res; 738 | Type *elem; 739 | 740 | elem = t->elem; 741 | if(h == nil) { 742 | elem->alg->copy(elem->size, av, nil); 743 | *pres = false; 744 | return; 745 | } 746 | 747 | res = nil; 748 | if(hash_lookup(t, h, ak, (void**)&res)) { 749 | *pres = true; 750 | elem->alg->copy(elem->size, av, hash_indirect(h, res+h->valoff)); 751 | } else { 752 | *pres = false; 753 | elem->alg->copy(elem->size, av, nil); 754 | } 755 | } 756 | 757 | int32 758 | runtime_mapassign(MapType *t, Hmap *h, byte *ak, byte *av) 759 | { 760 | byte *res; 761 | int32 hit; 762 | 763 | if(av == nil) { 764 | hit = hash_remove(t, h, ak); 765 | if(debug) { 766 | printf("mapassign: map=%p key=%s",(void*)h , ((String*)ak)->str); 767 | } 768 | return hit; 769 | } 770 | 771 | res = nil; 772 | hit = hash_insert(t, h, ak, (void**)&res); 773 | if(!hit && h->indirectval) 774 | *(void**)(res+h->valoff) = malloc(t->elem->size); 775 | t->key->alg->copy(t->key->size, res, ak); 776 | t->elem->alg->copy(t->elem->size, hash_indirect(h, res+h->valoff), av); 777 | 778 | if(debug) { 779 | printf("mapassign: map=%p key=%s val=%s",(void*)h, ((String*)ak)->str, ((String*)av)->str); 780 | } 781 | return hit; 782 | } 783 | 784 | // mapiterinit(mapType *type, hmap *map[any]any, hiter *any); 785 | void 786 | runtime_mapiterinit(MapType *t, Hmap *h, struct hash_iter *it) 787 | { 788 | if(h == nil) { 789 | it->data = nil; 790 | return; 791 | } 792 | hash_iter_init(t, h, it); 793 | it->data = hash_next(it); 794 | } 795 | 796 | // mapiternext(hiter *any); 797 | void 798 | runtime_mapiternext(struct hash_iter *it) 799 | { 800 | it->data = hash_next(it); 801 | } 802 | 803 | bool 804 | runtime_mapiterkey(struct hash_iter *it, void *ak) 805 | { 806 | byte *res; 807 | Type *key; 808 | 809 | res = it->data; 810 | if(res == nil) 811 | return false; 812 | key = it->t->key; 813 | key->alg->copy(key->size, ak, res); 814 | return true; 815 | } 816 | 817 | void 818 | runtime_mapiterkeyvalue(struct hash_iter *it, void *ak, void *av) 819 | { 820 | Hmap *h; 821 | byte *res; 822 | MapType *t; 823 | 824 | t = it->t; 825 | 826 | res = it->data; 827 | 828 | h = it->h; 829 | t->key->alg->copy(t->key->size, ak, res); 830 | t->elem->alg->copy(t->elem->size, av, hash_indirect(h, res+h->valoff)); 831 | 832 | } 833 | -------------------------------------------------------------------------------- /c_src/hashmap.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #ifndef __HASHMAP__ 6 | #define __HASHMAP__ 7 | 8 | #include "runtime.h" 9 | 10 | /* A hash table. 11 | Example, hashing nul-terminated char*s: 12 | hash_hash_t str_hash (void *v) { 13 | char *s; 14 | hash_hash_t hash = 0; 15 | for (s = *(char **)v; *s != 0; s++) { 16 | hash = (hash ^ *s) * 2654435769U; 17 | } 18 | return (hash); 19 | } 20 | int str_eq (void *a, void *b) { 21 | return (strcmp (*(char **)a, *(char **)b) == 0); 22 | } 23 | void str_del (void *arg, void *data) { 24 | *(char **)arg = *(char **)data; 25 | } 26 | 27 | struct hash *h = hash_new (sizeof (char *), &str_hash, &str_eq, &str_del, 3, 12, 15); 28 | ... 3=> 2**3 entries initial size 29 | ... 12=> 2**12 entries before sprouting sub-tables 30 | ... 15=> number of adjacent probes to attempt before growing 31 | 32 | Example lookup: 33 | char *key = "foobar"; 34 | char **result_ptr; 35 | if (hash_lookup (h, &key, (void **) &result_ptr)) { 36 | printf ("found in table: %s\n", *result_ptr); 37 | } else { 38 | printf ("not found in table\n"); 39 | } 40 | 41 | Example insertion: 42 | char *key = strdup ("foobar"); 43 | char **result_ptr; 44 | if (hash_lookup (h, &key, (void **) &result_ptr)) { 45 | printf ("found in table: %s\n", *result_ptr); 46 | printf ("to overwrite, do *result_ptr = key\n"); 47 | } else { 48 | printf ("not found in table; inserted as %s\n", *result_ptr); 49 | assert (*result_ptr == key); 50 | } 51 | 52 | Example deletion: 53 | char *key = "foobar"; 54 | char *result; 55 | if (hash_remove (h, &key, &result)) { 56 | printf ("key found and deleted from table\n"); 57 | printf ("called str_del (&result, data) to copy data to result: %s\n", result); 58 | } else { 59 | printf ("not found in table\n"); 60 | } 61 | 62 | Example iteration over the elements of *h: 63 | char **data; 64 | struct hash_iter it; 65 | hash_iter_init (h, &it); 66 | for (data = hash_next (&it); data != 0; data = hash_next (&it)) { 67 | printf ("%s\n", *data); 68 | } 69 | */ 70 | /* 71 | #define malloc runtime·mal 72 | #define memset(a,b,c) runtime·memclr((byte*)(a), (uint32)(c)) 73 | #define memcpy(a,b,c) runtime·memmove((byte*)(a),(byte*)(b),(uint32)(c)) 74 | #define assert(a) if(!(a)) runtime·throw("hashmap assert") 75 | #define free(x) runtime·free(x) 76 | #define memmove(a,b,c) runtime·memmove(a, b, c) 77 | */ 78 | struct Hmap; /* opaque */ 79 | struct hash_subtable; /* opaque */ 80 | struct hash_entry; /* opaque */ 81 | 82 | typedef uintptr hash_hash_t; 83 | 84 | struct hash_iter { 85 | uint8* data; /* returned from next */ 86 | int32 elemsize; /* size of elements in table */ 87 | int32 changes; /* number of changes observed last time */ 88 | int32 i; /* stack pointer in subtable_state */ 89 | bool cycled; /* have reached the end and wrapped to 0 */ 90 | hash_hash_t last_hash; /* last hash value returned */ 91 | hash_hash_t cycle; /* hash value where we started */ 92 | struct Hmap *h; /* the hash table */ 93 | MapType *t; /* the map type */ 94 | struct hash_iter_sub { 95 | struct hash_entry *e; /* pointer into subtable */ 96 | struct hash_entry *start; /* start of subtable */ 97 | struct hash_entry *last; /* last entry in subtable */ 98 | } subtable_state[4]; /* Should be large enough unless the hashing is 99 | so bad that many distinct data values hash 100 | to the same hash value. */ 101 | }; 102 | 103 | /* Return a hashtable h 2**init_power empty entries, each with 104 | "datasize" data bytes. 105 | (*data_hash)(a) should return the hash value of data element *a. 106 | (*data_eq)(a,b) should return whether the data at "a" and the data at "b" 107 | are equal. 108 | (*data_del)(arg, a) will be invoked when data element *a is about to be removed 109 | from the table. "arg" is the argument passed to "hash_remove()". 110 | 111 | Growing is accomplished by resizing if the current tables size is less than 112 | a threshold, and by adding subtables otherwise. hint should be set 113 | the expected maximum size of the table. 114 | "datasize" should be in [sizeof (void*), ..., 255]. If you need a 115 | bigger "datasize", store a pointer to another piece of memory. */ 116 | 117 | //struct hash *hash_new (int32 datasize, 118 | // hash_hash_t (*data_hash) (void *), 119 | // int32 (*data_eq) (void *, void *), 120 | // void (*data_del) (void *, void *), 121 | // int64 hint); 122 | 123 | /* Lookup *data in *h. If the data is found, return 1 and place a pointer to 124 | the found element in *pres. Otherwise return 0 and place 0 in *pres. */ 125 | //int32 hash_lookup (struct hash *h, void *data, void **pres); 126 | 127 | /* Lookup *data in *h. If the data is found, execute (*data_del) (arg, p) 128 | where p points to the data in the table, then remove it from *h and return 129 | 1. Otherwise return 0. */ 130 | //int32 hash_remove (struct hash *h, void *data, void *arg); 131 | 132 | /* Lookup *data in *h. If the data is found, return 1, and place a pointer 133 | to the found element in *pres. Otherwise, return 0, allocate a region 134 | for the data to be inserted, and place a pointer to the inserted element 135 | in *pres; it is the caller's responsibility to copy the data to be 136 | inserted to the pointer returned in *pres in this case. 137 | 138 | If using garbage collection, it is the caller's responsibility to 139 | add references for **pres if HASH_ADDED is returned. */ 140 | //int32 hash_insert (struct hash *h, void *data, void **pres); 141 | 142 | /* Return the number of elements in the table. */ 143 | //uint32 hash_count (struct hash *h); 144 | 145 | /* The following call is useful only if not using garbage collection on the 146 | table. 147 | Remove all sub-tables associated with *h. 148 | This undoes the effects of hash_init(). 149 | If other memory pointed to by user data must be freed, the caller is 150 | responsible for doiing do by iterating over *h first; see 151 | hash_iter_init()/hash_next(). */ 152 | //void hash_destroy (struct hash *h); 153 | 154 | /*----- iteration -----*/ 155 | 156 | /* Initialize *it from *h. */ 157 | //void hash_iter_init (struct hash *h, struct hash_iter *it); 158 | 159 | /* Return the next used entry in the table which which *it was initialized. */ 160 | //void *hash_next (struct hash_iter *it); 161 | 162 | /*---- test interface ----*/ 163 | /* Call (*data_visit) (arg, level, data) for every data entry in the table, 164 | whether used or not. "level" is the subtable level, 0 means first level. */ 165 | /* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */ 166 | //void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg); 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /c_src/lru.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common.h" 4 | #include "lru.h" 5 | #include "double_link.h" 6 | 7 | static void lru_destroy_item(lru_item_t *item); 8 | 9 | lru_t * lru_create() { 10 | lru_t * lru = malloc(sizeof(lru_t)); 11 | if (lru == NULL) return NULL; 12 | lru->list = d_list_create(); 13 | return lru; 14 | } 15 | 16 | void lru_destroy(lru_t *lru) { 17 | d_node_t *node; 18 | lru_item_t *item; 19 | 20 | node = lru->list->head; 21 | while (NULL != node) { 22 | item = (lru_item_t*)node->data; 23 | lru_destroy_item(item); 24 | 25 | node = node->next; 26 | } 27 | 28 | d_list_destroy(lru->list); 29 | free(lru); 30 | } 31 | 32 | int lru_eject_by_size(lru_t *lru, int size, EjectionCallback eject, void * container) { 33 | int ejected = 0; 34 | lru_item_t *item; 35 | d_node_t *node; 36 | d_node_t *next; 37 | 38 | dprintf("ejecting %d bytes\n", size); 39 | 40 | while(ejected < size) { 41 | node = d_list_shift(lru->list); 42 | if (NULL == node) { 43 | break; 44 | } 45 | item = (lru_item_t*)node->data; 46 | ejected += lru_item_size(item); 47 | if (NULL != eject) { 48 | (*eject)(container, item->key, item->keylen); 49 | } 50 | lru_destroy_item(item); 51 | next = node->next; 52 | d_node_destroy(node); 53 | node = next; 54 | } 55 | return ejected; 56 | } 57 | 58 | lru_item_t * lru_insert(lru_t *lru, char* key, int keylen, void * value, int size, DestroyCallback destroy) { 59 | lru_item_t *item; 60 | 61 | item = malloc(sizeof(lru_item_t)); 62 | if (item == NULL) return NULL; 63 | item->key = key; 64 | item->keylen = keylen; 65 | item->value = value; 66 | item->vallen = size; 67 | item->destroy = destroy; 68 | item->node = d_node_create(item); 69 | d_list_push(lru->list, item->node); 70 | return item; 71 | } 72 | 73 | void lru_touch(lru_t *lru, lru_item_t *item) { 74 | d_list_remove(lru->list, item->node); 75 | d_list_push(lru->list, item->node); 76 | } 77 | 78 | void lru_remove_and_destroy(lru_t *lru, lru_item_t *item) { 79 | d_list_remove(lru->list, item->node); 80 | d_node_destroy(item->node); 81 | lru_destroy_item(item); 82 | } 83 | 84 | static void lru_destroy_item(lru_item_t *item) { 85 | if (NULL != item->destroy) { 86 | (*(item->destroy))(item->key, item->keylen, item->value, item->vallen); 87 | dprintf("invoke destroy callback %s %p\n", item->key, item->value); 88 | } 89 | free(item); 90 | } 91 | -------------------------------------------------------------------------------- /c_src/lru.h: -------------------------------------------------------------------------------- 1 | #ifndef __LRU__ 2 | #define __LRU__ 3 | 4 | #include "double_link.h" 5 | 6 | 7 | //the destroy callback, this is needed to free memory for shit 8 | typedef void (*DestroyCallback)(char*, int, void*, int); 9 | 10 | //this is the callback for LRU ejection, so that upper layers can cleanup 11 | //first arg is the containing struct. can this be cleaner? 12 | typedef void (*EjectionCallback)(void*, char*, int); 13 | 14 | typedef struct _lru_t { 15 | d_list_t *list; 16 | } lru_t; 17 | 18 | typedef struct _lru_item_t { 19 | char * key; 20 | int keylen; 21 | void * value; 22 | int vallen; 23 | d_node_t * node; 24 | DestroyCallback destroy; 25 | } lru_item_t; 26 | 27 | #define lru_item_key(item) ((item)->key) 28 | #define lru_item_keylen(item) ((item)->keylen) 29 | #define lru_item_value(item) ((item)->value) 30 | #define lru_item_vallen(item) ((item)->vallen) 31 | #define lru_item_size(item) (lru_item_keylen(item) + lru_item_vallen(item)) 32 | 33 | lru_t * lru_create(); 34 | void lru_destroy(lru_t *lru); 35 | int lru_eject_by_size(lru_t *lru, int size, EjectionCallback cb, void * container); 36 | lru_item_t * lru_insert(lru_t *lru, char* key, int keylen, void * value, int size, DestroyCallback destroy); 37 | void lru_touch(lru_t *lru, lru_item_t *item); 38 | void lru_remove_and_destroy(lru_t *lru, lru_item_t *item); 39 | 40 | #endif -------------------------------------------------------------------------------- /c_src/runtime.c: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #include "runtime.h" 6 | 7 | enum { 8 | maxround = sizeof(uintptr), 9 | }; 10 | 11 | static uint32 fastrand; 12 | 13 | int32 14 | runtime_mcmp(byte *s1, byte *s2, uint32 n) 15 | { 16 | uint32 i; 17 | byte c1, c2; 18 | 19 | for(i=0; i c2) 25 | return +1; 26 | } 27 | return 0; 28 | } 29 | 30 | 31 | byte* 32 | runtime_mchr(byte *p, byte c, byte *ep) 33 | { 34 | for(; p < ep; p++) 35 | if(*p == c) 36 | return p; 37 | return nil; 38 | } 39 | 40 | uint32 41 | runtime_rnd(uint32 n, uint32 m) 42 | { 43 | uint32 r; 44 | 45 | if(m > maxround) 46 | m = maxround; 47 | r = n % m; 48 | if(r) 49 | n += m-r; 50 | return n; 51 | } 52 | 53 | int32 54 | runtime_atoi(byte *p) 55 | { 56 | int32 n; 57 | 58 | n = 0; 59 | while('0' <= *p && *p <= '9') 60 | n = n*10 + *p++ - '0'; 61 | return n; 62 | } 63 | 64 | uint32 65 | runtime_fastrand1(void) 66 | { 67 | uint32 x; 68 | 69 | x = fastrand; 70 | x += x; 71 | if(x & 0x80000000L) 72 | x ^= 0x88888eefUL; 73 | fastrand = x; 74 | return x; 75 | } 76 | -------------------------------------------------------------------------------- /c_src/runtime.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #ifndef __RUNTIME__ 6 | #define __RUNTIME__ 7 | 8 | /* 9 | * basic types 10 | */ 11 | typedef signed char int8; 12 | typedef unsigned char uint8; 13 | typedef signed short int16; 14 | typedef unsigned short uint16; 15 | typedef signed int int32; 16 | typedef unsigned int uint32; 17 | typedef signed long long int int64; 18 | typedef unsigned long long int uint64; 19 | typedef float float32; 20 | typedef double float64; 21 | 22 | #if defined(__ia64) || defined(__x86_64) || defined(__amd64) 23 | typedef uint64 uintptr; 24 | typedef int64 intptr; 25 | #else 26 | typedef uint32 uintptr; 27 | typedef int32 intptr; 28 | #endif 29 | 30 | /* 31 | * get rid of C types 32 | * the / / / forces a syntax error immediately, 33 | * which will show "last name: XXunsigned". 34 | */ 35 | //#define unsigned XXunsigned / / / 36 | //#define signed XXsigned / / / 37 | //#define char XXchar / / / 38 | //#define short XXshort / / / 39 | //#define int XXint / / / 40 | //#define long XXlong / / / 41 | //#define float XXfloat / / / 42 | //#define double XXdouble / / / 43 | #define nelem(x) (sizeof(x)/sizeof((x)[0])) 44 | #define nil ((void*)0) 45 | #define offsetof(s,m) (uint32)(&(((s*)0)->m)) 46 | 47 | /* 48 | * defined types 49 | */ 50 | typedef uint8 bool; 51 | typedef uint8 byte; 52 | typedef struct String String; 53 | typedef struct Type Type; 54 | typedef struct MapType MapType; 55 | typedef struct Hmap Hmap; 56 | typedef struct hash_iter hash_iter; 57 | 58 | enum 59 | { 60 | true = 1, 61 | false = 0, 62 | }; 63 | 64 | struct String 65 | { 66 | byte* str; 67 | int32 len; 68 | }; 69 | typedef struct Alg Alg; 70 | struct Alg 71 | { 72 | void (*hash)(uintptr*, uintptr, void*); 73 | void (*equal)(bool*, uintptr, void*, void*); 74 | //void (*print)(uintptr, void*); 75 | void (*copy)(uintptr, void*, void*); 76 | }; 77 | 78 | extern Alg StrAlg; 79 | extern Type StrType; 80 | extern MapType StrMapType; 81 | 82 | void runtime_memhash(uintptr*, uintptr, void*); 83 | void runtime_strhash(uintptr*, uintptr, void*); 84 | 85 | void runtime_memequal(bool*, uintptr, void*, void*); 86 | void runtime_strequal(bool*, uintptr, void*, void*); 87 | 88 | void runtime_strcopy(uintptr, void*, void*); 89 | 90 | uint32 runtime_rnd(uint32, uint32); 91 | byte* runtime_mchr(byte*, byte, byte*); 92 | int32 runtime_mcmp(byte*, byte*, uint32); 93 | int32 runtime_atoi(byte*); 94 | uint32 runtime_fastrand1(void); 95 | 96 | int32 runtime_mapassign(MapType*, Hmap*, byte*, byte*); 97 | void runtime_mapaccess(MapType*, Hmap*, byte*, byte*, bool*); 98 | void runtime_mapiternext(hash_iter*); 99 | bool runtime_mapiterkey(hash_iter*, void*); 100 | void runtime_mapiterkeyvalue(hash_iter*, void*, void*); 101 | void runtime_mapiterinit(MapType*, Hmap*, hash_iter*); 102 | Hmap* runtime_makemap_c(MapType*, int64); 103 | void runtime_mapdestroy(Hmap*); 104 | // for debug 105 | void hash_visit (Hmap*, void (*data_visit) (void*, int32 , void*), void*); 106 | #endif 107 | -------------------------------------------------------------------------------- /c_src/slabs.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Slabs memory allocation, based on powers-of-N. Slabs are up to 1MB in size 4 | * and are divided into chunks. The chunk sizes start off at the size of the 5 | * "item" structure plus space for a small key and value. They increase by 6 | * a multiplier factor from there, up to half the maximum slab size. The last 7 | * slab size is always 1MB, since that's the maximum item size allowed by the 8 | * memcached protocol. 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "slabs.h" 27 | 28 | /* 29 | * Forward Declarations 30 | */ 31 | static int do_slabs_newslab(slabs_t* pst, const unsigned int id); 32 | static void *memory_allocate(slabs_t* pst, size_t size); 33 | 34 | /* 35 | * slab pool management 36 | */ 37 | void* pool_new(slabs_t* pst) { 38 | void *ptr; 39 | slabheader_t *shp; 40 | if (pst->pool_freelist == NULL) { 41 | if (pst->mem_limit && 42 | (pst->mem_malloced + SETTING_ITEM_SIZE_MAX > pst->mem_limit)) { 43 | return NULL; 44 | } 45 | ptr = memory_allocate(pst, SETTING_ITEM_SIZE_MAX); 46 | if (!ptr) return NULL; 47 | pst->mem_malloced += SETTING_ITEM_SIZE_MAX; 48 | shp = (slabheader_t*)ptr; 49 | shp->next = NULL; 50 | pst->pool_freelist = ptr; 51 | } 52 | shp = pst->pool_freelist; 53 | pst->pool_freelist = shp->next; 54 | return (void*)shp; 55 | } 56 | 57 | void pool_free(slabs_t* pst, void* ptr) { 58 | slabheader_t *shp; 59 | shp = (slabheader_t*)ptr; 60 | shp->next = pst->pool_freelist; 61 | pst->pool_freelist = shp; 62 | } 63 | 64 | /* 65 | * slab list management per slabclass 66 | */ 67 | bool slab_add(slabs_t* pst, slabclass_t* psct, void* ptr) { 68 | size_t need_byte; 69 | slablist_t* pslt = (slablist_t*)memory_allocate(pst, sizeof(slablist_t)); 70 | if (!pslt) return false; 71 | need_byte = (size_t)ceil(psct->perslab / 8.0); 72 | pslt->used_bitmap = (unsigned char*)memory_allocate(pst, need_byte); 73 | if (!pslt->used_bitmap) return false; 74 | memset(pslt->used_bitmap, 0, need_byte); 75 | pslt->ptr = ptr; 76 | pslt->next = psct->slab_list; 77 | psct->slab_list = pslt; 78 | return true; 79 | } 80 | 81 | void* slab_remove(slabs_t* pst, slabclass_t* psct, slablist_t* pslt_target) { 82 | void* pret; 83 | slablist_t* pslt = psct->slab_list; 84 | slablist_t* pprev = NULL; 85 | while (pslt != NULL) { 86 | if (pslt == pslt_target) { 87 | if (pprev) { 88 | pprev->next = pslt->next; 89 | } else { 90 | psct->slab_list = pslt->next; 91 | } 92 | pret = pslt->ptr; 93 | free(pslt->used_bitmap); 94 | free(pslt); 95 | return pret; 96 | } 97 | pprev = pslt; 98 | pslt = pslt->next; 99 | } 100 | return NULL; 101 | } 102 | 103 | slablist_t* slab_search(slabs_t* pst, slabclass_t* psct, char* ptr_in_slab) { 104 | slablist_t* pslt = psct->slab_list; 105 | char* pstart; 106 | char* pend; 107 | while (pslt != NULL) { 108 | pstart = (char*)pslt->ptr; 109 | pend = pstart + SETTING_ITEM_SIZE_MAX; 110 | if (ptr_in_slab >= pstart && ptr_in_slab <= pend) return pslt; 111 | pslt = pslt->next; 112 | } 113 | return NULL; 114 | } 115 | 116 | /* 117 | * slab free space management per slab 118 | */ 119 | #define SLABLIST_USED_IDX(pi, pbi, psct, pslt, ptr_in_slab) \ 120 | size_t byte_offset = (size_t)(ptr_in_slab - ((char*)pslt->ptr)); \ 121 | *pi = (size_t)(byte_offset / psct->size); \ 122 | *pbi = (size_t)round(index / 8) 123 | 124 | inline void slablist_used(slabclass_t* psct, slablist_t* pslt, char* ptr_in_slab) { 125 | size_t index; 126 | size_t bmp_index; 127 | SLABLIST_USED_IDX(&index, &bmp_index, psct, pslt, ptr_in_slab); 128 | unsigned char bitmask = (unsigned char)(1 << (index % 8)); 129 | pslt->used_bitmap[bmp_index] |= bitmask; 130 | } 131 | 132 | inline void slablist_unused(slabclass_t* psct, slablist_t* pslt, char* ptr_in_slab) { 133 | size_t index; 134 | size_t bmp_index; 135 | SLABLIST_USED_IDX(&index, &bmp_index, psct, pslt, ptr_in_slab); 136 | unsigned char bitmask = ~(unsigned char)(1 << (index % 8)); 137 | pslt->used_bitmap[bmp_index] &= bitmask; 138 | } 139 | 140 | inline bool slablist_is_empty(slabclass_t* psct, slablist_t* pslt) { 141 | unsigned char* pcurrent = (unsigned char*)pslt->used_bitmap; 142 | size_t need_byte = (size_t)ceil(psct->perslab / 8.0); 143 | while (need_byte > 0) { 144 | if (need_byte >= sizeof(unsigned int)) { 145 | if (*((unsigned int*)pcurrent)) return false; 146 | need_byte -= sizeof(unsigned int); 147 | pcurrent += sizeof(unsigned int); 148 | } else if (need_byte >= sizeof(unsigned short)) { 149 | if (*((unsigned short*)pcurrent)) return false; 150 | need_byte -= sizeof(unsigned short); 151 | pcurrent += sizeof(unsigned short); 152 | } else { 153 | if (*pcurrent) return false; 154 | need_byte -= sizeof(unsigned char); 155 | pcurrent += sizeof(unsigned char); 156 | } 157 | } 158 | return true; 159 | } 160 | 161 | /* 162 | * Figures out which slab class (chunk size) is required to store an item of 163 | * a given size. 164 | * 165 | * Given object size, return id to use when allocating/freeing memory for object 166 | * 0 means error: can't store such a large object 167 | */ 168 | 169 | static unsigned int slabs_clsid(slabs_t* pst, const size_t size) { 170 | int res = POWER_SMALLEST; 171 | 172 | if (size == 0) 173 | return 0; 174 | while (size > pst->slabclass[res].size) 175 | if (res++ == pst->power_largest) /* won't fit in the biggest slab */ 176 | return 0; 177 | return res; 178 | } 179 | 180 | /** 181 | * Determines the chunk sizes and initializes the slab class descriptors 182 | * accordingly. 183 | */ 184 | void slabs_init(slabs_t* pst, const size_t limit, const double factor, const bool prealloc) { 185 | int i = POWER_SMALLEST - 1; 186 | unsigned int size = sizeof(slabheader_t) + SETTING_CHUNK_SIZE; 187 | 188 | if (limit > 0 && limit < SETTING_ITEM_SIZE_MAX) { 189 | pst->mem_limit = SETTING_ITEM_SIZE_MAX; 190 | } else { 191 | pst->mem_limit = limit; 192 | } 193 | 194 | pst->pool_freelist = NULL; 195 | 196 | if (prealloc) { 197 | /* Allocate everything in a big chunk with malloc */ 198 | pst->mem_base = malloc(pst->mem_limit); 199 | if (pst->mem_base != NULL) { 200 | pst->mem_current = pst->mem_base; 201 | pst->mem_avail = pst->mem_limit; 202 | } else { 203 | fprintf(stderr, "Warning: Failed to allocate requested memory in" 204 | " one large chunk.\nWill allocate in smaller chunks\n"); 205 | } 206 | } 207 | 208 | memset(pst->slabclass, 0, sizeof(pst->slabclass)); 209 | 210 | while (++i < POWER_LARGEST && size <= SETTING_ITEM_SIZE_MAX / factor) { 211 | /* Make sure items are always n-byte aligned */ 212 | if (size % CHUNK_ALIGN_BYTES) 213 | size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); 214 | 215 | pst->slabclass[i].size = size; 216 | pst->slabclass[i].perslab = SETTING_ITEM_SIZE_MAX / pst->slabclass[i].size; 217 | size *= factor; 218 | if (SETTING_VERBOSE > 1) { 219 | fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", 220 | i, pst->slabclass[i].size, pst->slabclass[i].perslab); 221 | } 222 | } 223 | 224 | pst->power_largest = i; 225 | pst->slabclass[pst->power_largest].size = SETTING_ITEM_SIZE_MAX; 226 | pst->slabclass[pst->power_largest].perslab = 1; 227 | if (SETTING_VERBOSE > 1) { 228 | fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", 229 | i, pst->slabclass[i].size, pst->slabclass[i].perslab); 230 | fprintf(stderr, "pst:%p\n", pst); 231 | } 232 | 233 | } 234 | 235 | static int do_slabs_newslab(slabs_t* pst, const unsigned int id) { 236 | slabclass_t *p = &pst->slabclass[id]; 237 | 238 | void* ptr = pool_new(pst); 239 | if (ptr == NULL) return 0; 240 | 241 | p->end_page_ptr = ptr; 242 | p->end_page_free = p->perslab; 243 | 244 | bool ret = slab_add(pst, p, ptr); 245 | if (!ret) return 0; 246 | 247 | return 1; 248 | } 249 | 250 | static void *do_slabs_alloc(slabs_t* pst, const size_t size, unsigned int id) { 251 | slabclass_t *p; 252 | void *ret = NULL; 253 | slabheader_t *it = NULL; 254 | slablist_t* pslt = NULL; 255 | 256 | if (id < POWER_SMALLEST || id > pst->power_largest) { 257 | //MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0); 258 | return NULL; 259 | } 260 | 261 | p = &pst->slabclass[id]; 262 | //printf("alloc slab:%p class:%u ent_page_p:%p sl_curr:%u \n", pst, p->size, p->end_page_ptr, p->sl_curr); 263 | 264 | /* fail unless we have space at the end of a recently allocated page, 265 | we have something on our freelist, or we could allocate a new page */ 266 | if (! (p->end_page_ptr != 0 || p->sl_curr != 0 || 267 | do_slabs_newslab(pst, id) != 0)) { 268 | /* We don't have more memory available */ 269 | ret = NULL; 270 | } else if (p->sl_curr != 0) { 271 | /* return off our freelist */ 272 | it = (slabheader_t*)p->slots; 273 | p->slots = it->next; 274 | if (it->next) it->next->prev = 0; 275 | p->sl_curr--; 276 | ret = (void *)it; 277 | pslt = slab_search(pst, p, (char*)ret); 278 | slablist_used(p, pslt, (char*)ret); 279 | } else { 280 | /* if we recently allocated a whole page, return from that */ 281 | assert(p->end_page_ptr != NULL); 282 | ret = p->end_page_ptr; 283 | if (--p->end_page_free != 0) { 284 | p->end_page_ptr = ((caddr_t)p->end_page_ptr) + p->size; 285 | } else { 286 | p->end_page_ptr = 0; 287 | } 288 | pslt = slab_search(pst, p, (char*)ret); 289 | slablist_used(p, pslt, (char*)ret); 290 | } 291 | 292 | if (ret) { 293 | p->requested += size; 294 | //MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret); 295 | } else { 296 | //MEMCACHED_SLABS_ALLOCATE_FAILED(size, id); 297 | } 298 | //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); 299 | 300 | return ret; 301 | } 302 | 303 | static void do_slabs_free(slabs_t* pst, void *ptr, const size_t size, unsigned int id) { 304 | slabclass_t *p; 305 | slabheader_t* it; 306 | slablist_t* pslt; 307 | bool ret; 308 | void* ppool; 309 | //assert(((item *)ptr)->slabs_clsid == 0); 310 | assert(id >= POWER_SMALLEST && id <= pst->power_largest); 311 | if (id < POWER_SMALLEST || id > pst->power_largest) 312 | return; 313 | 314 | //MEMCACHED_SLABS_FREE(size, id, ptr); 315 | p = &pst->slabclass[id]; 316 | 317 | it = (slabheader_t*)ptr; 318 | //it->it_flags |= ITEM_SLABBED; 319 | it->prev = 0; 320 | it->next = p->slots; 321 | if (it->next) it->next->prev = it; 322 | p->slots = it; 323 | 324 | p->sl_curr++; 325 | p->requested -= size; 326 | 327 | //printf("free slab:%p class:%u ent_page_p:%p sl_curr:%u \n", pst, p->size, p->end_page_ptr, p->sl_curr); 328 | 329 | pslt = slab_search(pst, p, (char*)ptr); 330 | slablist_unused(p, pslt, (char*)ptr); 331 | ret = slablist_is_empty(p, pslt); 332 | if (ret) { 333 | // release slab from freelist(slots) 334 | slabheader_t* pit = it; 335 | slabheader_t* pprev = NULL; 336 | while (pit != NULL) { 337 | slablist_t* pslt_curr = slab_search(pst, p, (char*)pit); 338 | if (pslt == pslt_curr) { 339 | if (pprev) { 340 | pprev->next = pit->next; 341 | } else { 342 | p->slots = pit->next; 343 | } 344 | p->sl_curr--; 345 | } else { 346 | pprev = pit; 347 | } 348 | pit = pit->next; 349 | } 350 | // release slab from end_page(end_page_ptr, end_page_free) 351 | slablist_t* pslt_endp = slab_search(pst, p, (char*)p->end_page_ptr); 352 | if (pslt_endp == pslt) { 353 | p->end_page_ptr = NULL; 354 | p->end_page_free = 0; 355 | } 356 | // release slab from slab_list 357 | ppool = slab_remove(pst, p, pslt); 358 | pool_free(pst, ppool); 359 | } 360 | 361 | return; 362 | } 363 | 364 | /* 365 | static int nz_strcmp(int nzlength, const char *nz, const char *z) { 366 | int zlength=strlen(z); 367 | return (zlength == nzlength) && (strncmp(nz, z, zlength) == 0) ? 0 : -1; 368 | } 369 | 370 | bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c) { 371 | bool ret = true; 372 | 373 | if (add_stats != NULL) { 374 | if (!stat_type) { 375 | STATS_LOCK(); 376 | APPEND_STAT("bytes", "%llu", (unsigned long long)stats.curr_bytes); 377 | APPEND_STAT("curr_items", "%u", stats.curr_items); 378 | APPEND_STAT("total_items", "%u", stats.total_items); 379 | APPEND_STAT("evictions", "%llu", 380 | (unsigned long long)stats.evictions); 381 | APPEND_STAT("reclaimed", "%llu", 382 | (unsigned long long)stats.reclaimed); 383 | STATS_UNLOCK(); 384 | } else if (nz_strcmp(nkey, stat_type, "items") == 0) { 385 | item_stats(add_stats, c); 386 | } else if (nz_strcmp(nkey, stat_type, "slabs") == 0) { 387 | slabs_stats(add_stats, c); 388 | } else if (nz_strcmp(nkey, stat_type, "sizes") == 0) { 389 | item_stats_sizes(add_stats, c); 390 | } else { 391 | ret = false; 392 | } 393 | } else { 394 | ret = false; 395 | } 396 | 397 | return ret; 398 | } 399 | 400 | static void do_slabs_stats(ADD_STAT add_stats, void *c) { 401 | int i, total; 402 | 403 | struct thread_stats thread_stats; 404 | threadlocal_stats_aggregate(&thread_stats); 405 | 406 | total = 0; 407 | for(i = POWER_SMALLEST; i <= power_largest; i++) { 408 | slabclass_t *p = &slabclass[i]; 409 | if (p->slabs != 0) { 410 | uint32_t perslab, slabs; 411 | slabs = p->slabs; 412 | perslab = p->perslab; 413 | 414 | char key_str[STAT_KEY_LEN]; 415 | char val_str[STAT_VAL_LEN]; 416 | int klen = 0, vlen = 0; 417 | 418 | APPEND_NUM_STAT(i, "chunk_size", "%u", p->size); 419 | APPEND_NUM_STAT(i, "chunks_per_page", "%u", perslab); 420 | APPEND_NUM_STAT(i, "total_pages", "%u", slabs); 421 | APPEND_NUM_STAT(i, "total_chunks", "%u", slabs * perslab); 422 | APPEND_NUM_STAT(i, "used_chunks", "%u", 423 | slabs*perslab - p->sl_curr - p->end_page_free); 424 | APPEND_NUM_STAT(i, "free_chunks", "%u", p->sl_curr); 425 | APPEND_NUM_STAT(i, "free_chunks_end", "%u", p->end_page_free); 426 | APPEND_NUM_STAT(i, "mem_requested", "%llu", 427 | (unsigned long long)p->requested); 428 | APPEND_NUM_STAT(i, "get_hits", "%llu", 429 | (unsigned long long)thread_stats.slab_stats[i].get_hits); 430 | APPEND_NUM_STAT(i, "cmd_set", "%llu", 431 | (unsigned long long)thread_stats.slab_stats[i].set_cmds); 432 | APPEND_NUM_STAT(i, "delete_hits", "%llu", 433 | (unsigned long long)thread_stats.slab_stats[i].delete_hits); 434 | APPEND_NUM_STAT(i, "incr_hits", "%llu", 435 | (unsigned long long)thread_stats.slab_stats[i].incr_hits); 436 | APPEND_NUM_STAT(i, "decr_hits", "%llu", 437 | (unsigned long long)thread_stats.slab_stats[i].decr_hits); 438 | APPEND_NUM_STAT(i, "cas_hits", "%llu", 439 | (unsigned long long)thread_stats.slab_stats[i].cas_hits); 440 | APPEND_NUM_STAT(i, "cas_badval", "%llu", 441 | (unsigned long long)thread_stats.slab_stats[i].cas_badval); 442 | APPEND_NUM_STAT(i, "touch_hits", "%llu", 443 | (unsigned long long)thread_stats.slab_stats[i].touch_hits); 444 | total++; 445 | } 446 | } 447 | 448 | 449 | 450 | APPEND_STAT("active_slabs", "%d", total); 451 | APPEND_STAT("total_malloced", "%llu", (unsigned long long)mem_malloced); 452 | add_stats(NULL, 0, NULL, 0, c); 453 | } 454 | */ 455 | 456 | static void *memory_allocate(slabs_t* pst, size_t size) { 457 | void *ret; 458 | 459 | if (pst->mem_base == NULL) { 460 | /* We are not using a preallocated large memory chunk */ 461 | ret = malloc(size); 462 | } else { 463 | ret = pst->mem_current; 464 | 465 | if (size > pst->mem_avail) { 466 | return NULL; 467 | } 468 | 469 | /* mem_current pointer _must_ be aligned!!! */ 470 | if (size % CHUNK_ALIGN_BYTES) { 471 | size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); 472 | } 473 | 474 | pst->mem_current = ((char*)pst->mem_current) + size; 475 | if (size < pst->mem_avail) { 476 | pst->mem_avail -= size; 477 | } else { 478 | pst->mem_avail = 0; 479 | } 480 | } 481 | 482 | return ret; 483 | } 484 | 485 | void *slabs_alloc(slabs_t* pst, size_t size) { 486 | void *ret; 487 | size += sizeof(slabheader_t); 488 | unsigned int id = slabs_clsid(pst, size); 489 | ret = do_slabs_alloc(pst, size, id); 490 | if (ret == NULL) { 491 | return NULL; 492 | } 493 | return (void*)((char*)ret + sizeof(slabheader_t)); 494 | } 495 | 496 | void slabs_free(slabs_t* pst, void *ptr, size_t size) { 497 | void *header; 498 | size += sizeof(slabheader_t); 499 | unsigned int id = slabs_clsid(pst, size); 500 | header = (void*)((char*)ptr - sizeof(slabheader_t)); 501 | do_slabs_free(pst, header, size, id); 502 | } 503 | /* 504 | void slabs_stats(ADD_STAT add_stats, void *c) { 505 | pthread_mutex_lock(&slabs_lock); 506 | do_slabs_stats(add_stats, c); 507 | pthread_mutex_unlock(&slabs_lock); 508 | } 509 | */ 510 | -------------------------------------------------------------------------------- /c_src/slabs.h: -------------------------------------------------------------------------------- 1 | /* slabs memory allocation */ 2 | #ifndef SLABS_H 3 | #define SLABS_H 4 | 5 | #include "runtime.h" 6 | 7 | #define SETTING_CHUNK_SIZE 128 8 | #define SETTING_ITEM_SIZE_MAX 1024 * 1024 * 4 9 | #define POWER_LARGEST 200 10 | #define MAX_NUMBER_OF_SLAB_CLASSES (POWER_LARGEST + 1) 11 | #define POWER_SMALLEST 1 12 | #define CHUNK_ALIGN_BYTES 8 13 | #define SETTING_VERBOSE 2 14 | #define MAX_NUMBER_OF_SLAB_CLASSES (POWER_LARGEST + 1) 15 | 16 | typedef struct slabheader { 17 | struct slabheader *next; 18 | struct slabheader *prev; 19 | } slabheader_t; 20 | 21 | typedef struct slablist { 22 | void *ptr; 23 | unsigned char *used_bitmap; // using perslab/8 24 | struct slablist *next; 25 | } slablist_t; 26 | 27 | typedef struct { 28 | unsigned int size; /* sizes of items */ 29 | unsigned int perslab; /* how many items per slab */ 30 | 31 | void *slots; /* list of item ptrs */ 32 | unsigned int sl_curr; /* total free items in list */ 33 | 34 | void *end_page_ptr; /* pointer to next free item at end of page, or 0 */ 35 | unsigned int end_page_free; /* number of items remaining at end of last alloced page */ 36 | 37 | unsigned int slabs; /* how many slabs were allocated for this class */ 38 | 39 | //@TODO void **slab_list; /* array of slab pointers */ 40 | slablist_t *slab_list; /* array of slab pointers */ 41 | unsigned int list_size; /* size of prev array */ 42 | 43 | unsigned int killing; /* index+1 of dying slab, or zero if none */ 44 | size_t requested; /* The number of requested bytes */ 45 | } slabclass_t; 46 | // per thread 47 | typedef struct { 48 | slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES]; 49 | size_t mem_limit; 50 | size_t mem_malloced; 51 | int power_largest; 52 | void *mem_base; 53 | void *mem_current; 54 | size_t mem_avail; 55 | void *pool_freelist; 56 | } slabs_t; 57 | 58 | void* pool_new(slabs_t* pst); 59 | void pool_free(slabs_t* pst, void* ptr); 60 | bool slab_add(slabs_t* pst, slabclass_t* psct, void* ptr); 61 | void* slab_remove(slabs_t* pst, slabclass_t* psct, slablist_t* pslt_target); 62 | slablist_t* slab_search(slabs_t* pst, slabclass_t* psct, char* ptr_in_slab); 63 | void slablist_used(slabclass_t* psct, slablist_t* pslt, char* ptr_in_slab); 64 | void slablist_unused(slabclass_t* psct, slablist_t* pslt, char* ptr_in_slab); 65 | bool slablist_is_empty(slabclass_t* psct, slablist_t* pslt); 66 | 67 | /** Init the subsystem. 1st argument is the limit on no. of bytes to allocate, 68 | 0 if no limit. 2nd argument is the growth factor; each slab will use a chunk 69 | size equal to the previous slab's chunk size times this factor. 70 | 3rd argument specifies if the slab allocator should allocate all memory 71 | up front (if true), or allocate memory in chunks as it is needed (if false) 72 | */ 73 | void slabs_init(slabs_t* pst, const size_t limit, const double factor, const bool prealloc); 74 | 75 | 76 | /** 77 | * Given object size, return id to use when allocating/freeing memory for object 78 | * 0 means error: can't store such a large object 79 | */ 80 | 81 | //unsigned int slabs_clsid(slabs_t* pst, const size_t size); 82 | 83 | /** Allocate object of given length. 0 on error */ /*@null@*/ 84 | void *slabs_alloc(slabs_t* pst, const size_t size); 85 | 86 | /** Free previously allocated object */ 87 | void slabs_free(slabs_t* pst, void *ptr, size_t size); 88 | 89 | /** Fill buffer with stats */ /*@null@*/ 90 | //void slabs_stats(ADD_STAT add_stats, void *c); 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /c_src/test/check_cherly.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | #include 4 | #include "cherly.h" 5 | 6 | static slabs_t slab; 7 | 8 | static void print_visitor(void *arg, int32 level, void *data) { 9 | int i; 10 | String* key = (String*)data; 11 | String* val = (String*)((byte*)data+runtime_rnd(sizeof(String), sizeof(void*))); 12 | if (key->str == nil) return; 13 | printf("%s\n", (const char*)arg); 14 | for (i = 0; i < level; i++) 15 | printf("\t"); 16 | printf("mapassign: level=%d key=%s val=%s pk=%p pv=%p \n", level, key->str, val->str, key, val); 17 | } 18 | 19 | START_TEST(basic_get_and_put) 20 | { cherly_t cherly; 21 | char* key = "exist"; 22 | char* stuff = "stuff"; 23 | int len; 24 | 25 | void* buf = slabs_alloc(&slab, 20); 26 | printf("basic: buf=%p \n", buf); 27 | memset(buf, 0, 20); 28 | memcpy(buf, key, 5); 29 | void* vbuf = ((char*)buf)+6; 30 | memcpy(vbuf, stuff, 5); 31 | cherly_init(&cherly, 0, 120); 32 | fail_unless(NULL == cherly_get(&cherly, "noexist", 7, &len)); 33 | cherly_put(&cherly, buf, 5, vbuf, 5, NULL); 34 | hash_visit(cherly.hm, print_visitor, "basic"); 35 | fail_unless(NULL == cherly_get(&cherly, "noexist", 7, &len)); 36 | void* ret = cherly_get(&cherly, key, 5, &len); 37 | fail_unless(memcmp(ret, vbuf, len) == 0); 38 | cherly_destroy(&cherly); 39 | //slabs_free(&slab, buf, 20); 40 | } 41 | END_TEST 42 | 43 | START_TEST(put_already_there) 44 | { 45 | cherly_t cherly; 46 | char* stuff = "stuff"; 47 | char* otherstuff = "blah"; 48 | 49 | void* buf = slabs_alloc(&slab, 20); 50 | printf("basic: buf=%p \n", buf); 51 | memset(buf, 0, 20); 52 | memcpy(buf, stuff, 5); 53 | void* nbuf = ((char*)buf)+6; 54 | memcpy(nbuf, otherstuff, 4); 55 | cherly_init(&cherly, 0, 120); 56 | cherly_put(&cherly, "exist", 5, buf, 5, NULL); 57 | fail_unless(10 == cherly_size(&cherly)); 58 | cherly_put(&cherly, "exist", 5, nbuf, 4, NULL); 59 | hash_visit(cherly.hm, print_visitor, "put already"); 60 | cherly_destroy(&cherly); 61 | } 62 | END_TEST 63 | 64 | START_TEST(put_beyond_limit) 65 | { 66 | cherly_t cherly; 67 | 68 | cherly_init(&cherly, 0, 12); 69 | void* buf = slabs_alloc(&slab, 10); 70 | printf("basic: buf=%p \n", buf); 71 | memset(buf, 0, 10); 72 | memcpy(buf, "one", 3); 73 | cherly_put(&cherly, "one", 3, buf, 3, NULL); 74 | hash_visit(cherly.hm, print_visitor, "put1"); 75 | slabs_free(&slab, buf, 10); 76 | 77 | void* buf2 = slabs_alloc(&slab, 10); 78 | printf("basic: buf2=%p \n", buf2); 79 | memset(buf2, 0, 10); 80 | memcpy(buf2, "two", 3); 81 | cherly_put(&cherly, "two", 3, buf2, 3, NULL); 82 | hash_visit(cherly.hm, print_visitor, "put2"); 83 | slabs_free(&slab, buf2, 10); 84 | 85 | void* buf3 = slabs_alloc(&slab, 10); 86 | printf("basic: buf3=%p \n", buf3); 87 | memset(buf3, 0, 10); 88 | memcpy(buf3, "three", 5); 89 | cherly_put(&cherly, "three", 5, buf3, 5, NULL); 90 | hash_visit(cherly.hm, print_visitor, "put3"); 91 | fail_unless(1 == cherly_items_length(&cherly), "cherly length was %d", cherly_items_length(&cherly)); 92 | fail_unless(10 == cherly_size(&cherly), "cherly size was %d", cherly_size(&cherly)); 93 | } 94 | END_TEST 95 | 96 | Suite * cherly_suite(void) { 97 | Suite *s = suite_create("cherly.c"); 98 | 99 | /* Core test case */ 100 | memset(&slab, 0, sizeof(slab)); 101 | slabs_init(&slab, 0, 1.5, false); 102 | 103 | TCase *tc_core = tcase_create("Core"); 104 | tcase_add_test(tc_core, basic_get_and_put); 105 | tcase_add_test(tc_core, put_already_there); 106 | tcase_add_test(tc_core, put_beyond_limit); 107 | suite_add_tcase(s, tc_core); 108 | 109 | return s; 110 | } 111 | -------------------------------------------------------------------------------- /c_src/test/check_double_link.c: -------------------------------------------------------------------------------- 1 | #include "double_link.h" 2 | #include "stdlib.h" 3 | #include 4 | #include "common.h" 5 | 6 | START_TEST(create_and_remove) 7 | { 8 | d_list_t *list; 9 | d_node_t *node; 10 | int i; 11 | 12 | list = d_list_create(); 13 | node = d_node_create(NULL); 14 | fail_unless(node != NULL, NULL); 15 | d_list_push(list, node); 16 | fail_unless(1 == d_list_size(list), NULL); 17 | 18 | for(i=0; i<5; i++) { 19 | node = d_node_create(NULL); 20 | d_list_push(list, node); 21 | } 22 | fail_unless(6 == d_list_size(list), NULL); 23 | } 24 | END_TEST 25 | 26 | Suite * double_link_suite(void) { 27 | Suite *s = suite_create ("double_link.c"); 28 | 29 | /* Core test case */ 30 | TCase *tc_core = tcase_create ("Core"); 31 | tcase_add_test (tc_core, create_and_remove); 32 | suite_add_tcase (s, tc_core); 33 | 34 | return s; 35 | } -------------------------------------------------------------------------------- /c_src/test/check_lru.c: -------------------------------------------------------------------------------- 1 | #include "lru.h" 2 | #include "stdlib.h" 3 | #include "stdio.h" 4 | #include 5 | #include "common.h" 6 | 7 | START_TEST(create_and_insert) { 8 | lru_t *lru; 9 | 10 | lru = lru_create(); 11 | fail_unless(NULL != lru->list); 12 | lru_insert(lru, "key", 3, "value", 5, NULL); 13 | fail_unless(NULL != lru->list->head); 14 | lru_destroy(lru); 15 | } 16 | END_TEST 17 | 18 | START_TEST(touch) { 19 | lru_t *lru; 20 | lru_item_t * one; 21 | 22 | lru = lru_create(); 23 | one = lru_insert(lru, "one", 3, "one", 3, NULL); 24 | lru_insert(lru, "two", 3, "two", 3, NULL); 25 | lru_insert(lru, "three", 5, "three", 5, NULL); 26 | lru_touch(lru, one); 27 | fail_unless(one == (lru_item_t*)lru->list->head->data); 28 | lru_destroy(lru); 29 | } 30 | END_TEST 31 | 32 | START_TEST(eject_one) { 33 | lru_t *lru; 34 | int ejected = 0; 35 | 36 | lru = lru_create(); 37 | lru_insert(lru, "one", 3, "one", 3, NULL); 38 | lru_insert(lru, "two", 3, "two", 3, NULL); 39 | lru_insert(lru, "three", 5, "three", 5, NULL); 40 | ejected = lru_eject_by_size(lru, 3, NULL, NULL); 41 | dprintf("ejected %d\n", ejected); 42 | fail_unless(ejected > 0); 43 | } 44 | END_TEST 45 | 46 | START_TEST(eject_multiple) { 47 | lru_t *lru; 48 | int ejected = 0; 49 | lru_item_t *three; 50 | mark_point(); 51 | lru = lru_create(); 52 | mark_point(); 53 | lru_insert(lru, "one", 3, "one", 3, NULL); 54 | lru_insert(lru, "two", 3, "two", 3, NULL); 55 | three = lru_insert(lru, "three", 5, "three", 5, NULL); 56 | ejected = lru_eject_by_size(lru, 12, NULL, NULL); 57 | dprintf("test ejected %d\n", ejected); 58 | fail_unless((lru_item_t*)lru->list->head->data == three); 59 | fail_unless(ejected == 12); 60 | lru_destroy(lru); 61 | } 62 | END_TEST 63 | 64 | Suite * lru_suite(void) { 65 | Suite *s = suite_create("lru.c"); 66 | 67 | TCase *tc_core = tcase_create("Core"); 68 | tcase_add_test(tc_core, create_and_insert); 69 | tcase_add_test(tc_core, touch); 70 | tcase_add_test(tc_core, eject_one); 71 | tcase_add_test(tc_core, eject_multiple); 72 | suite_add_tcase(s, tc_core); 73 | 74 | return s; 75 | } -------------------------------------------------------------------------------- /c_src/test/check_slabs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "slabs.h" 6 | 7 | static slabs_t slab; 8 | 9 | START_TEST(pool) 10 | { 11 | void* ps1 = pool_new(&slab); 12 | fail_unless(NULL != ps1); 13 | fail_unless(NULL == slab.pool_freelist); 14 | void* ps2 = pool_new(&slab); 15 | fail_unless(NULL != ps2); 16 | fail_unless(NULL == slab.pool_freelist); 17 | pool_free(&slab, ps1); 18 | fail_unless(ps1 == slab.pool_freelist); 19 | pool_free(&slab, ps2); 20 | fail_unless(ps2 == slab.pool_freelist); 21 | slabheader_t *shp = (slabheader_t*)slab.pool_freelist; 22 | fail_unless(ps1 == shp->next); 23 | void* ps3 = pool_new(&slab); 24 | fail_unless(ps2 == ps3); 25 | fail_unless(ps1 == slab.pool_freelist); 26 | void* ps4 = pool_new(&slab); 27 | fail_unless(ps1 == ps4); 28 | fail_unless(NULL == slab.pool_freelist); 29 | void* ps5 = pool_new(&slab); 30 | fail_unless(NULL != ps5); 31 | fail_unless(NULL == slab.pool_freelist); 32 | } 33 | END_TEST 34 | 35 | START_TEST(slablist) 36 | { 37 | slabclass_t* psct = &slab.slabclass[1]; // size:144 perslab:58254 38 | void* ps = pool_new(&slab); 39 | bool ret = slab_add(&slab, psct, ps); 40 | fail_unless(ret); 41 | fail_unless(NULL != psct->slab_list); 42 | fail_unless(ps == psct->slab_list->ptr); 43 | fail_unless(NULL == psct->slab_list->next); 44 | size_t need_byte = (size_t)ceil(psct->perslab / 8); 45 | void* pv = malloc(need_byte); 46 | memset(pv, 0, need_byte); 47 | fail_unless(0 == memcmp(pv, psct->slab_list->used_bitmap, need_byte)); 48 | void* ps2 = pool_new(&slab); 49 | ret = slab_add(&slab, psct, ps2); 50 | fail_unless(ret); 51 | fail_unless(NULL != psct->slab_list); 52 | fail_unless(ps2 == psct->slab_list->ptr); 53 | fail_unless(NULL != psct->slab_list->next); 54 | fail_unless(NULL == psct->slab_list->next->next); 55 | void* ps3 = pool_new(&slab); 56 | ret = slab_add(&slab, psct, ps3); 57 | fail_unless(ret); 58 | slablist_t* pslt = slab_search(&slab, psct, ((char*)ps + 4)); 59 | fail_unless(pslt == psct->slab_list->next->next); 60 | pslt = slab_search(&slab, psct, ((char*)ps2)); 61 | fail_unless(pslt == psct->slab_list->next); 62 | pslt = slab_search(&slab, psct, ((char*)ps3 + SETTING_ITEM_SIZE_MAX)); 63 | fail_unless(pslt == psct->slab_list); 64 | pslt = slab_search(&slab, psct, 0); 65 | fail_unless(pslt == NULL); 66 | void* ps4 = slab_remove(&slab, psct, psct->slab_list->next); 67 | fail_unless(ps4 == ps2); 68 | fail_unless(psct->slab_list->ptr == ps3); 69 | fail_unless(psct->slab_list->next->ptr == ps); 70 | fail_unless(psct->slab_list->next->next == NULL); 71 | void* ps5 = slab_remove(&slab, psct, psct->slab_list); 72 | fail_unless(ps5 == ps3); 73 | fail_unless(psct->slab_list->ptr == ps); 74 | fail_unless(psct->slab_list->next == NULL); 75 | } 76 | END_TEST 77 | 78 | START_TEST(slab_used_bitmap) 79 | { 80 | slabclass_t* psct = &slab.slabclass[1]; // size:144 perslab:58254 81 | void* ps = pool_new(&slab); 82 | slab_add(&slab, psct, ps); 83 | slablist_t* pslt = psct->slab_list; 84 | fail_unless(slablist_is_empty(psct, pslt)); 85 | char* pc1 = (char*)(pslt->ptr) + psct->size * 8; // should point to the head of 2byte 86 | slablist_used(psct, pslt, pc1); 87 | fail_unless(pslt->used_bitmap[1] == 1); 88 | fail_unless(!slablist_is_empty(psct, pslt)); 89 | char* pc2 = (char*)(pslt->ptr) + psct->size * 9; 90 | slablist_used(psct, pslt, pc2); 91 | fail_unless(pslt->used_bitmap[1] == 3); 92 | fail_unless(!slablist_is_empty(psct, pslt)); 93 | slablist_unused(psct, pslt, pc1); 94 | fail_unless(pslt->used_bitmap[1] == 2); 95 | fail_unless(!slablist_is_empty(psct, pslt)); 96 | slablist_unused(psct, pslt, pc2); 97 | fail_unless(pslt->used_bitmap[1] == 0); 98 | fail_unless(slablist_is_empty(psct, pslt)); 99 | } 100 | END_TEST 101 | 102 | START_TEST(slab_it) 103 | { 104 | // The freed slab exist at both(slots, end_page) 105 | slab.pool_freelist = NULL; 106 | void* p1 = slabs_alloc(&slab, 1000000); 107 | void* p2 = slabs_alloc(&slab, 1000000); 108 | slabs_free(&slab, p1, 1000000); 109 | fail_unless(slab.pool_freelist == NULL); 110 | slabclass_t* psct = &slab.slabclass[14]; 111 | fail_unless(psct->slab_list != NULL); 112 | fail_unless(psct->slab_list->next == NULL); 113 | fail_unless(psct->sl_curr == 1); 114 | fail_unless(psct->end_page_ptr != NULL); 115 | fail_unless(psct->end_page_free == 1); 116 | slabs_free(&slab, p2, 1000000); 117 | fail_unless(slab.pool_freelist != NULL); 118 | fail_unless(psct->slab_list == NULL); 119 | fail_unless(psct->sl_curr == 0); 120 | fail_unless(psct->end_page_ptr == NULL); 121 | fail_unless(psct->end_page_free == 0); 122 | // The freed slab exist at end_page 123 | void* p3 = slabs_alloc(&slab, 1000000); 124 | fail_unless(p1 == p3); 125 | fail_unless(slab.pool_freelist == NULL); 126 | fail_unless(psct->slots == NULL); 127 | slabs_free(&slab, p3, 1000000); 128 | fail_unless(slab.pool_freelist != NULL); 129 | slabheader_t *shp = (slabheader_t*)slab.pool_freelist; 130 | fail_unless(shp->next == NULL); 131 | fail_unless(psct->slab_list == NULL); 132 | fail_unless(psct->sl_curr == 0); 133 | fail_unless(psct->end_page_ptr == NULL); 134 | fail_unless(psct->end_page_free == 0); 135 | // The freed slab exist at slots 136 | void* p11 = slabs_alloc(&slab, 1000000); 137 | void* p12 = slabs_alloc(&slab, 1000000); 138 | void* p13 = slabs_alloc(&slab, 1000000); 139 | void* p14 = slabs_alloc(&slab, 1000000); // second slab start 140 | void* p15 = slabs_alloc(&slab, 1000000); 141 | void* p16 = slabs_alloc(&slab, 1000000); 142 | fail_unless(psct->end_page_ptr == NULL); 143 | fail_unless(psct->end_page_free == 0); 144 | fail_unless(psct->slab_list != NULL); 145 | fail_unless(psct->slab_list->next != NULL); 146 | fail_unless(psct->slab_list->next->next == NULL); 147 | slabs_free(&slab, p11, 1000000); 148 | slabs_free(&slab, p13, 1000000); 149 | slabs_free(&slab, p14, 1000000); 150 | slabs_free(&slab, p15, 1000000); 151 | fail_unless(psct->slots != NULL); 152 | slabs_free(&slab, p16, 1000000); 153 | shp = (slabheader_t*)psct->slots; 154 | fail_unless((shp + 1) == p13); 155 | fail_unless((shp->next + 1) == p11); 156 | fail_unless(shp->next->next == NULL); 157 | fail_unless(psct->end_page_ptr == NULL); 158 | fail_unless(psct->end_page_free == 0); 159 | shp = (slabheader_t*)slab.pool_freelist; 160 | fail_unless(shp->next == NULL); 161 | void* p17 = slabs_alloc(&slab, 1000000); 162 | fail_unless(p17 == p13); 163 | } 164 | END_TEST 165 | 166 | START_TEST(slab_exceed_limit) 167 | { 168 | void* p1; 169 | int i; 170 | slabs_t slab_ex; 171 | const size_t EXPECTED_MAX_ITEM = 4; 172 | const size_t ALLOC_SIZE = SETTING_ITEM_SIZE_MAX - sizeof(slabheader_t); 173 | memset(&slab_ex, 0, sizeof(slabs_t)); 174 | fprintf(stderr, "do exceed pst:%p\n", &slab_ex); 175 | slabs_init(&slab_ex, SETTING_ITEM_SIZE_MAX * EXPECTED_MAX_ITEM, 2, false); 176 | for (i = 0; i < EXPECTED_MAX_ITEM; i++) { 177 | p1 = slabs_alloc(&slab_ex, ALLOC_SIZE); 178 | fail_unless(p1 != NULL); 179 | } 180 | void* p2 = slabs_alloc(&slab_ex, ALLOC_SIZE); 181 | fail_unless(p2 == NULL); 182 | slabs_free(&slab_ex, p1, ALLOC_SIZE); 183 | // success if request size belongs to the above same slab 184 | void* p3 = slabs_alloc(&slab_ex, ALLOC_SIZE); 185 | fail_unless(p3 != NULL); 186 | // fail if request size belongs to a different slab 187 | void* p4 = slabs_alloc(&slab_ex, 512); 188 | fail_unless(p4 == NULL); 189 | } 190 | END_TEST 191 | 192 | Suite* slabs_suite(void) { 193 | Suite *s = suite_create("slabs.c"); 194 | 195 | /* Core test case */ 196 | memset(&slab, 0, sizeof(slab)); 197 | slabs_init(&slab, 0, 2, false); 198 | 199 | TCase *tc_core = tcase_create("Core"); 200 | tcase_add_test(tc_core, pool); 201 | tcase_add_test(tc_core, slablist); 202 | tcase_add_test(tc_core, slab_used_bitmap); 203 | tcase_add_test(tc_core, slab_it); 204 | tcase_add_test(tc_core, slab_exceed_limit); 205 | suite_add_tcase(s, tc_core); 206 | 207 | return s; 208 | } 209 | -------------------------------------------------------------------------------- /c_src/test/suite.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | Suite * cherly_suite(); 6 | Suite * double_link_suite(); 7 | Suite * lru_suite(); 8 | Suite * slabs_suite(); 9 | 10 | int main (void) { 11 | int number_failed; 12 | 13 | SRunner *sr = srunner_create(cherly_suite()); 14 | srunner_add_suite(sr, double_link_suite()); 15 | srunner_add_suite(sr, lru_suite()); 16 | srunner_add_suite(sr, slabs_suite()); 17 | 18 | srunner_run_all(sr, CK_NORMAL); 19 | number_failed = srunner_ntests_failed(sr); 20 | srunner_free(sr); 21 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 22 | } 23 | -------------------------------------------------------------------------------- /c_src/type.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | #ifndef __TYPE_H__ 6 | #define __TYPE_H__ 7 | 8 | struct Type 9 | { 10 | uintptr size; 11 | Alg *alg; 12 | }; 13 | 14 | struct MapType 15 | { 16 | Type *key; 17 | Type *elem; 18 | }; 19 | #endif 20 | -------------------------------------------------------------------------------- /include/cherly.hrl: -------------------------------------------------------------------------------- 1 | %%====================================================================== 2 | %% 3 | %% Cherly 4 | %% 5 | %% Copyright (c) 2012 Stoic, Inc. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% --------------------------------------------------------------------- 22 | %% ECache 23 | %% @doc 24 | %% @end 25 | %%====================================================================== 26 | -author("Yosuke Hara"). 27 | 28 | 29 | %% records. 30 | -record(cache_stats, { 31 | gets = 0 :: integer(), 32 | puts = 0 :: integer(), 33 | dels = 0 :: integer(), 34 | hits = 0 :: integer(), 35 | records = 0 :: integer(), 36 | cached_size = 0 :: integer() 37 | }). 38 | 39 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leo-project/cherly/7f2b74f23863632911d2813c3aac94238475bf39/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {require_otp_vsn, "R14B04|R15B02|R15B03|R16B"}. 2 | 3 | {erl_opts, [{d, 'NOTEST'}, 4 | warn_obsolete_guard, 5 | warn_unused_import, 6 | warnings_as_errors, 7 | warn_shadow_vars, 8 | warn_export_vars, 9 | warn_export_all]}. 10 | {xref_checks, [undefined_function_calls]}. 11 | {cover_enabled, true}. 12 | {clean_files, []}. 13 | 14 | {port_specs, [ 15 | {"priv/cherly.so", ["c_src/*.c"]} 16 | %% 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"]} 17 | ]}. 18 | 19 | {port_env, [ 20 | {"CFLAGS", "$CFLAGS -fPIC -g -O2"}, 21 | {"DRV_CFLAGS", "$DRV_CFLAGS -Wall -Wno-unused-function -Wno-strict-aliasing -Wno-pointer-to-int-cast -Ic_src"}, 22 | {"DRV_LDFLAGS", "$DRV_LDFLAGS -lm"}, 23 | {"EXE_CFLAGS", "$EXE_CFLAGS -Wall -Wno-unused-function -Wno-strict-aliasing -Wno-pointer-to-int-cast -Ic_src/ -I/usr/include"}, 24 | {"EXE_LDFLAGS", "$EXE_LDFLAGS -lm -lcheck"} 25 | ]}. 26 | 27 | {post_hooks, [ 28 | %% for UT: {eunit, "c_src/test/suite"} 29 | ]}. 30 | -------------------------------------------------------------------------------- /rebar.config.develop: -------------------------------------------------------------------------------- 1 | {require_otp_vsn, "R14B04|R15B02|R15B03|R16B"}. 2 | 3 | {erl_opts, [{d, 'NOTEST'}, 4 | warn_obsolete_guard, 5 | warn_unused_import, 6 | warnings_as_errors, 7 | warn_shadow_vars, 8 | warn_export_vars, 9 | warn_export_all]}. 10 | {xref_checks, [undefined_function_calls]}. 11 | {cover_enabled, true}. 12 | {clean_files, []}. 13 | 14 | {port_specs, [ 15 | {"priv/cherly.so", ["c_src/*.c"]}, 16 | {"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"]} 17 | ]}. 18 | 19 | {port_env, [ 20 | {"CFLAGS", "$CFLAGS -fPIC -g -O2"}, 21 | {"DRV_CFLAGS", "$DRV_CFLAGS -Wall -Wno-unused-function -Wno-strict-aliasing -Wno-pointer-to-int-cast -Ic_src"}, 22 | {"DRV_LDFLAGS", "$DRV_LDFLAGS -lm"}, 23 | {"EXE_CFLAGS", "$EXE_CFLAGS -Wall -Wno-unused-function -Wno-strict-aliasing -Wno-pointer-to-int-cast -Ic_src/ -I/usr/include"}, 24 | {"EXE_LDFLAGS", "$EXE_LDFLAGS -lm -lcheck"} 25 | ]}. 26 | 27 | {post_hooks, [ 28 | {eunit, "c_src/test/suite"} 29 | ]}. 30 | -------------------------------------------------------------------------------- /src/cherly.app.src: -------------------------------------------------------------------------------- 1 | {application, cherly, 2 | [{description, "Cherly caching library"}, 3 | {mod, {cherly_app, []}}, 4 | {vsn, "0.12.8"}, 5 | {modules, [cherly, cherly_app]}, 6 | {registered, []}, 7 | {applications, [kernel, stdlib]} 8 | ]}. 9 | -------------------------------------------------------------------------------- /src/cherly.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% File: cherly.erl 3 | %%% @author Cliff Moon [] 4 | %%% @copyright 2009 Cliff Moon See LICENSE file 5 | %%% @doc 6 | %%% 7 | %%% @end 8 | %%% 9 | %%% @since 2009-02-22 by Cliff Moon 10 | %%% @since 2012-02-22 by Yoshiyuki Kanno 11 | %%%------------------------------------------------------------------- 12 | -module(cherly). 13 | -author('cliff@moonpolysoft.com'). 14 | -author('Yoshiyuki Kanno'). 15 | 16 | -export([start/1, put/3, get/2, remove/2, size/1, items/1, stop/1]). 17 | -on_load(init/0). 18 | 19 | 20 | %% @doc Initialize 21 | %% 22 | -spec(init() -> 23 | ok). 24 | init() -> 25 | SoName = case code:priv_dir(?MODULE) of 26 | {error, bad_name} -> 27 | case code:which(?MODULE) of 28 | Filename when is_list(Filename) -> 29 | filename:join([filename:dirname(Filename),"../priv", "cherly"]); 30 | _ -> 31 | filename:join("../priv", "cherly") 32 | end; 33 | Dir -> 34 | filename:join(Dir, "cherly") 35 | end, 36 | erlang:load_nif(SoName, 0). 37 | 38 | 39 | %% @doc Launch cherly 40 | %% 41 | -spec(start(integer()) -> 42 | {ok, any()}). 43 | start(_Size) -> 44 | exit(nif_library_not_loaded). 45 | 46 | 47 | %% @doc Insert an object into the cherly 48 | %% 49 | -spec(put(any(), binary(), binary()) -> 50 | ok | {error, any()}). 51 | put(_Res, _Key, _Value) -> 52 | exit(nif_library_not_loaded). 53 | 54 | 55 | %% @doc Retrieve an object from the cherly 56 | %% 57 | -spec(get(any(), binary()) -> 58 | {ok, binary()} | not_found | {error, any()}). 59 | get(_Res, _Key) -> 60 | exit(nif_library_not_loaded). 61 | 62 | %% @doc Remove an object from the cherly 63 | %% 64 | -spec(remove(any(), binary()) -> 65 | ok | {error, any()}). 66 | remove(_Res, _Key) -> 67 | exit(nif_library_not_loaded). 68 | 69 | 70 | %% @doc Retrieve size of cached objects 71 | %% 72 | -spec(size(any()) -> 73 | {ok, integer()} | {error, any()}). 74 | size(_Res) -> 75 | exit(nif_library_not_loaded). 76 | 77 | %% @doc Retrieve total of cached objects 78 | %% 79 | -spec(items(any()) -> 80 | {ok, integer()} | {error, any()}). 81 | items(_Res) -> 82 | exit(nif_library_not_loaded). 83 | 84 | 85 | %% @doc Halt the cherly 86 | %% 87 | -spec(stop(any()) -> 88 | ok | {error, any()}). 89 | stop(_Res) -> 90 | exit(nif_library_not_loaded). 91 | 92 | -------------------------------------------------------------------------------- /src/cherly_app.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% File: cherly_app.erl 3 | %%% @author Cliff Moon [] 4 | %%% @copyright 2009 Cliff Moon 5 | %%% @doc 6 | %%% 7 | %%% @end 8 | %%% 9 | %%% @since 2009-03-11 by Cliff Moon 10 | %%%------------------------------------------------------------------- 11 | -module(cherly_app). 12 | -author('cliff@powerset.com'). 13 | 14 | -behaviour(application). 15 | 16 | %% Application callbacks 17 | -export([start/2, stop/1]). 18 | 19 | %%==================================================================== 20 | %% Application callbacks 21 | %%==================================================================== 22 | %%-------------------------------------------------------------------- 23 | %% @spec start(Type, StartArgs) -> {ok, Pid} | 24 | %% {ok, Pid, State} | 25 | %% {error, Reason} 26 | %% @doc This function is called whenever an application 27 | %% is started using application:start/1,2, and should start the processes 28 | %% of the application. If the application is structured according to the 29 | %% OTP design principles as a supervision tree, this means starting the 30 | %% top supervisor of the tree. 31 | %% @end 32 | %%-------------------------------------------------------------------- 33 | start(_Type, []) -> 34 | cherly_sup:start_link(). 35 | 36 | %%-------------------------------------------------------------------- 37 | %% @spec stop(State) -> void() 38 | %% @doc This function is called whenever an application 39 | %% has stopped. It is intended to be the opposite of Module:start/2 and 40 | %% should do any necessary cleaning up. The return value is ignored. 41 | %% @end 42 | %%-------------------------------------------------------------------- 43 | stop({_, C}) -> 44 | cherly_sup:stop(), 45 | exit(C, shutdown), 46 | ok. 47 | -------------------------------------------------------------------------------- /src/cherly_server.erl: -------------------------------------------------------------------------------- 1 | %%====================================================================== 2 | %% 3 | %% Cherly 4 | %% 5 | %% Copyright (c) 2012 Rakuten, Inc. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% --------------------------------------------------------------------- 22 | %% Cherly Server 23 | %% @doc 24 | %% @end 25 | %%====================================================================== 26 | -module(cherly_server). 27 | -author("Yosuke Hara"). 28 | 29 | -behaviour(gen_server). 30 | 31 | -include("cherly.hrl"). 32 | -include_lib("eunit/include/eunit.hrl"). 33 | 34 | %% API 35 | -export([start_link/2, stop/1, 36 | get/2, put/3, delete/2, stats/1, items/1, size/1]). 37 | 38 | %% gen_server callbacks 39 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 40 | terminate/2, code_change/3]). 41 | 42 | -record(state, {handler, 43 | total_cache_size = 0 :: integer(), 44 | stats_gets = 0 :: integer(), 45 | stats_puts = 0 :: integer(), 46 | stats_dels = 0 :: integer(), 47 | stats_hits = 0 :: integer() 48 | }). 49 | 50 | %%-------------------------------------------------------------------- 51 | %% API 52 | %%-------------------------------------------------------------------- 53 | %% Function: {ok,Pid} | ignore | {error, Error} 54 | %% Description: Starts the server. 55 | start_link(Id, CacheSize) -> 56 | gen_server:start_link({local, Id}, ?MODULE, [CacheSize], []). 57 | 58 | 59 | %% Function: -> ok 60 | %% Description: Manually stops the server. 61 | stop(Pid) -> 62 | gen_server:cast(Pid, stop). 63 | 64 | 65 | %% @doc Retrieve a value associated with a specified key 66 | %% 67 | -spec(get(atom(), binary()) -> 68 | undefined | binary() | {error, any()}). 69 | get(Id, Key) -> 70 | gen_server:call(Id, {get, Key}). 71 | 72 | 73 | %% @doc Insert a key-value pair into the cherly 74 | %% 75 | -spec(put(atom(), binary(), binary()) -> 76 | ok | {error, any()}). 77 | put(Id, Key, Value) -> 78 | gen_server:call(Id, {put, Key, Value}). 79 | 80 | 81 | %% @doc Remove a key-value pair by a specified key into the cherly 82 | -spec(delete(atom(), binary()) -> 83 | ok | {error, any()}). 84 | delete(Id, Key) -> 85 | gen_server:call(Id, {delete, Key}). 86 | 87 | 88 | %% @doc Return server's state 89 | -spec(stats(atom()) -> 90 | any()). 91 | stats(Id) -> 92 | gen_server:call(Id, {stats}). 93 | 94 | 95 | %% @doc Return server's items 96 | -spec(items(atom()) -> 97 | any()). 98 | items(Id) -> 99 | gen_server:call(Id, {items}). 100 | 101 | 102 | %% @doc Return server's summary of cache size 103 | -spec(size(atom()) -> 104 | any()). 105 | size(Id) -> 106 | gen_server:call(Id, {size}). 107 | 108 | 109 | %%==================================================================== 110 | %% GEN_SERVER CALLBACKS 111 | %%==================================================================== 112 | init([CacheSize]) -> 113 | {ok, Handler} = cherly:start(CacheSize), 114 | {ok, #state{total_cache_size = CacheSize, 115 | handler = Handler}}. 116 | 117 | handle_call({get, Key}, _From, #state{handler = Handler, 118 | stats_gets = Gets, 119 | stats_hits = Hits} = State) -> 120 | case catch cherly:get(Handler, Key) of 121 | {ok, Value} -> 122 | {reply, {ok, Value}, State#state{stats_gets = Gets + 1, 123 | stats_hits = Hits + 1}}; 124 | not_found -> 125 | {reply, not_found, State#state{stats_gets = Gets + 1}}; 126 | {error, Cause} -> 127 | error_logger:error_msg("~p,~p,~p,~p~n", 128 | [{module, ?MODULE_STRING}, 129 | {function, "handle_call/3"}, 130 | {line, ?LINE}, {body, Cause}]), 131 | {reply, {error, Cause}, State}; 132 | {'EXIT', Cause} -> 133 | error_logger:error_msg("~p,~p,~p,~p~n", 134 | [{module, ?MODULE_STRING}, 135 | {function, "handle_call/3"}, 136 | {line, ?LINE}, {body, Cause}]), 137 | {reply, {error, Cause}, State} 138 | end; 139 | 140 | handle_call({put, Key, Val}, _From, #state{handler = Handler, 141 | stats_puts = Puts} = State) -> 142 | case catch cherly:put(Handler, Key, Val) of 143 | ok -> 144 | {reply, ok, State#state{stats_puts = Puts + 1}}; 145 | {'EXIT', Cause} -> 146 | error_logger:error_msg("~p,~p,~p,~p~n", 147 | [{module, ?MODULE_STRING}, 148 | {function, "handle_call/3"}, 149 | {line, ?LINE}, {body, Cause}]), 150 | {reply, {error, Cause}, State}; 151 | {error, Cause} -> 152 | error_logger:error_msg("~p,~p,~p,~p~n", 153 | [{module, ?MODULE_STRING}, 154 | {function, "handle_call/3"}, 155 | {line, ?LINE}, {body, Cause}]), 156 | {reply, {error, Cause}, State} 157 | end; 158 | 159 | handle_call({delete, Key}, _From, State = #state{handler = Handler, 160 | stats_dels = Dels}) -> 161 | case catch cherly:remove(Handler, Key) of 162 | ok -> 163 | {reply, ok, State#state{stats_dels = Dels + 1}}; 164 | {'EXIT', Cause} -> 165 | error_logger:error_msg("~p,~p,~p,~p~n", 166 | [{module, ?MODULE_STRING}, 167 | {function, "handle_call/3"}, 168 | {line, ?LINE}, {body, Cause}]), 169 | {reply, {error, Cause}, State}; 170 | {error, Cause} -> 171 | error_logger:error_msg("~p,~p,~p,~p~n", 172 | [{module, ?MODULE_STRING}, 173 | {function, "handle_call/3"}, 174 | {line, ?LINE}, {body, Cause}]), 175 | {reply, {error, Cause}, State} 176 | end; 177 | 178 | handle_call({stats}, _From, State = #state{handler = Handler, 179 | stats_hits = Hits, 180 | stats_gets = Gets, 181 | stats_puts = Puts, 182 | stats_dels = Dels}) -> 183 | {ok, Items} = cherly:items(Handler), 184 | {ok, Size} = cherly:size(Handler), 185 | Stats = #cache_stats{hits = Hits, 186 | gets = Gets, 187 | puts = Puts, 188 | dels = Dels, 189 | records = Items, 190 | cached_size = Size}, 191 | {reply, {ok, Stats}, State}; 192 | 193 | handle_call({items}, _From, #state{handler = Handler} = State) -> 194 | Reply = cherly:items(Handler), 195 | {reply, Reply, State}; 196 | 197 | handle_call({size}, _From, #state{handler = Handler} = State) -> 198 | Reply = cherly:size(Handler), 199 | {reply, Reply, State}; 200 | 201 | handle_call(_Request, _From, State) -> 202 | {reply, undefined, State}. 203 | 204 | handle_cast(stop, State) -> 205 | {stop, normal, State}; 206 | 207 | handle_cast(_Msg, State) -> 208 | {noreply, State}. 209 | 210 | handle_info(_Info, State) -> 211 | {noreply, State}. 212 | 213 | 214 | %% ---------------------------------------------------------------------------------------------------------- 215 | %% Function: terminate(Reason, State) -> void() 216 | %% Description: This function is called by a gen_server when it is about to terminate. When it returns, 217 | %% the gen_server terminates with Reason. The return value is ignored. 218 | %% ---------------------------------------------------------------------------------------------------------- 219 | terminate(_Reason, _State) -> 220 | terminated. 221 | 222 | 223 | %% ---------------------------------------------------------------------------------------------------------- 224 | %% Function: code_change(OldVsn, State, Extra) -> {ok, NewState} 225 | %% Description: Convert process state when code is changed. 226 | %% ---------------------------------------------------------------------------------------------------------- 227 | code_change(_OldVsn, State, _Extra) -> 228 | {ok, State}. 229 | 230 | -------------------------------------------------------------------------------- /src/cherly_sup.erl: -------------------------------------------------------------------------------- 1 | %%====================================================================== 2 | %% 3 | %% Cherly 4 | %% 5 | %% Copyright (c) 2012 Rakuten, Inc. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% --------------------------------------------------------------------- 22 | %% Cherly Supervisor 23 | %% @doc 24 | %% @end 25 | %%====================================================================== 26 | -module(cherly_sup). 27 | -author("Yosuke Hara"). 28 | 29 | -behaviour(supervisor). 30 | 31 | -include_lib("eunit/include/eunit.hrl"). 32 | 33 | %% External API 34 | -export([start_link/0, stop/0]). 35 | 36 | %% Callbacks 37 | -export([init/1]). 38 | 39 | -define(MAX_RESTART, 5). 40 | -define(MAX_TIME, 60). 41 | -define(SHUTDOWN_WAITING_TIME, 2000). 42 | 43 | -ifdef(TEST). 44 | -define(DEF_TOTA_CACHE_SIZE, 1024 * 1024). %% 1MB 45 | -else. 46 | -define(DEF_TOTA_CACHE_SIZE, 1024 * 1024 * 1024). %% 1GB 47 | -endif. 48 | 49 | 50 | %%----------------------------------------------------------------------- 51 | %% External API 52 | %%----------------------------------------------------------------------- 53 | %% @spec (Params) -> ok 54 | %% @doc start link. 55 | %% @end 56 | start_link() -> 57 | TotalCacheSize = case application:get_env(cherly, total_cache_size) of 58 | {ok, Value1} when is_integer(Value1) -> 59 | Value1; 60 | _ -> 61 | ?DEF_TOTA_CACHE_SIZE 62 | end, 63 | supervisor:start_link({local, ?MODULE}, ?MODULE, [TotalCacheSize]). 64 | 65 | 66 | %% @spec () -> ok | 67 | %% not_started 68 | %% @doc stop process. 69 | %% @end 70 | stop() -> 71 | case whereis(?MODULE) of 72 | Pid when is_pid(Pid) == true -> 73 | exit(Pid, shutdown), 74 | ok; 75 | _ -> not_started 76 | end. 77 | 78 | 79 | %% --------------------------------------------------------------------- 80 | %% Callbacks 81 | %% --------------------------------------------------------------------- 82 | %% @spec (Params) -> ok 83 | %% @doc stop process. 84 | %% @end 85 | %% @private 86 | init([TotalCacheSize]) -> 87 | {ok, {{one_for_one, ?MAX_RESTART, ?MAX_TIME}, 88 | [{cherly_server, {cherly_server, start_link, [TotalCacheSize]}, 89 | permanent, ?SHUTDOWN_WAITING_TIME, worker, [cherly_server]}]}}. 90 | 91 | -------------------------------------------------------------------------------- /test/basho_bench_driver_cherly.config: -------------------------------------------------------------------------------- 1 | {mode, max}. 2 | {duration, 10}. 3 | {concurrent, 1}. 4 | 5 | {driver, basho_bench_driver_cherly}. 6 | 7 | {key_generator, {int_to_bin,{uniform_int, 5000000}}}. 8 | {value_generator, {fixed_bin, 1024}}. 9 | 10 | {operations, [{get, 1}, {put, 1}]}. 11 | 12 | {code_paths, ["deps/cherly"]}. 13 | 14 | {cache_capacity, 1000000000}. 15 | 16 | -------------------------------------------------------------------------------- /test/basho_bench_driver_cherly.erl: -------------------------------------------------------------------------------- 1 | %% ------------------------------------------------------------------- 2 | %% 3 | %% Cherly - Benchmarking Suite 4 | %% 5 | %% Copyright (c) 2012 Rakuten, Inc. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | -module(basho_bench_driver_cherly). 23 | -author("Yosuke Hara"). 24 | 25 | -export([new/1, 26 | run/4]). 27 | 28 | -record(state, {handler :: binary(), check_integrity :: boolean()}). 29 | 30 | %% ==================================================================== 31 | %% API 32 | %% ==================================================================== 33 | 34 | new(_Id) -> 35 | case code:which(cherly) of 36 | non_existing -> 37 | io:format("Cherly-benchmark requires cherly to be available on code path.\n"); 38 | _ -> 39 | void 40 | end, 41 | 42 | CacheCapacity = basho_bench_config:get( 43 | cache_capacity, 1073741824), %% default:1GB 44 | io:format("Cache capacity: ~w\n", [CacheCapacity]), 45 | CI = basho_bench_config:get( 46 | check_integrity, false), %% should be false when doing benchmark 47 | io:format("Check Integrity: ~p\n", [CI]), 48 | 49 | {ok, C} = cherly:start(CacheCapacity), 50 | {ok, #state{handler = C, check_integrity = CI}}. 51 | 52 | 53 | run(get, KeyGen, _ValueGen, #state{handler = C, check_integrity = CI} = State) -> 54 | Key = KeyGen(), 55 | case cherly:get(C, Key) of 56 | {ok, Value} -> 57 | case CI of 58 | true -> 59 | LocalMD5 = erlang:get(Key), 60 | RemoteMD5 = erlang:md5(Value), 61 | case RemoteMD5 =:= LocalMD5 of 62 | true -> {ok, State}; 63 | false -> {error, checksum_error} 64 | end; 65 | false -> {ok, State} 66 | end; 67 | not_found -> 68 | {ok, State}; 69 | {error, Reason} -> 70 | {error, Reason, State} 71 | end; 72 | 73 | run(put, KeyGen, ValueGen, #state{handler = C, check_integrity = CI} = State) -> 74 | Key = KeyGen(), 75 | Val = ValueGen(), 76 | case cherly:put(C, Key, Val) of 77 | ok -> 78 | case CI of 79 | true -> 80 | LocalMD5 = erlang:md5(Val), 81 | erlang:put(Key, LocalMD5); 82 | false -> void 83 | end, 84 | {ok, State}; 85 | {error, Reason} -> 86 | {error, Reason, State}; 87 | Other -> 88 | io:format("put unexpected result:~p \n", [Other]), 89 | {error, Other, State} 90 | end. 91 | 92 | -------------------------------------------------------------------------------- /test/test_cherly.erl: -------------------------------------------------------------------------------- 1 | %%==================================================================== 2 | %% 3 | %% Cherly 4 | %% 5 | %% Copyright (c) 2012 Stoic, Inc. 6 | %% 7 | %% This file is provided to you under the Apache License, 8 | %% Version 2.0 (the "License"); you may not use this file 9 | %% except in compliance with the License. You may obtain 10 | %% a copy of the License at 11 | %% 12 | %% http://www.apache.org/licenses/LICENSE-2.0 13 | %% 14 | %% Unless required by applicable law or agreed to in writing, 15 | %% software distributed under the License is distributed on an 16 | %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 | %% KIND, either express or implied. See the License for the 18 | %% specific language governing permissions and limitations 19 | %% under the License. 20 | %% 21 | %% ------------------------------------------------------------------- 22 | %% Cherly Unit Test 23 | %% @doc 24 | %% @end 25 | %%==================================================================== 26 | -module(test_cherly). 27 | -author('Yoshiyuki Kanno'). 28 | -author('Yosuke Hara'). 29 | 30 | -include("cherly.hrl"). 31 | -include_lib("eunit/include/eunit.hrl"). 32 | 33 | -export([succ/1, fast_acc/3, time_to_epoch_float/1]). 34 | 35 | 36 | %%-------------------------------------------------------------------- 37 | %% TEST FUNCTIONS 38 | %%-------------------------------------------------------------------- 39 | -ifdef(EUNIT). 40 | 41 | simple_test() -> 42 | {ok, C} = cherly:start(120), 43 | K = <<"key">>, 44 | V = <<"value">>, 45 | Len = byte_size(K) + byte_size(V), 46 | cherly:put(C, K, V), 47 | 48 | ?assertEqual({ok, V}, cherly:get(C, <<"key">>)), 49 | ?assertEqual({ok, Len}, cherly:size(C)), 50 | cherly:stop(C). 51 | 52 | put_plural_objects_test() -> 53 | {ok, C} = cherly:start(10000), 54 | Keys = ["A","B","C","D","E","F", 55 | "G","H","I","J","K","L", 56 | "M","N","O","P","Q","R", 57 | "S","T","U","V","W","X", 58 | "Y","Z","1","2","3","4", 59 | "5","6","7","8","9","0"], 60 | lists:foreach(fun(K) -> 61 | cherly:put(C, list_to_binary(K), <<"LEOFS">>) 62 | end, Keys), 63 | lists:foreach(fun(K) -> 64 | {ok, <<"LEOFS">>} = cherly:get(C, list_to_binary(K)) 65 | end, Keys), 66 | 67 | Items = length(Keys), 68 | Size = Items + (Items * 5), 69 | 70 | ?assertEqual({ok, Items}, cherly:items(C)), 71 | ?assertEqual({ok, Size}, cherly:size(C)), 72 | cherly:stop(C). 73 | 74 | put_term_key_test() -> 75 | {ok, C} = cherly:start(1000), 76 | K = term_to_binary({1234567890, "server/erlang"}), 77 | V = <<"LEOFS">>, 78 | Len = byte_size(K) + byte_size(V), 79 | 80 | ok = cherly:put(C, K, V), 81 | {ok, V} = cherly:get(C, K), 82 | 83 | ?assertEqual({ok, 1}, cherly:items(C)), 84 | ?assertEqual({ok, Len}, cherly:size(C)), 85 | cherly:stop(C). 86 | 87 | put_including_null_key_test() -> 88 | {ok, C} = cherly:start(1000), 89 | H = <<"abcdefghijklmnopqrstuvwxyz">>, 90 | T = <<0:64>>, 91 | K = <>, 92 | V = <<"LEOFS">>, 93 | Len = byte_size(K) + byte_size(V), 94 | 95 | ok = cherly:put(C, K, V), 96 | {ok, V} = cherly:get(C, K), 97 | 98 | ?assertEqual({ok, 1}, cherly:items(C)), 99 | ?assertEqual({ok, Len}, cherly:size(C)), 100 | cherly:stop(C). 101 | 102 | put_get_and_remove_test() -> 103 | {ok, C} = cherly:start(120), 104 | K = <<"key">>, 105 | V = <<"value">>, 106 | 107 | ?assertEqual(not_found, cherly:get(C, K)), 108 | cherly:put(C, K, V), 109 | ?assertEqual({ok, V}, cherly:get(C, K)), 110 | cherly:remove(C, K), 111 | ?assertEqual(not_found, cherly:get(C, K)), 112 | ?assertEqual({ok, 0}, cherly:size(C)), 113 | cherly:stop(C). 114 | 115 | put_with_lru_eject_test() -> 116 | {ok, C} = cherly:start(70), 117 | V = <<"value">>, 118 | lists:foldl(fun(_, Str) -> 119 | Mod = list_to_binary(succ(Str)), 120 | ?debugVal(Mod), 121 | cherly:put(C, Mod, V), 122 | binary_to_list(Mod) 123 | end, "abc", lists:seq(1, 10)), 124 | ?debugVal(cherly:size(C)), 125 | %% ?assertEqual({ok, 8}, cherly:items(C)), 126 | cherly:stop(C). 127 | 128 | what_goes_in_must_come_out_test() -> 129 | {ok, C} = cherly:start(120), 130 | K = <<"key">>, 131 | 132 | cherly:put(C, K, list_to_binary([<<"val1">>, <<"val2">>])), 133 | ?assertEqual({ok, list_to_binary([<<"val1">>, <<"val2">>])}, cherly:get(C, K)), 134 | cherly:stop(C). 135 | 136 | big_stuff_that_goes_in_must_come_out_test() -> 137 | {ok, C} = cherly:start(1048576), 138 | K = <<"key">>, 139 | V1 = <<0:524288>>, 140 | V2 = <<1:524288>>, 141 | 142 | cherly:put(C, K, list_to_binary([V1, V2])), 143 | {ok, Ret} = cherly:get(C, K), 144 | ?assertEqual(list_to_binary([V1,V2]), Ret), 145 | cherly:stop(C). 146 | 147 | put_one_thing_in_no_list_big_test() -> 148 | {ok, C} = cherly:start(1048576), 149 | K = <<"key">>, 150 | V = <<0:524288>>, 151 | 152 | cherly:put(C, K, V), 153 | ?assertEqual({ok, V}, cherly:get(C, K)), 154 | cherly:stop(C). 155 | 156 | put_one_thing_in_no_list_small_test() -> 157 | {ok, C} = cherly:start(1048576), 158 | K = <<"key">>, 159 | V = <<1:8>>, 160 | cherly:put(C, K, V), 161 | ?assertEqual({ok, V}, cherly:get(C, K)), 162 | cherly:stop(C). 163 | 164 | remove_nonexistant_test() -> 165 | {ok, C} = cherly:start(120), 166 | K = <<"key">>, 167 | 168 | cherly:remove(C, K), 169 | ?assertEqual(not_found, cherly:get(C, K)), 170 | cherly:stop(C). 171 | 172 | put_bigger_thing_than_1MB_test() -> 173 | {ok, C} = cherly:start(1024 * 1024 * 5), 174 | K = <<"key">>, 175 | V = crypto:rand_bytes(1024 * 1024 * 2), 176 | cherly:put(C, K, V), 177 | cherly:remove(C, K), 178 | ?assertEqual(not_found, cherly:get(C, K)), 179 | {ok, 0} = cherly:items(C), 180 | {ok, 0} = cherly:size(C), 181 | cherly:stop(C). 182 | 183 | double_get_test() -> 184 | %% outputv modifies the iovec with a skipsize. That's fucking rad 185 | {ok, C} = cherly:start(1123123), 186 | K = <<"aczup">>, 187 | V = list_to_binary([<<131,108,0,0,0,1,104,2,107,0,9,60,48,46,52,55,50,46,48, 188 | 62,99,49,46,50,51,54,53,51,49,53,54,49,57,53,57,56,55, 189 | 50,57,54,49,48,52,101,43,48,57,0,0,0,0,0,106>>, 190 | <<235,105,34,223,191,105,56,25,199,24,148,52,180,112, 191 | 198,246,56,150,15,175,56,34,38,120,99,41,59,53,204, 192 | 233,41,246,189,135,39,171,124,233,143,40,108,119,63, 193 | 130,237,8,121,35,97,121,172,20,149,241,129,191,2,211, 194 | 151,167,0,102,103,63,242,240,41,83,150,211,189,32,56, 195 | 65,217,241,234,237,58,216,34,245,253,153,140,190,186, 196 | 24,147,240,181,63,222,161,13,217,55,232,254,148>>]), 197 | cherly:put(C, K, V), 198 | ?assertEqual({ok, V}, cherly:get(C, K)), 199 | ?assertEqual({ok, V}, cherly:get(C, K)), 200 | cherly:stop(C). 201 | 202 | server_test() -> 203 | K = <<"KEY-1">>, 204 | V = <<"VALUE-1">>, 205 | 206 | ProcId = 'test_cherly', 207 | cherly_server:start_link(ProcId, (1024 * 1024)), 208 | ok = cherly_server:put(ProcId, K, V), 209 | {ok, V} = cherly_server:get(ProcId, K), 210 | {ok, 1} = cherly_server:items(ProcId), 211 | {ok, 12} = cherly_server:size(ProcId), 212 | 213 | {ok, Stats1} = cherly_server:stats(ProcId), 214 | ?assertEqual(#cache_stats{gets = 1, 215 | puts = 1, 216 | dels = 0, 217 | hits = 1, 218 | records = 1, 219 | cached_size = 12}, Stats1), 220 | 221 | ok = cherly_server:delete(ProcId, K), 222 | {ok, Stats2} = cherly_server:stats(ProcId), 223 | ?assertEqual(#cache_stats{gets = 1, 224 | puts = 1, 225 | dels = 1, 226 | hits = 1, 227 | records = 0, 228 | cached_size = 0}, Stats2), 229 | 230 | {ok, 0} = cherly_server:items(ProcId), 231 | {ok, 0} = cherly_server:size(ProcId), 232 | 233 | cherly_server:stop(ProcId), 234 | ok. 235 | 236 | 237 | %%-------------------------------------------------------------------- 238 | %% INNER FUNCTIONS 239 | %%-------------------------------------------------------------------- 240 | succ([]) -> 241 | []; 242 | succ(Str) -> 243 | succ_int(lists:reverse(Str), []). 244 | 245 | 246 | succ_int([Char|Str], Acc) -> 247 | if 248 | Char >= $z -> succ_int(Str, [$a|Acc]); 249 | true -> lists:reverse(lists:reverse([Char+1|Acc]) ++ Str) 250 | end. 251 | 252 | fast_acc(_, Acc, 0) -> Acc; 253 | fast_acc(Fun, Acc, N) -> 254 | fast_acc(Fun, Fun(Acc), N-1). 255 | 256 | 257 | time_to_epoch_float({Mega,Sec,Micro}) -> 258 | Mega * 1000000 + Sec + Micro / 1000000. 259 | 260 | -endif. 261 | 262 | --------------------------------------------------------------------------------