erlang各种有用的函数包括一些有用nif封装,还有一些性能测试case。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

688 lines
17 KiB

  1. /*
  2. * @author Evgeny Khramtsov <ekhramtsov@process-one.net>
  3. * @copyright (C) 2002-2020 ProcessOne, SARL. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. #include <erl_nif.h>
  19. #include <stdio.h>
  20. #include <errno.h>
  21. #include "uthash.h"
  22. void __free(void *ptr, size_t size) {
  23. enif_free(ptr);
  24. }
  25. #undef uthash_malloc
  26. #undef uthash_free
  27. #define uthash_malloc enif_alloc
  28. #define uthash_free __free
  29. /****************************************************************
  30. * Structures/Globals definitions *
  31. ****************************************************************/
  32. typedef struct __tree_t {
  33. char *key;
  34. char *val;
  35. int refc;
  36. struct __tree_t *sub;
  37. UT_hash_handle hh;
  38. } tree_t;
  39. typedef struct {
  40. tree_t *tree;
  41. char *name;
  42. ErlNifRWLock *lock;
  43. } state_t;
  44. typedef struct {
  45. char *name;
  46. state_t *state;
  47. UT_hash_handle hh;
  48. } registry_t;
  49. static ErlNifResourceType *tree_state_t = NULL;
  50. static registry_t *registry = NULL;
  51. static ErlNifRWLock *registry_lock = NULL;
  52. /****************************************************************
  53. * MQTT Tree Manipulation *
  54. ****************************************************************/
  55. tree_t *tree_new(char *key, size_t len) {
  56. tree_t *tree = enif_alloc(sizeof(tree_t));
  57. if (tree) {
  58. memset(tree, 0, sizeof(tree_t));
  59. if (key && len) {
  60. tree->key = enif_alloc(len);
  61. if (tree->key) {
  62. memcpy(tree->key, key, len);
  63. } else {
  64. enif_free(tree);
  65. tree = NULL;
  66. }
  67. }
  68. }
  69. return tree;
  70. }
  71. void tree_free(tree_t *t) {
  72. tree_t *found, *iter;
  73. if (t) {
  74. enif_free(t->key);
  75. enif_free(t->val);
  76. HASH_ITER(hh, t->sub, found, iter) {
  77. HASH_DEL(t->sub, found);
  78. tree_free(found);
  79. }
  80. memset(t, 0, sizeof(tree_t));
  81. enif_free(t);
  82. }
  83. }
  84. void tree_clear(tree_t *root) {
  85. tree_t *found, *iter;
  86. HASH_ITER(hh, root->sub, found, iter) {
  87. HASH_DEL(root->sub, found);
  88. tree_free(found);
  89. }
  90. }
  91. int tree_add(tree_t *root, char *path, size_t size) {
  92. int i = 0;
  93. size_t len;
  94. tree_t *t = root;
  95. tree_t *found, *new;
  96. while (i<=size) {
  97. len = strlen(path+i) + 1;
  98. HASH_FIND_STR(t->sub, path+i, found);
  99. if (found) {
  100. i += len;
  101. t = found;
  102. } else {
  103. new = tree_new(path+i, len);
  104. if (new) {
  105. HASH_ADD_STR(t->sub, key, new);
  106. i += len;
  107. t = new;
  108. } else
  109. return ENOMEM;
  110. }
  111. }
  112. if (!t->val) {
  113. t->val = enif_alloc(size+1);
  114. if (t->val) {
  115. t->val[size] = 0;
  116. for (i=0; i<size; i++) {
  117. char c = path[i];
  118. t->val[i] = c ? c : '/';
  119. }
  120. } else
  121. return ENOMEM;
  122. }
  123. t->refc++;
  124. return 0;
  125. }
  126. int tree_del(tree_t *root, char *path, size_t i, size_t size) {
  127. tree_t *found;
  128. if (i<=size) {
  129. HASH_FIND_STR(root->sub, path+i, found);
  130. if (found) {
  131. i += strlen(path+i) + 1;
  132. int deleted = tree_del(found, path, i, size);
  133. if (deleted) {
  134. HASH_DEL(root->sub, found);
  135. tree_free(found);
  136. }
  137. }
  138. } else if (root->refc) {
  139. root->refc--;
  140. if (!root->refc) {
  141. enif_free(root->val);
  142. root->val = NULL;
  143. }
  144. }
  145. return !root->refc && !root->sub;
  146. }
  147. void tree_size(tree_t *tree, size_t *size) {
  148. tree_t *found, *iter;
  149. HASH_ITER(hh, tree->sub, found, iter) {
  150. if (found->refc) (*size)++;
  151. tree_size(found, size);
  152. }
  153. }
  154. int tree_refc(tree_t *tree, char *path, size_t i, size_t size) {
  155. tree_t *found;
  156. if (i<=size) {
  157. HASH_FIND_STR(tree->sub, path+i, found);
  158. if (found) {
  159. i += strlen(path+i) + 1;
  160. return tree_refc(found, path, i, size);
  161. } else {
  162. return 0;
  163. }
  164. } else
  165. return tree->refc;
  166. }
  167. /****************************************************************
  168. * Registration *
  169. ****************************************************************/
  170. void delete_registry_entry(registry_t *entry) {
  171. /* registry_lock must be RW-locked! */
  172. HASH_DEL(registry, entry);
  173. entry->state->name = NULL;
  174. enif_release_resource(entry->state);
  175. enif_free(entry->name);
  176. enif_free(entry);
  177. }
  178. int register_tree(char *name, state_t *state) {
  179. registry_t *entry, *found;
  180. entry = enif_alloc(sizeof(registry_t));
  181. if (!entry) return ENOMEM;
  182. entry->name = enif_alloc(strlen(name) + 1);
  183. if (!entry->name) {
  184. free(entry);
  185. return ENOMEM;
  186. }
  187. entry->state = state;
  188. strcpy(entry->name, name);
  189. enif_rwlock_rwlock(registry_lock);
  190. HASH_FIND_STR(registry, name, found);
  191. if (found) {
  192. enif_rwlock_rwunlock(registry_lock);
  193. enif_free(entry->name);
  194. enif_free(entry);
  195. return EINVAL;
  196. } else {
  197. if (state->name) {
  198. /* Unregistering previously registered name */
  199. HASH_FIND_STR(registry, state->name, found);
  200. if (found)
  201. delete_registry_entry(found);
  202. }
  203. enif_keep_resource(state);
  204. HASH_ADD_STR(registry, name, entry);
  205. state->name = entry->name;
  206. enif_rwlock_rwunlock(registry_lock);
  207. return 0;
  208. }
  209. }
  210. int unregister_tree(char *name) {
  211. registry_t *entry;
  212. int ret;
  213. enif_rwlock_rwlock(registry_lock);
  214. HASH_FIND_STR(registry, name, entry);
  215. if (entry) {
  216. delete_registry_entry(entry);
  217. ret = 0;
  218. } else {
  219. ret = EINVAL;
  220. }
  221. enif_rwlock_rwunlock(registry_lock);
  222. return ret;
  223. }
  224. /****************************************************************
  225. * NIF helpers *
  226. ****************************************************************/
  227. static ERL_NIF_TERM cons(ErlNifEnv *env, char *str, ERL_NIF_TERM tail)
  228. {
  229. if (str) {
  230. size_t len = strlen(str);
  231. ERL_NIF_TERM head;
  232. unsigned char *buf = enif_make_new_binary(env, len, &head);
  233. if (buf) {
  234. memcpy(buf, str, len);
  235. return enif_make_list_cell(env, head, tail);
  236. }
  237. }
  238. return tail;
  239. }
  240. static void match(ErlNifEnv *env, tree_t *root,
  241. char *path, size_t i, size_t size, ERL_NIF_TERM *acc)
  242. {
  243. tree_t *found;
  244. size_t len = 0;
  245. if (i<=size) {
  246. HASH_FIND_STR(root->sub, path+i, found);
  247. if (found) {
  248. len = strlen(path+i) + 1;
  249. match(env, found, path, i+len, size, acc);
  250. };
  251. if (i || path[0] != '$') {
  252. HASH_FIND_STR(root->sub, "+", found);
  253. if (found) {
  254. len = strlen(path+i) + 1;
  255. match(env, found, path, i+len, size, acc);
  256. }
  257. HASH_FIND_STR(root->sub, "#", found);
  258. if (found) {
  259. *acc = cons(env, found->val, *acc);
  260. }
  261. }
  262. } else {
  263. *acc = cons(env, root->val, *acc);
  264. HASH_FIND_STR(root->sub, "#", found);
  265. if (found)
  266. *acc = cons(env, found->val, *acc);
  267. }
  268. }
  269. static void to_list(ErlNifEnv *env, tree_t *root, ERL_NIF_TERM *acc)
  270. {
  271. tree_t *found, *iter;
  272. HASH_ITER(hh, root->sub, found, iter) {
  273. if (found->val) {
  274. size_t len = strlen(found->val);
  275. ERL_NIF_TERM refc = enif_make_int(env, found->refc);
  276. ERL_NIF_TERM val;
  277. unsigned char *buf = enif_make_new_binary(env, len, &val);
  278. if (buf) {
  279. memcpy(buf, found->val, len);
  280. *acc = enif_make_list_cell(env, enif_make_tuple2(env, val, refc), *acc);
  281. }
  282. };
  283. to_list(env, found, acc);
  284. }
  285. }
  286. static ERL_NIF_TERM dump(ErlNifEnv *env, tree_t *tree)
  287. {
  288. tree_t *found, *iter;
  289. ERL_NIF_TERM tail, head;
  290. tail = enif_make_list(env, 0);
  291. HASH_ITER(hh, tree->sub, found, iter) {
  292. head = dump(env, found);
  293. tail = enif_make_list_cell(env, head, tail);
  294. }
  295. if (tree->key) {
  296. ERL_NIF_TERM part, path;
  297. part = enif_make_string(env, tree->key, ERL_NIF_LATIN1);
  298. if (tree->val)
  299. path = enif_make_string(env, tree->val, ERL_NIF_LATIN1);
  300. else
  301. path = enif_make_atom(env, "none");
  302. return enif_make_tuple4(env, part, path, enif_make_int(env, tree->refc), tail);
  303. } else
  304. return tail;
  305. }
  306. static ERL_NIF_TERM raise(ErlNifEnv *env, int err)
  307. {
  308. switch (err) {
  309. case ENOMEM:
  310. return enif_raise_exception(env, enif_make_atom(env, "enomem"));
  311. default:
  312. return enif_make_badarg(env);
  313. }
  314. }
  315. void prep_path(char *path, ErlNifBinary *bin) {
  316. int i;
  317. unsigned char c;
  318. path[bin->size] = 0;
  319. for (i=0; i<bin->size; i++) {
  320. c = bin->data[i];
  321. path[i] = (c == '/') ? 0 : c;
  322. }
  323. }
  324. /****************************************************************
  325. * Constructors/Destructors *
  326. ****************************************************************/
  327. static state_t *init_tree_state(ErlNifEnv *env) {
  328. state_t *state = enif_alloc_resource(tree_state_t, sizeof(state_t));
  329. if (state) {
  330. memset(state, 0, sizeof(state_t));
  331. state->tree = tree_new(NULL, 0);
  332. state->lock = enif_rwlock_create("mqtree_lock");
  333. if (state->tree && state->lock)
  334. return state;
  335. else
  336. enif_release_resource(state);
  337. }
  338. return NULL;
  339. }
  340. static void destroy_tree_state(ErlNifEnv *env, void *data) {
  341. state_t *state = (state_t *) data;
  342. if (state) {
  343. tree_free(state->tree);
  344. if (state->lock) enif_rwlock_destroy(state->lock);
  345. }
  346. memset(state, 0, sizeof(state_t));
  347. }
  348. /****************************************************************
  349. * NIF definitions *
  350. ****************************************************************/
  351. static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM max) {
  352. registry_lock = enif_rwlock_create("mqtree_registry");
  353. if (registry_lock) {
  354. ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
  355. tree_state_t = enif_open_resource_type(env, NULL, "mqtree_state",
  356. destroy_tree_state,
  357. flags, NULL);
  358. return 0;
  359. }
  360. return ENOMEM;
  361. }
  362. static void unload(ErlNifEnv* env, void* priv) {
  363. if (registry_lock) {
  364. enif_rwlock_destroy(registry_lock);
  365. registry_lock = NULL;
  366. }
  367. }
  368. static ERL_NIF_TERM new_0(ErlNifEnv* env, int argc,
  369. const ERL_NIF_TERM argv[])
  370. {
  371. ERL_NIF_TERM result;
  372. state_t *state = init_tree_state(env);
  373. if (state) {
  374. result = enif_make_resource(env, state);
  375. enif_release_resource(state);
  376. } else
  377. result = raise(env, ENOMEM);
  378. return result;
  379. }
  380. static ERL_NIF_TERM insert_2(ErlNifEnv* env, int argc,
  381. const ERL_NIF_TERM argv[])
  382. {
  383. state_t *state;
  384. ErlNifBinary path_bin;
  385. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) ||
  386. !enif_inspect_iolist_as_binary(env, argv[1], &path_bin))
  387. return raise(env, EINVAL);
  388. if (!path_bin.size)
  389. return enif_make_atom(env, "ok");
  390. char path[path_bin.size+1];
  391. prep_path(path, &path_bin);
  392. enif_rwlock_rwlock(state->lock);
  393. int ret = tree_add(state->tree, path, path_bin.size);
  394. enif_rwlock_rwunlock(state->lock);
  395. if (!ret)
  396. return enif_make_atom(env, "ok");
  397. else
  398. return raise(env, ret);
  399. }
  400. static ERL_NIF_TERM delete_2(ErlNifEnv* env, int argc,
  401. const ERL_NIF_TERM argv[])
  402. {
  403. state_t *state;
  404. ErlNifBinary path_bin;
  405. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) ||
  406. !enif_inspect_iolist_as_binary(env, argv[1], &path_bin))
  407. return raise(env, EINVAL);
  408. if (!path_bin.size)
  409. return enif_make_atom(env, "ok");
  410. char path[path_bin.size+1];
  411. prep_path(path, &path_bin);
  412. enif_rwlock_rwlock(state->lock);
  413. tree_del(state->tree, path, 0, path_bin.size);
  414. enif_rwlock_rwunlock(state->lock);
  415. return enif_make_atom(env, "ok");
  416. }
  417. static ERL_NIF_TERM match_2(ErlNifEnv* env, int argc,
  418. const ERL_NIF_TERM argv[])
  419. {
  420. state_t *state;
  421. ErlNifBinary path_bin;
  422. ERL_NIF_TERM result = enif_make_list(env, 0);
  423. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) ||
  424. !enif_inspect_iolist_as_binary(env, argv[1], &path_bin))
  425. return raise(env, EINVAL);
  426. if (!path_bin.size)
  427. return result;
  428. char path[path_bin.size+1];
  429. prep_path(path, &path_bin);
  430. enif_rwlock_rlock(state->lock);
  431. match(env, state->tree, path, 0, path_bin.size, &result);
  432. enif_rwlock_runlock(state->lock);
  433. return result;
  434. }
  435. static ERL_NIF_TERM refc_2(ErlNifEnv* env, int argc,
  436. const ERL_NIF_TERM argv[])
  437. {
  438. state_t *state;
  439. ErlNifBinary path_bin;
  440. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state) ||
  441. !enif_inspect_iolist_as_binary(env, argv[1], &path_bin))
  442. return raise(env, EINVAL);
  443. if (!path_bin.size)
  444. return enif_make_int(env, 0);
  445. char path[path_bin.size+1];
  446. prep_path(path, &path_bin);
  447. enif_rwlock_rlock(state->lock);
  448. int refc = tree_refc(state->tree, path, 0, path_bin.size);
  449. enif_rwlock_runlock(state->lock);
  450. return enif_make_int(env, refc);
  451. }
  452. static ERL_NIF_TERM clear_1(ErlNifEnv* env, int argc,
  453. const ERL_NIF_TERM argv[])
  454. {
  455. state_t *state;
  456. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state))
  457. return raise(env, EINVAL);
  458. enif_rwlock_rwlock(state->lock);
  459. tree_clear(state->tree);
  460. enif_rwlock_rwunlock(state->lock);
  461. return enif_make_atom(env, "ok");
  462. }
  463. static ERL_NIF_TERM size_1(ErlNifEnv* env, int argc,
  464. const ERL_NIF_TERM argv[])
  465. {
  466. state_t *state;
  467. size_t size = 0;
  468. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state))
  469. return raise(env, EINVAL);
  470. enif_rwlock_rlock(state->lock);
  471. tree_size(state->tree, &size);
  472. enif_rwlock_runlock(state->lock);
  473. return enif_make_uint64(env, (ErlNifUInt64) size);
  474. }
  475. static ERL_NIF_TERM is_empty_1(ErlNifEnv* env, int argc,
  476. const ERL_NIF_TERM argv[])
  477. {
  478. state_t *state;
  479. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state))
  480. return raise(env, EINVAL);
  481. enif_rwlock_rlock(state->lock);
  482. char *ret = state->tree->sub ? "false" : "true";
  483. enif_rwlock_runlock(state->lock);
  484. return enif_make_atom(env, ret);
  485. }
  486. static ERL_NIF_TERM to_list_1(ErlNifEnv* env, int argc,
  487. const ERL_NIF_TERM argv[])
  488. {
  489. state_t *state;
  490. ERL_NIF_TERM result = enif_make_list(env, 0);
  491. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state))
  492. return raise(env, EINVAL);
  493. enif_rwlock_rlock(state->lock);
  494. to_list(env, state->tree, &result);
  495. enif_rwlock_runlock(state->lock);
  496. return result;
  497. }
  498. static ERL_NIF_TERM dump_1(ErlNifEnv* env, int argc,
  499. const ERL_NIF_TERM argv[])
  500. {
  501. state_t *state;
  502. if (!enif_get_resource(env, argv[0], tree_state_t, (void *) &state))
  503. return raise(env, EINVAL);
  504. enif_rwlock_rlock(state->lock);
  505. ERL_NIF_TERM result = dump(env, state->tree);
  506. enif_rwlock_runlock(state->lock);
  507. return result;
  508. }
  509. static ERL_NIF_TERM register_2(ErlNifEnv* env, int argc,
  510. const ERL_NIF_TERM argv[])
  511. {
  512. state_t *state;
  513. unsigned int len;
  514. int ret;
  515. if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1) ||
  516. !enif_get_resource(env, argv[1], tree_state_t, (void *) &state))
  517. return raise(env, EINVAL);
  518. char name[len+1];
  519. enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1);
  520. if (!strcmp(name, "undefined"))
  521. return raise(env, EINVAL);
  522. ret = register_tree(name, state);
  523. if (ret)
  524. return raise(env, ret);
  525. else
  526. return enif_make_atom(env, "ok");
  527. }
  528. static ERL_NIF_TERM unregister_1(ErlNifEnv* env, int argc,
  529. const ERL_NIF_TERM argv[])
  530. {
  531. unsigned int len;
  532. int ret;
  533. if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1))
  534. return raise(env, EINVAL);
  535. char name[len+1];
  536. enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1);
  537. ret = unregister_tree(name);
  538. if (ret)
  539. return raise(env, ret);
  540. else
  541. return enif_make_atom(env, "ok");
  542. }
  543. static ERL_NIF_TERM whereis_1(ErlNifEnv* env, int argc,
  544. const ERL_NIF_TERM argv[])
  545. {
  546. unsigned int len;
  547. registry_t *entry;
  548. ERL_NIF_TERM result;
  549. if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1))
  550. return raise(env, EINVAL);
  551. char name[len+1];
  552. enif_get_atom(env, argv[0], name, len+1, ERL_NIF_LATIN1);
  553. enif_rwlock_rlock(registry_lock);
  554. HASH_FIND_STR(registry, name, entry);
  555. if (entry)
  556. result = enif_make_resource(env, entry->state);
  557. else
  558. result = enif_make_atom(env, "undefined");
  559. enif_rwlock_runlock(registry_lock);
  560. return result;
  561. }
  562. static ERL_NIF_TERM registered_0(ErlNifEnv* env, int argc,
  563. const ERL_NIF_TERM argv[])
  564. {
  565. registry_t *entry, *iter;
  566. ERL_NIF_TERM result = enif_make_list(env, 0);
  567. enif_rwlock_rlock(registry_lock);
  568. HASH_ITER(hh, registry, entry, iter) {
  569. result = enif_make_list_cell(env, enif_make_atom(env, entry->name), result);
  570. }
  571. enif_rwlock_runlock(registry_lock);
  572. return result;
  573. }
  574. static ErlNifFunc nif_funcs[] =
  575. {
  576. {"new", 0, new_0},
  577. {"insert", 2, insert_2},
  578. {"delete", 2, delete_2},
  579. {"match", 2, match_2},
  580. {"refc", 2, refc_2},
  581. {"clear", 1, clear_1},
  582. {"size", 1, size_1},
  583. {"is_empty", 1, is_empty_1},
  584. {"to_list", 1, to_list_1},
  585. {"dump", 1, dump_1},
  586. {"register", 2, register_2},
  587. {"unregister", 1, unregister_1},
  588. {"whereis", 1, whereis_1},
  589. {"registered", 0, registered_0}
  590. };
  591. ERL_NIF_INIT(mqtree, nif_funcs, load, NULL, NULL, unload)