erlang各种有用的函数包括一些有用nif封装,还有一些性能测试case。
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

658 rindas
14 KiB

pirms 5 gadiem
  1. // This file is part of khash released under the MIT license.
  2. // See the LICENSE file for more information.
  3. // Copyright 2013 Cloudant, Inc <support@cloudant.com>
  4. #include <assert.h>
  5. #include <string.h>
  6. #include <stdint.h>
  7. #include "erl_nif.h"
  8. #include "hash.h"
  9. #ifdef _WIN32
  10. #define INLINE __inline
  11. #else
  12. #define INLINE inline
  13. #endif
  14. #define KHASH_VERSION 0
  15. typedef struct
  16. {
  17. ERL_NIF_TERM atom_ok;
  18. ERL_NIF_TERM atom_error;
  19. ERL_NIF_TERM atom_value;
  20. ERL_NIF_TERM atom_not_found;
  21. ERL_NIF_TERM atom_end_of_table;
  22. ERL_NIF_TERM atom_expired_iterator;
  23. ErlNifResourceType* res_hash;
  24. ErlNifResourceType* res_iter;
  25. } khash_priv;
  26. typedef struct
  27. {
  28. unsigned int hval;
  29. ErlNifEnv* env;
  30. ERL_NIF_TERM key;
  31. ERL_NIF_TERM val;
  32. } khnode_t;
  33. typedef struct
  34. {
  35. int version;
  36. unsigned int gen;
  37. hash_t* h;
  38. ErlNifPid p;
  39. } khash_t;
  40. typedef struct
  41. {
  42. int version;
  43. unsigned int gen;
  44. khash_t* khash;
  45. hscan_t scan;
  46. } khash_iter_t;
  47. static INLINE ERL_NIF_TERM
  48. make_atom(ErlNifEnv* env, const char* name)
  49. {
  50. ERL_NIF_TERM ret;
  51. if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) {
  52. return ret;
  53. }
  54. return enif_make_atom(env, name);
  55. }
  56. static INLINE ERL_NIF_TERM
  57. make_ok(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM value)
  58. {
  59. return enif_make_tuple2(env, priv->atom_ok, value);
  60. }
  61. static INLINE ERL_NIF_TERM
  62. make_error(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM reason)
  63. {
  64. return enif_make_tuple2(env, priv->atom_error, reason);
  65. }
  66. static INLINE int
  67. check_pid(ErlNifEnv* env, khash_t* khash)
  68. {
  69. ErlNifPid pid;
  70. enif_self(env, &pid);
  71. if(enif_compare(pid.pid, khash->p.pid) == 0) {
  72. return 1;
  73. }
  74. return 0;
  75. }
  76. hnode_t*
  77. khnode_alloc(void* ctx)
  78. {
  79. hnode_t* ret = (hnode_t*) enif_alloc(sizeof(hnode_t));
  80. khnode_t* node = (khnode_t*) enif_alloc(sizeof(khnode_t));
  81. memset(ret, '\0', sizeof(hnode_t));
  82. memset(node, '\0', sizeof(khnode_t));
  83. node->env = enif_alloc_env();
  84. ret->hash_key = node;
  85. return ret;
  86. }
  87. void
  88. khnode_free(hnode_t* obj, void* ctx)
  89. {
  90. khnode_t* node = (khnode_t*) kl_hnode_getkey(obj);
  91. enif_free_env(node->env);
  92. enif_free(node);
  93. enif_free(obj);
  94. return;
  95. }
  96. int
  97. khash_cmp_fun(const void* l, const void* r)
  98. {
  99. khnode_t* left = (khnode_t*) l;
  100. khnode_t* right = (khnode_t*) r;
  101. int cmp = enif_compare(left->key, right->key);
  102. if(cmp < 0) {
  103. return -1;
  104. } else if(cmp == 0) {
  105. return 0;
  106. } else {
  107. return 1;
  108. }
  109. }
  110. hash_val_t
  111. khash_hash_fun(const void* obj)
  112. {
  113. khnode_t* node = (khnode_t*) obj;
  114. return (hash_val_t) node->hval;
  115. }
  116. static INLINE khash_t*
  117. khash_create_int(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM opts)
  118. {
  119. khash_t* khash = NULL;
  120. assert(priv != NULL && "missing private data member");
  121. khash = (khash_t*) enif_alloc_resource(priv->res_hash, sizeof(khash_t));
  122. memset(khash, '\0', sizeof(khash_t));
  123. khash->version = KHASH_VERSION;
  124. khash->gen = 0;
  125. khash->h = kl_hash_create(HASHCOUNT_T_MAX, khash_cmp_fun, khash_hash_fun);
  126. if(khash->h == NULL ) {
  127. enif_release_resource(khash);
  128. return NULL;
  129. }
  130. kl_hash_set_allocator(khash->h, khnode_alloc, khnode_free, NULL);
  131. enif_self(env, &(khash->p));
  132. return khash;
  133. }
  134. static ERL_NIF_TERM
  135. khash_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  136. {
  137. khash_priv* priv = enif_priv_data(env);
  138. khash_t* khash;
  139. ERL_NIF_TERM ret;
  140. if(argc != 1) {
  141. return enif_make_badarg(env);
  142. }
  143. khash = khash_create_int(env, priv, argv[0]);
  144. if(khash == NULL) {
  145. return enif_make_badarg(env);
  146. }
  147. ret = enif_make_resource(env, khash);
  148. enif_release_resource(khash);
  149. return make_ok(env, priv, ret);
  150. }
  151. static void
  152. khash_free(ErlNifEnv* env, void* obj)
  153. {
  154. khash_t* khash = (khash_t*) obj;
  155. if(khash->h != NULL) {
  156. kl_hash_free_nodes(khash->h);
  157. kl_hash_destroy(khash->h);
  158. }
  159. return;
  160. }
  161. static ERL_NIF_TERM
  162. khash_to_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  163. {
  164. khash_priv* priv = (khash_priv*) enif_priv_data(env);
  165. ERL_NIF_TERM ret = enif_make_list(env, 0);
  166. khash_t* khash = NULL;
  167. void* res = NULL;
  168. hscan_t scan;
  169. hnode_t* entry;
  170. khnode_t* node;
  171. ERL_NIF_TERM key;
  172. ERL_NIF_TERM val;
  173. ERL_NIF_TERM tuple;
  174. if(argc != 1) {
  175. return enif_make_badarg(env);
  176. }
  177. if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) {
  178. return enif_make_badarg(env);
  179. }
  180. khash = (khash_t*) res;
  181. if(!check_pid(env, khash)) {
  182. return enif_make_badarg(env);
  183. }
  184. kl_hash_scan_begin(&scan, khash->h);
  185. while((entry = kl_hash_scan_next(&scan)) != NULL) {
  186. node = (khnode_t*) kl_hnode_getkey(entry);
  187. key = enif_make_copy(env, node->key);
  188. val = enif_make_copy(env, node->val);
  189. tuple = enif_make_tuple2(env, key, val);
  190. ret = enif_make_list_cell(env, tuple, ret);
  191. }
  192. return ret;
  193. }
  194. static ERL_NIF_TERM
  195. khash_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  196. {
  197. khash_priv* priv = enif_priv_data(env);
  198. khash_t* khash = NULL;
  199. void* res = NULL;
  200. if(argc != 1) {
  201. return enif_make_badarg(env);
  202. }
  203. if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) {
  204. return enif_make_badarg(env);
  205. }
  206. khash = (khash_t*) res;
  207. if(!check_pid(env, khash)) {
  208. return enif_make_badarg(env);
  209. }
  210. kl_hash_free_nodes(khash->h);
  211. khash->gen += 1;
  212. return priv->atom_ok;
  213. }
  214. static INLINE hnode_t*
  215. khash_lookup_int(ErlNifEnv* env, uint32_t hv, ERL_NIF_TERM key, khash_t* khash)
  216. {
  217. khnode_t node;
  218. node.hval = hv;
  219. node.env = env;
  220. node.key = key;
  221. return kl_hash_lookup(khash->h, &node);
  222. }
  223. static ERL_NIF_TERM
  224. khash_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  225. {
  226. khash_priv* priv = enif_priv_data(env);
  227. khash_t* khash = NULL;
  228. void* res = NULL;
  229. uint32_t hval;
  230. hnode_t* entry;
  231. khnode_t* node;
  232. ERL_NIF_TERM ret;
  233. if(argc != 3) {
  234. return enif_make_badarg(env);
  235. }
  236. if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) {
  237. return enif_make_badarg(env);
  238. }
  239. khash = (khash_t*) res;
  240. if(!check_pid(env, khash)) {
  241. return enif_make_badarg(env);
  242. }
  243. if(!enif_get_uint(env, argv[1], &hval)) {
  244. return enif_make_badarg(env);
  245. }
  246. entry = khash_lookup_int(env, hval, argv[2], khash);
  247. if(entry == NULL) {
  248. ret = priv->atom_not_found;
  249. } else {
  250. node = (khnode_t*) kl_hnode_getkey(entry);
  251. ret = enif_make_copy(env, node->val);
  252. ret = enif_make_tuple2(env, priv->atom_value, ret);
  253. }
  254. return ret;
  255. }
  256. static ERL_NIF_TERM
  257. khash_get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  258. {
  259. khash_priv* priv = enif_priv_data(env);
  260. khash_t* khash = NULL;
  261. void* res = NULL;
  262. uint32_t hval;
  263. hnode_t* entry;
  264. khnode_t* node;
  265. ERL_NIF_TERM ret;
  266. if(argc != 4) {
  267. return enif_make_badarg(env);
  268. }
  269. if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) {
  270. return enif_make_badarg(env);
  271. }
  272. khash = (khash_t*) res;
  273. if(!check_pid(env, khash)) {
  274. return enif_make_badarg(env);
  275. }
  276. if(!enif_get_uint(env, argv[1], &hval)) {
  277. return enif_make_badarg(env);
  278. }
  279. entry = khash_lookup_int(env, hval, argv[2], khash);
  280. if(entry == NULL) {
  281. ret = argv[3];
  282. } else {
  283. node = (khnode_t*) kl_hnode_getkey(entry);
  284. ret = enif_make_copy(env, node->val);
  285. }
  286. return ret;
  287. }
  288. static ERL_NIF_TERM
  289. khash_put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  290. {
  291. khash_priv* priv = enif_priv_data(env);
  292. khash_t* khash = NULL;
  293. void* res = NULL;
  294. uint32_t hval;
  295. hnode_t* entry;
  296. khnode_t* node;
  297. if(argc != 4) {
  298. return enif_make_badarg(env);
  299. }
  300. if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) {
  301. return enif_make_badarg(env);
  302. }
  303. khash = (khash_t*) res;
  304. if(!check_pid(env, khash)) {
  305. return enif_make_badarg(env);
  306. }
  307. if(!enif_get_uint(env, argv[1], &hval)) {
  308. return enif_make_badarg(env);
  309. }
  310. entry = khash_lookup_int(env, hval, argv[2], khash);
  311. if(entry == NULL) {
  312. entry = khnode_alloc(NULL);
  313. node = (khnode_t*) kl_hnode_getkey(entry);
  314. node->hval = hval;
  315. node->key = enif_make_copy(node->env, argv[2]);
  316. node->val = enif_make_copy(node->env, argv[3]);
  317. kl_hash_insert(khash->h, entry, node);
  318. } else {
  319. node = (khnode_t*) kl_hnode_getkey(entry);
  320. enif_clear_env(node->env);
  321. node->key = enif_make_copy(node->env, argv[2]);
  322. node->val = enif_make_copy(node->env, argv[3]);
  323. }
  324. khash->gen += 1;
  325. return priv->atom_ok;
  326. }
  327. static ERL_NIF_TERM
  328. khash_del(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  329. {
  330. khash_priv* priv = enif_priv_data(env);
  331. khash_t* khash = NULL;
  332. void* res = NULL;
  333. uint32_t hval;
  334. hnode_t* entry;
  335. ERL_NIF_TERM ret;
  336. if(argc != 3) {
  337. return enif_make_badarg(env);
  338. }
  339. if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) {
  340. return enif_make_badarg(env);
  341. }
  342. khash = (khash_t*) res;
  343. if(!check_pid(env, khash)) {
  344. return enif_make_badarg(env);
  345. }
  346. if(!enif_get_uint(env, argv[1], &hval)) {
  347. return enif_make_badarg(env);
  348. }
  349. entry = khash_lookup_int(env, hval, argv[2], khash);
  350. if(entry == NULL) {
  351. ret = priv->atom_not_found;
  352. } else {
  353. kl_hash_delete_free(khash->h, entry);
  354. ret = priv->atom_ok;
  355. }
  356. khash->gen += 1;
  357. return ret;
  358. }
  359. static ERL_NIF_TERM
  360. khash_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  361. {
  362. khash_priv* priv = enif_priv_data(env);
  363. khash_t* khash;
  364. if(argc != 1) {
  365. return enif_make_badarg(env);
  366. }
  367. if(!enif_get_resource(env, argv[0], priv->res_hash, (void*) &khash)) {
  368. return enif_make_badarg(env);
  369. }
  370. if(!check_pid(env, khash)) {
  371. return enif_make_badarg(env);
  372. }
  373. return enif_make_uint64(env, kl_hash_count(khash->h));
  374. }
  375. static ERL_NIF_TERM
  376. khash_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  377. {
  378. khash_priv* priv = enif_priv_data(env);
  379. khash_t* khash = NULL;
  380. void* res = NULL;
  381. khash_iter_t* iter;
  382. ERL_NIF_TERM ret;
  383. if(argc != 1) {
  384. return enif_make_badarg(env);
  385. }
  386. if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) {
  387. return enif_make_badarg(env);
  388. }
  389. khash = (khash_t*) res;
  390. if(!check_pid(env, khash)) {
  391. return enif_make_badarg(env);
  392. }
  393. iter = (khash_iter_t*) enif_alloc_resource(
  394. priv->res_iter, sizeof(khash_iter_t));
  395. memset(iter, '\0', sizeof(khash_iter_t));
  396. iter->version = KHASH_VERSION;
  397. iter->gen = khash->gen;
  398. iter->khash = khash;
  399. kl_hash_scan_begin(&(iter->scan), iter->khash->h);
  400. // The iterator needs to guarantee that the khash
  401. // remains alive for the life of the iterator.
  402. enif_keep_resource(khash);
  403. ret = enif_make_resource(env, iter);
  404. enif_release_resource(iter);
  405. return make_ok(env, priv, ret);
  406. }
  407. static void
  408. khash_iter_free(ErlNifEnv* env, void* obj)
  409. {
  410. khash_iter_t* iter = (khash_iter_t*) obj;
  411. enif_release_resource(iter->khash);
  412. }
  413. static ERL_NIF_TERM
  414. khash_iter_next(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  415. {
  416. khash_priv* priv = enif_priv_data(env);
  417. khash_iter_t* iter = NULL;
  418. void* res = NULL;
  419. hnode_t* entry;
  420. khnode_t* node;
  421. ERL_NIF_TERM key;
  422. ERL_NIF_TERM val;
  423. if(argc != 1) {
  424. return enif_make_badarg(env);
  425. }
  426. if(!enif_get_resource(env, argv[0], priv->res_iter, &res)) {
  427. return enif_make_badarg(env);
  428. }
  429. iter = (khash_iter_t*) res;
  430. if(!check_pid(env, iter->khash)) {
  431. return enif_make_badarg(env);
  432. }
  433. if(iter->gen != iter->khash->gen) {
  434. return make_error(env, priv, priv->atom_expired_iterator);
  435. }
  436. entry = kl_hash_scan_next(&(iter->scan));
  437. if(entry == NULL) {
  438. return priv->atom_end_of_table;
  439. }
  440. node = (khnode_t*) kl_hnode_getkey(entry);
  441. key = enif_make_copy(env, node->key);
  442. val = enif_make_copy(env, node->val);
  443. return enif_make_tuple2(env, key, val);
  444. }
  445. static int
  446. load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
  447. {
  448. int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
  449. ErlNifResourceType* res;
  450. khash_priv* new_priv = (khash_priv*) enif_alloc(sizeof(khash_priv));
  451. if(new_priv == NULL) {
  452. return 1;
  453. }
  454. res = enif_open_resource_type(
  455. env, NULL, "khash", khash_free, flags, NULL);
  456. if(res == NULL) {
  457. return 1;
  458. }
  459. new_priv->res_hash = res;
  460. res = enif_open_resource_type(
  461. env, NULL, "khash_iter", khash_iter_free, flags, NULL);
  462. if(res == NULL) {
  463. return 1;
  464. }
  465. new_priv->res_iter = res;
  466. new_priv->atom_ok = make_atom(env, "ok");
  467. new_priv->atom_error = make_atom(env, "error");
  468. new_priv->atom_value = make_atom(env, "value");
  469. new_priv->atom_not_found = make_atom(env, "not_found");
  470. new_priv->atom_end_of_table = make_atom(env, "end_of_table");
  471. new_priv->atom_expired_iterator = make_atom(env, "expired_iterator");
  472. *priv = (void*) new_priv;
  473. return 0;
  474. }
  475. static int
  476. reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
  477. {
  478. return 0;
  479. }
  480. static int
  481. upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
  482. {
  483. return load(env, priv, info);
  484. }
  485. static void
  486. unload(ErlNifEnv* env, void* priv)
  487. {
  488. enif_free(priv);
  489. return;
  490. }
  491. static ErlNifFunc funcs[] = {
  492. {"new", 1, khash_new},
  493. {"to_list", 1, khash_to_list},
  494. {"clear", 1, khash_clear},
  495. {"lookup_int", 3, khash_lookup},
  496. {"get_int", 4, khash_get},
  497. {"put_int", 4, khash_put},
  498. {"del_int", 3, khash_del},
  499. {"size", 1, khash_size},
  500. {"iter", 1, khash_iter},
  501. {"iter_next", 1, khash_iter_next}
  502. };
  503. ERL_NIF_INIT(khash, funcs, &load, &reload, &upgrade, &unload);