value.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. /**
  2. * @file value.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the author nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. * @section DESCRIPTION
  30. *
  31. * Synopsis:
  32. * value(value)
  33. * value value::get(string index)
  34. * value value::getpath(list(string) path)
  35. *
  36. * Description:
  37. * Value objects allow examining and manipulating values.
  38. *
  39. * value(value) constructs a value object from the given value.
  40. *
  41. * value::get(index) constructs a value object from the list element
  42. * of the value, which must be a list. This it *not* a copy, and the
  43. * two value objects will share the same value data (more correctly, different
  44. * portions of it).
  45. *
  46. * value::getpath(path) is like get(), except that it performs multiple
  47. * consecutive resolutions. Also, if the path is an empty list, it performs
  48. * no resulution at all.
  49. *
  50. * Variables:
  51. * type - type of the value; "string" or "list"
  52. * length - length of the list (only if the value if a list)
  53. *
  54. * Synopsis:
  55. * value::delete()
  56. *
  57. * Description:
  58. * Deletes the underlying value data of this value object. After delection,
  59. * the value object enters a deleted state, which will cause any operation
  60. * on it to fail. Any other value objects which were sharing the deleted
  61. * value data or portions of it will too enter deleted state.
  62. */
  63. #include <stdlib.h>
  64. #include <string.h>
  65. #include <stddef.h>
  66. #include <limits.h>
  67. #include <misc/offset.h>
  68. #include <misc/debug.h>
  69. #include <misc/parse_number.h>
  70. #include <structure/LinkedList0.h>
  71. #include <structure/IndexedList.h>
  72. #include <ncd/NCDModule.h>
  73. #include <generated/blog_channel_ncd_value.h>
  74. #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
  75. struct value;
  76. struct instance {
  77. NCDModuleInst *i;
  78. struct value *v;
  79. LinkedList0Node refs_list_node;
  80. };
  81. struct value {
  82. LinkedList0 refs_list;
  83. struct value *parent;
  84. union {
  85. struct {
  86. IndexedListNode list_contents_il_node;
  87. } list_parent;
  88. };
  89. int type;
  90. union {
  91. struct {
  92. char *string;
  93. } string;
  94. struct {
  95. IndexedList list_contents_il;
  96. } list;
  97. };
  98. };
  99. static const char * get_type_str (int type);
  100. static void value_cleanup (struct value *v);
  101. static void value_delete (struct value *v);
  102. static struct value * value_init_string (NCDModuleInst *i, const char *str);
  103. static struct value * value_init_list (NCDModuleInst *i);
  104. static size_t value_list_len (struct value *v);
  105. static struct value * value_list_at (struct value *v, size_t index);
  106. static int value_list_insert (NCDModuleInst *i, struct value *list, struct value *v, size_t index);
  107. static void value_list_remove (struct value *list, struct value *v);
  108. static struct value * value_init_fromvalue (NCDModuleInst *i, NCDValue *value);
  109. static int value_to_value (NCDModuleInst *i, struct value *v, NCDValue *out_value);
  110. static struct value * value_get (NCDModuleInst *i, struct value *v, const char *index_str);
  111. static struct value * value_get_path (NCDModuleInst *i, struct value *v, NCDValue *path);
  112. static const char * get_type_str (int type)
  113. {
  114. switch (type) {
  115. case NCDVALUE_STRING: return "string";
  116. case NCDVALUE_LIST: return "list";
  117. }
  118. ASSERT(0)
  119. return NULL;
  120. }
  121. static void value_cleanup (struct value *v)
  122. {
  123. if (v->parent || !LinkedList0_IsEmpty(&v->refs_list)) {
  124. return;
  125. }
  126. switch (v->type) {
  127. case NCDVALUE_STRING: {
  128. free(v->string.string);
  129. } break;
  130. case NCDVALUE_LIST: {
  131. while (value_list_len(v) > 0) {
  132. struct value *ev = value_list_at(v, 0);
  133. value_list_remove(v, ev);
  134. value_cleanup(ev);
  135. }
  136. } break;
  137. default: ASSERT(0);
  138. }
  139. free(v);
  140. }
  141. static void value_delete (struct value *v)
  142. {
  143. if (v->parent) {
  144. ASSERT(v->parent->type == NCDVALUE_LIST)
  145. value_list_remove(v->parent, v);
  146. }
  147. LinkedList0Node *ln;
  148. while (ln = LinkedList0_GetFirst(&v->refs_list)) {
  149. struct instance *inst = UPPER_OBJECT(ln, struct instance, refs_list_node);
  150. ASSERT(inst->v == v)
  151. LinkedList0_Remove(&v->refs_list, &inst->refs_list_node);
  152. inst->v = NULL;
  153. }
  154. switch (v->type) {
  155. case NCDVALUE_STRING: {
  156. free(v->string.string);
  157. } break;
  158. case NCDVALUE_LIST: {
  159. while (value_list_len(v) > 0) {
  160. struct value *ev = value_list_at(v, 0);
  161. value_delete(ev);
  162. }
  163. } break;
  164. default: ASSERT(0);
  165. }
  166. free(v);
  167. }
  168. static struct value * value_init_string (NCDModuleInst *i, const char *str)
  169. {
  170. struct value *v = malloc(sizeof(*v));
  171. if (!v) {
  172. ModuleLog(i, BLOG_ERROR, "malloc failed");
  173. goto fail0;
  174. }
  175. LinkedList0_Init(&v->refs_list);
  176. v->parent = NULL;
  177. v->type = NCDVALUE_STRING;
  178. if (!(v->string.string = strdup(str))) {
  179. ModuleLog(i, BLOG_ERROR, "strdup failed");
  180. goto fail1;
  181. }
  182. return v;
  183. fail1:
  184. free(v);
  185. fail0:
  186. return NULL;
  187. }
  188. static struct value * value_init_list (NCDModuleInst *i)
  189. {
  190. struct value *v = malloc(sizeof(*v));
  191. if (!v) {
  192. ModuleLog(i, BLOG_ERROR, "malloc failed");
  193. return NULL;
  194. }
  195. LinkedList0_Init(&v->refs_list);
  196. v->parent = NULL;
  197. v->type = NCDVALUE_LIST;
  198. IndexedList_Init(&v->list.list_contents_il);
  199. return v;
  200. }
  201. static size_t value_list_len (struct value *v)
  202. {
  203. ASSERT(v->type == NCDVALUE_LIST)
  204. return IndexedList_Count(&v->list.list_contents_il);
  205. }
  206. static struct value * value_list_at (struct value *v, size_t index)
  207. {
  208. ASSERT(v->type == NCDVALUE_LIST)
  209. ASSERT(index < value_list_len(v))
  210. IndexedListNode *iln = IndexedList_GetAt(&v->list.list_contents_il, index);
  211. ASSERT(iln)
  212. struct value *e = UPPER_OBJECT(iln, struct value, list_parent.list_contents_il_node);
  213. ASSERT(e->parent == v)
  214. return e;
  215. }
  216. static int value_list_insert (NCDModuleInst *i, struct value *list, struct value *v, size_t index)
  217. {
  218. ASSERT(list->type == NCDVALUE_LIST)
  219. ASSERT(!v->parent)
  220. ASSERT(index <= value_list_len(list))
  221. if (value_list_len(list) == SIZE_MAX) {
  222. ModuleLog(i, BLOG_ERROR, "list has too many elements");
  223. return 0;
  224. }
  225. IndexedList_InsertAt(&list->list.list_contents_il, &v->list_parent.list_contents_il_node, index);
  226. v->parent = list;
  227. return 1;
  228. }
  229. static void value_list_remove (struct value *list, struct value *v)
  230. {
  231. ASSERT(list->type == NCDVALUE_LIST)
  232. ASSERT(v->parent == list)
  233. IndexedList_Remove(&list->list.list_contents_il, &v->list_parent.list_contents_il_node);
  234. v->parent = NULL;
  235. }
  236. static struct value * value_init_fromvalue (NCDModuleInst *i, NCDValue *value)
  237. {
  238. struct value *v;
  239. switch (NCDValue_Type(value)) {
  240. case NCDVALUE_STRING: {
  241. if (!(v = value_init_string(i, NCDValue_StringValue(value)))) {
  242. goto fail0;
  243. }
  244. } break;
  245. case NCDVALUE_LIST: {
  246. if (!(v = value_init_list(i))) {
  247. goto fail0;
  248. }
  249. for (NCDValue *eval = NCDValue_ListFirst(value); eval; eval = NCDValue_ListNext(value, eval)) {
  250. struct value *ev = value_init_fromvalue(i, eval);
  251. if (!ev) {
  252. goto fail1;
  253. }
  254. if (!value_list_insert(i, v, ev, value_list_len(v))) {
  255. value_cleanup(ev);
  256. goto fail1;
  257. }
  258. }
  259. } break;
  260. default: ASSERT(0);
  261. }
  262. return v;
  263. fail1:
  264. value_cleanup(v);
  265. fail0:
  266. return NULL;
  267. }
  268. static int value_to_value (NCDModuleInst *i, struct value *v, NCDValue *out_value)
  269. {
  270. switch (v->type) {
  271. case NCDVALUE_STRING: {
  272. if (!(NCDValue_InitString(out_value, v->string.string))) {
  273. ModuleLog(i, BLOG_ERROR, "NCDValue_InitString failed");
  274. return 0;
  275. }
  276. return 1;
  277. } break;
  278. case NCDVALUE_LIST: {
  279. NCDValue_InitList(out_value);
  280. for (size_t index = 0; index < value_list_len(v); index++) {
  281. NCDValue eval;
  282. if (!value_to_value(i, value_list_at(v, index), &eval)) {
  283. goto fail1;
  284. }
  285. if (!NCDValue_ListAppend(out_value, eval)) {
  286. ModuleLog(i, BLOG_ERROR, "NCDValue_ListAppend failed");
  287. NCDValue_Free(&eval);
  288. goto fail1;
  289. }
  290. }
  291. return 1;
  292. fail1:
  293. NCDValue_Free(out_value);
  294. return 0;
  295. } break;
  296. default: ASSERT(0);
  297. }
  298. }
  299. static struct value * value_get (NCDModuleInst *i, struct value *v, const char *index_str)
  300. {
  301. ASSERT(index_str)
  302. switch (v->type) {
  303. case NCDVALUE_STRING: {
  304. ModuleLog(i, BLOG_ERROR, "cannot resolve into a string");
  305. goto fail;
  306. } break;
  307. case NCDVALUE_LIST: {
  308. uintmax_t index;
  309. if (!parse_unsigned_integer(index_str, &index)) {
  310. ModuleLog(i, BLOG_ERROR, "index string is not a valid number (resolving into list)");
  311. goto fail;
  312. }
  313. if (index >= value_list_len(v)) {
  314. ModuleLog(i, BLOG_ERROR, "index string is out of bounds (resolving into list)");
  315. goto fail;
  316. }
  317. v = value_list_at(v, index);
  318. } break;
  319. default: ASSERT(0);
  320. }
  321. return v;
  322. fail:
  323. return NULL;
  324. }
  325. static struct value * value_get_path (NCDModuleInst *i, struct value *v, NCDValue *path)
  326. {
  327. ASSERT(NCDValue_Type(path) == NCDVALUE_LIST)
  328. for (NCDValue *ev = NCDValue_ListFirst(path); ev; ev = NCDValue_ListNext(path, ev)) {
  329. if (NCDValue_Type(ev) != NCDVALUE_STRING) {
  330. ModuleLog(i, BLOG_ERROR, "path component is not a string");
  331. goto fail;
  332. }
  333. if (!(v = value_get(i, v, NCDValue_StringValue(ev)))) {
  334. goto fail;
  335. }
  336. }
  337. return v;
  338. fail:
  339. return NULL;
  340. }
  341. static void func_new_common (NCDModuleInst *i, struct value *v)
  342. {
  343. ASSERT(v)
  344. // allocate instance
  345. struct instance *o = malloc(sizeof(*o));
  346. if (!o) {
  347. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  348. goto fail0;
  349. }
  350. o->i = i;
  351. NCDModuleInst_Backend_SetUser(i, o);
  352. // set value
  353. o->v = v;
  354. // add reference
  355. LinkedList0_Prepend(&o->v->refs_list, &o->refs_list_node);
  356. NCDModuleInst_Backend_Up(i);
  357. return;
  358. fail1:
  359. free(o);
  360. fail0:
  361. value_cleanup(v);
  362. NCDModuleInst_Backend_SetError(i);
  363. NCDModuleInst_Backend_Dead(i);
  364. }
  365. static void func_die (void *vo)
  366. {
  367. struct instance *o = vo;
  368. NCDModuleInst *i = o->i;
  369. if (o->v) {
  370. // remove reference
  371. LinkedList0_Remove(&o->v->refs_list, &o->refs_list_node);
  372. // cleanup after removing reference
  373. value_cleanup(o->v);
  374. }
  375. // free instance
  376. free(o);
  377. NCDModuleInst_Backend_Dead(i);
  378. }
  379. static int func_getvar (void *vo, const char *name, NCDValue *out_value)
  380. {
  381. struct instance *o = vo;
  382. if (strcmp(name, "type") && strcmp(name, "length") && strcmp(name, "")) {
  383. return 0;
  384. }
  385. if (!o->v) {
  386. ModuleLog(o->i, BLOG_ERROR, "value was deleted");
  387. return 0;
  388. }
  389. if (!strcmp(name, "type")) {
  390. if (!NCDValue_InitString(out_value, get_type_str(o->v->type))) {
  391. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
  392. return 0;
  393. }
  394. }
  395. else if (!strcmp(name, "length")) {
  396. if (o->v->type != NCDVALUE_LIST) {
  397. ModuleLog(o->i, BLOG_ERROR, "value is not a list");
  398. return 0;
  399. }
  400. char str[64];
  401. snprintf(str, sizeof(str), "%zu", value_list_len(o->v));
  402. if (!NCDValue_InitString(out_value, str)) {
  403. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
  404. return 0;
  405. }
  406. }
  407. else if (!strcmp(name, "")) {
  408. if (!value_to_value(o->i, o->v, out_value)) {
  409. return 0;
  410. }
  411. }
  412. return 1;
  413. }
  414. static void func_new_value (NCDModuleInst *i)
  415. {
  416. NCDValue *value_arg;
  417. if (!NCDValue_ListRead(i->args, 1, &value_arg)) {
  418. ModuleLog(i, BLOG_ERROR, "wrong arity");
  419. goto fail0;
  420. }
  421. struct value *v = value_init_fromvalue(i, value_arg);
  422. if (!v) {
  423. goto fail0;
  424. }
  425. func_new_common(i, v);
  426. return;
  427. fail0:
  428. NCDModuleInst_Backend_SetError(i);
  429. NCDModuleInst_Backend_Dead(i);
  430. }
  431. static void func_new_get (NCDModuleInst *i)
  432. {
  433. NCDValue *index_arg;
  434. if (!NCDValue_ListRead(i->args, 1, &index_arg)) {
  435. ModuleLog(i, BLOG_ERROR, "wrong arity");
  436. goto fail0;
  437. }
  438. if (NCDValue_Type(index_arg) != NCDVALUE_STRING) {
  439. ModuleLog(i, BLOG_ERROR, "wrong type");
  440. goto fail0;
  441. }
  442. struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
  443. if (!mo->v) {
  444. ModuleLog(i, BLOG_ERROR, "value was deleted");
  445. goto fail0;
  446. }
  447. struct value *v = value_get(i, mo->v, NCDValue_StringValue(index_arg));
  448. if (!v) {
  449. goto fail0;
  450. }
  451. func_new_common(i, v);
  452. return;
  453. fail0:
  454. NCDModuleInst_Backend_SetError(i);
  455. NCDModuleInst_Backend_Dead(i);
  456. }
  457. static void func_new_getpath (NCDModuleInst *i)
  458. {
  459. NCDValue *path_arg;
  460. if (!NCDValue_ListRead(i->args, 1, &path_arg)) {
  461. ModuleLog(i, BLOG_ERROR, "wrong arity");
  462. goto fail0;
  463. }
  464. if (NCDValue_Type(path_arg) != NCDVALUE_LIST) {
  465. ModuleLog(i, BLOG_ERROR, "wrong type");
  466. goto fail0;
  467. }
  468. struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
  469. if (!mo->v) {
  470. ModuleLog(i, BLOG_ERROR, "value was deleted");
  471. goto fail0;
  472. }
  473. struct value *v = value_get_path(i, mo->v, path_arg);
  474. if (!v) {
  475. goto fail0;
  476. }
  477. func_new_common(i, v);
  478. return;
  479. fail0:
  480. NCDModuleInst_Backend_SetError(i);
  481. NCDModuleInst_Backend_Dead(i);
  482. }
  483. static void delete_func_new (NCDModuleInst *i)
  484. {
  485. if (!NCDValue_ListRead(i->args, 0)) {
  486. ModuleLog(i, BLOG_ERROR, "wrong arity");
  487. goto fail0;
  488. }
  489. struct instance *mo = ((NCDModuleInst *)i->method_user)->inst_user;
  490. if (!mo->v) {
  491. ModuleLog(i, BLOG_ERROR, "value was deleted");
  492. goto fail0;
  493. }
  494. value_delete(mo->v);
  495. NCDModuleInst_Backend_Up(i);
  496. return;
  497. fail0:
  498. NCDModuleInst_Backend_SetError(i);
  499. NCDModuleInst_Backend_Dead(i);
  500. }
  501. static const struct NCDModule modules[] = {
  502. {
  503. .type = "value",
  504. .func_new = func_new_value,
  505. .func_die = func_die,
  506. .func_getvar = func_getvar
  507. }, {
  508. .type = "value::get",
  509. .base_type = "value",
  510. .func_new = func_new_get,
  511. .func_die = func_die,
  512. .func_getvar = func_getvar
  513. }, {
  514. .type = "value::getpath",
  515. .base_type = "value",
  516. .func_new = func_new_getpath,
  517. .func_die = func_die,
  518. .func_getvar = func_getvar
  519. }, {
  520. .type = "value::delete",
  521. .func_new = delete_func_new
  522. }, {
  523. .type = NULL
  524. }
  525. };
  526. const struct NCDModuleGroup ncdmodule_value = {
  527. .modules = modules
  528. };