node.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /// @file
  2. /// @ingroup cgraph_core
  3. /// @ingroup cgraph_node
  4. /*************************************************************************
  5. * Copyright (c) 2011 AT&T Intellectual Property
  6. * All rights reserved. This program and the accompanying materials
  7. * are made available under the terms of the Eclipse Public License v1.0
  8. * which accompanies this distribution, and is available at
  9. * https://www.eclipse.org/legal/epl-v10.html
  10. *
  11. * Contributors: Details at https://graphviz.org
  12. *************************************************************************/
  13. #include <assert.h>
  14. #include <cgraph/cghdr.h>
  15. #include <cgraph/node_set.h>
  16. #include <stdbool.h>
  17. #include <stdlib.h>
  18. #include <util/alloc.h>
  19. #include <util/unreachable.h>
  20. Agnode_t *agfindnode_by_id(Agraph_t * g, IDTYPE id)
  21. {
  22. Agsubnode_t *sn;
  23. sn = node_set_find(g->n_id, id);
  24. return sn ? sn->node : NULL;
  25. }
  26. static Agnode_t *agfindnode_by_name(Agraph_t * g, char *name)
  27. {
  28. IDTYPE id;
  29. if (agmapnametoid(g, AGNODE, name, &id, false))
  30. return agfindnode_by_id(g, id);
  31. else
  32. return NULL;
  33. }
  34. Agnode_t *agfstnode(Agraph_t * g)
  35. {
  36. Agsubnode_t *sn;
  37. sn = dtfirst(g->n_seq);
  38. return sn ? sn->node : NULL;
  39. }
  40. Agnode_t *agnxtnode(Agraph_t * g, Agnode_t * n)
  41. {
  42. Agsubnode_t *sn;
  43. sn = agsubrep(g, n);
  44. if (sn) sn = dtnext(g->n_seq, sn);
  45. return sn ? sn->node : NULL;
  46. }
  47. Agnode_t *aglstnode(Agraph_t * g)
  48. {
  49. Agsubnode_t *sn;
  50. sn = dtlast(g->n_seq);
  51. return sn ? sn->node : NULL;
  52. }
  53. Agnode_t *agprvnode(Agraph_t * g, Agnode_t * n)
  54. {
  55. Agsubnode_t *sn;
  56. sn = agsubrep(g, n);
  57. if (sn) sn = dtprev(g->n_seq, sn);
  58. return sn ? sn->node : NULL;
  59. }
  60. /* internal node constructor */
  61. static Agnode_t *newnode(Agraph_t * g, IDTYPE id, uint64_t seq)
  62. {
  63. Agnode_t *n;
  64. assert((seq & SEQ_MASK) == seq && "sequence ID overflow");
  65. n = agalloc(g, sizeof(Agnode_t));
  66. AGTYPE(n) = AGNODE;
  67. AGID(n) = id;
  68. AGSEQ(n) = seq & SEQ_MASK;
  69. n->root = agroot(g);
  70. if (agroot(g)->desc.has_attrs)
  71. (void)agbindrec(n, AgDataRecName, sizeof(Agattr_t), false);
  72. /* nodeattr_init and method_init will be called later, from the
  73. * subgraph where the node was actually created, but first it has
  74. * to be installed in all the (sub)graphs up to root. */
  75. return n;
  76. }
  77. static void installnode(Agraph_t * g, Agnode_t * n)
  78. {
  79. Agsubnode_t *sn;
  80. size_t osize;
  81. (void)osize;
  82. assert(node_set_size(g->n_id) == (size_t)dtsize(g->n_seq));
  83. osize = node_set_size(g->n_id);
  84. if (g == agroot(g)) sn = &(n->mainsub);
  85. else sn = agalloc(g, sizeof(Agsubnode_t));
  86. sn->node = n;
  87. node_set_add(g->n_id, sn);
  88. dtinsert(g->n_seq, sn);
  89. assert(node_set_size(g->n_id) == (size_t)dtsize(g->n_seq));
  90. assert(node_set_size(g->n_id) == osize + 1);
  91. }
  92. static void installnodetoroot(Agraph_t * g, Agnode_t * n)
  93. {
  94. Agraph_t *par;
  95. installnode(g, n);
  96. if ((par = agparent(g)))
  97. installnodetoroot(par, n);
  98. }
  99. static void initnode(Agraph_t * g, Agnode_t * n)
  100. {
  101. if (agroot(g)->desc.has_attrs)
  102. agnodeattr_init(g,n);
  103. agmethod_init(g, n);
  104. }
  105. /* external node constructor - create by id */
  106. Agnode_t *agidnode(Agraph_t * g, IDTYPE id, int cflag)
  107. {
  108. Agraph_t *root;
  109. Agnode_t *n;
  110. n = agfindnode_by_id(g, id);
  111. if (n == NULL && cflag) {
  112. root = agroot(g);
  113. if ((g != root) && ((n = agfindnode_by_id(root, id)))) /*old */
  114. agsubnode(g, n, 1); /* insert locally */
  115. else {
  116. if (agallocid(g, AGNODE, id)) { /* new */
  117. n = newnode(g, id, agnextseq(g, AGNODE));
  118. installnodetoroot(g, n);
  119. initnode(g, n);
  120. } else
  121. n = NULL; /* allocid for new node failed */
  122. }
  123. }
  124. /* else return probe result */
  125. return n;
  126. }
  127. Agnode_t *agnode(Agraph_t * g, char *name, int cflag)
  128. {
  129. Agraph_t *root;
  130. Agnode_t *n;
  131. IDTYPE id;
  132. root = agroot(g);
  133. /* probe for existing node */
  134. if (agmapnametoid(g, AGNODE, name, &id, false)) {
  135. if ((n = agfindnode_by_id(g, id)))
  136. return n;
  137. /* might already exist globally, but need to insert locally */
  138. if (cflag && (g != root) && ((n = agfindnode_by_id(root, id)))) {
  139. return agsubnode(g, n, 1);
  140. }
  141. }
  142. if (cflag && agmapnametoid(g, AGNODE, name, &id, true)) { /* reserve id */
  143. n = newnode(g, id, agnextseq(g, AGNODE));
  144. installnodetoroot(g, n);
  145. initnode(g, n);
  146. assert(agsubrep(g,n));
  147. agregister(g, AGNODE, n); /* register in external namespace */
  148. return n;
  149. }
  150. return NULL;
  151. }
  152. /* removes image of node and its edges from graph.
  153. caller must ensure n belongs to g. */
  154. void agdelnodeimage(Agraph_t * g, Agnode_t * n, void *ignored)
  155. {
  156. Agedge_t *e, *f;
  157. Agsubnode_t template = {0};
  158. template.node = n;
  159. (void)ignored;
  160. for (e = agfstedge(g, n); e; e = f) {
  161. f = agnxtedge(g, e, n);
  162. agdeledgeimage(g, e, 0);
  163. }
  164. /* If the following lines are switched, switch the discpline using
  165. * free_subnode below.
  166. */
  167. node_set_remove(g->n_id, n->base.tag.id);
  168. dtdelete(g->n_seq, &template);
  169. }
  170. int agdelnode(Agraph_t * g, Agnode_t * n)
  171. {
  172. Agedge_t *e, *f;
  173. if (!agfindnode_by_id(g, AGID(n)))
  174. return FAILURE; /* bad arg */
  175. if (g == agroot(g)) {
  176. for (e = agfstedge(g, n); e; e = f) {
  177. f = agnxtedge(g, e, n);
  178. agdeledge(g, e);
  179. }
  180. if (g->desc.has_attrs)
  181. agnodeattr_delete(n);
  182. agmethod_delete(g, n);
  183. agrecclose((Agobj_t *) n);
  184. agfreeid(g, AGNODE, AGID(n));
  185. }
  186. if (agapply(g, (Agobj_t *)n, (agobjfn_t)agdelnodeimage, NULL, false) == SUCCESS) {
  187. if (g == agroot(g))
  188. agfree(g, n);
  189. return SUCCESS;
  190. } else
  191. return FAILURE;
  192. }
  193. static void dict_relabel(Agraph_t *ignored, Agnode_t *n, void *arg) {
  194. (void)ignored;
  195. Agraph_t *g;
  196. uint64_t new_id;
  197. g = agraphof(n);
  198. new_id = *(uint64_t *) arg;
  199. Agsubnode_t *key = agsubrep(g, n);
  200. assert(key != NULL && "node being renamed does not exist");
  201. node_set_remove(g->n_id, key->node->base.tag.id);
  202. AGID(key->node) = new_id;
  203. node_set_add(g->n_id, key);
  204. /* because all the subgraphs share the same node now, this
  205. now requires a separate deletion and insertion phase */
  206. }
  207. int agrelabel_node(Agnode_t * n, char *newname)
  208. {
  209. Agraph_t *g;
  210. IDTYPE new_id;
  211. g = agroot(agraphof(n));
  212. if (agfindnode_by_name(g, newname))
  213. return FAILURE;
  214. if (agmapnametoid(g, AGNODE, newname, &new_id, true)) {
  215. if (agfindnode_by_id(agroot(g), new_id) == NULL) {
  216. agfreeid(g, AGNODE, AGID(n));
  217. agapply(g, (Agobj_t*)n, (agobjfn_t)dict_relabel, &new_id, false);
  218. return SUCCESS;
  219. } else {
  220. agfreeid(g, AGNODE, new_id); /* couldn't use it after all */
  221. }
  222. /* obj* is unchanged, so no need to re agregister() */
  223. }
  224. return FAILURE;
  225. }
  226. /* lookup or insert <n> in <g> */
  227. Agnode_t *agsubnode(Agraph_t * g, Agnode_t * n0, int cflag)
  228. {
  229. Agraph_t *par;
  230. Agnode_t *n;
  231. if (g->root != n0->root)
  232. return NULL;
  233. n = agfindnode_by_id(g, AGID(n0));
  234. if (n == NULL && cflag) {
  235. if ((par = agparent(g))) {
  236. n = agsubnode(par, n0, cflag);
  237. installnode(g, n);
  238. /* no callback for existing node insertion in subgraph (?) */
  239. }
  240. /* else impossible that <n> doesn't belong to <g> */
  241. }
  242. /* else lookup succeeded */
  243. return n;
  244. }
  245. /// compare a subnode to an identifier for equality
  246. ///
  247. /// @param sn0 Operand 1
  248. /// @param sn1 Operand 2
  249. /// @return True if nodes are equal
  250. static bool agsubnodeideq(const Agsubnode_t *sn0, IDTYPE id) {
  251. return AGID(sn0->node) == id;
  252. }
  253. static int agsubnodeseqcmpf(void *arg0, void *arg1) {
  254. Agsubnode_t *sn0 = arg0;
  255. Agsubnode_t *sn1 = arg1;
  256. if (AGSEQ(sn0->node) < AGSEQ(sn1->node)) return -1;
  257. if (AGSEQ(sn0->node) > AGSEQ(sn1->node)) return 1;
  258. return 0;
  259. }
  260. /* free_subnode:
  261. * Free Agsubnode_t allocated in installnode. This should
  262. * only be done for subgraphs, as the root graph uses the
  263. * subnode structure built into the node. This explains the
  264. * AGSNMAIN test. Also, note that both the id and the seq
  265. * dictionaries use the same subnode object, so only one
  266. * should do the deletion.
  267. */
  268. static void free_subnode(void *subnode) {
  269. Agsubnode_t *sn = subnode;
  270. if (!AGSNMAIN(sn))
  271. agfree (sn->node->root, sn);
  272. }
  273. Dtdisc_t Ag_subnode_seq_disc = {
  274. .link = offsetof(Agsubnode_t, seq_link), // link offset
  275. .freef = free_subnode,
  276. .comparf = agsubnodeseqcmpf,
  277. };
  278. static void agnodesetfinger(Agraph_t * g, Agnode_t * n, void *ignored)
  279. {
  280. Agsubnode_t template = {0};
  281. template.node = n;
  282. dtsearch(g->n_seq,&template);
  283. (void)ignored;
  284. }
  285. static void agnoderenew(Agraph_t * g, Agnode_t * n, void *ignored)
  286. {
  287. dtrenew(g->n_seq, dtfinger(g->n_seq));
  288. (void)n;
  289. (void)ignored;
  290. }
  291. int agnodebefore(Agnode_t *fst, Agnode_t *snd)
  292. {
  293. Agraph_t *g;
  294. Agnode_t *n, *nxt;
  295. g = agroot(fst);
  296. if (AGSEQ(fst) > AGSEQ(snd)) return SUCCESS;
  297. /* move snd out of the way somewhere */
  298. n = snd;
  299. if (agapply (g,(Agobj_t *)n, (agobjfn_t)agnodesetfinger, n, false) != SUCCESS) {
  300. return FAILURE;
  301. }
  302. {
  303. uint64_t seq = g->clos->seq[AGNODE] + 2;
  304. assert((seq & SEQ_MASK) == seq && "sequence ID overflow");
  305. AGSEQ(snd) = seq & SEQ_MASK;
  306. }
  307. if (agapply(g, (Agobj_t *)n, (agobjfn_t)agnoderenew, n, false) != SUCCESS) {
  308. return FAILURE;
  309. }
  310. n = agprvnode(g,snd);
  311. do {
  312. nxt = agprvnode(g,n);
  313. if (agapply(g, (Agobj_t *)n, (agobjfn_t)agnodesetfinger, n, false) != SUCCESS) {
  314. return FAILURE;
  315. }
  316. uint64_t seq = AGSEQ(n) + 1;
  317. assert((seq & SEQ_MASK) == seq && "sequence ID overflow");
  318. AGSEQ(n) = seq & SEQ_MASK;
  319. if (agapply(g, (Agobj_t *)n, (agobjfn_t)agnoderenew, n, false) != SUCCESS) {
  320. return FAILURE;
  321. }
  322. if (n == fst) break;
  323. n = nxt;
  324. } while (n);
  325. if (agapply(g, (Agobj_t *)snd, (agobjfn_t)agnodesetfinger, n, false) != SUCCESS) {
  326. return FAILURE;
  327. }
  328. assert(AGSEQ(fst) != 0 && "sequence ID overflow");
  329. AGSEQ(snd) = (AGSEQ(fst) - 1) & SEQ_MASK;
  330. if (agapply(g, (Agobj_t *)snd, (agobjfn_t)agnoderenew, snd, false) != SUCCESS) {
  331. return FAILURE;
  332. }
  333. return SUCCESS;
  334. }
  335. struct graphviz_node_set {
  336. Agsubnode_t **slots; ///< backing store for elements
  337. size_t size; ///< number of elements in the set
  338. size_t capacity_exp; ///< log₂ size of `slots`
  339. /// the minimum and maximum ID of nodes that have been inserted into the set
  340. ///
  341. /// These fields are used to optimize existence checks. `node_set_find` can
  342. /// quickly return `NULL` if the target node is outside the known range to
  343. /// have been inserted into the set. This seems niche, but negative queries
  344. /// like this are common enough that this is a measurable performance
  345. /// improvement.
  346. bool min_initialized;
  347. IDTYPE min;
  348. IDTYPE max;
  349. };
  350. /// a sentinel, marking a set slot from which an element has been deleted
  351. static Agsubnode_t *const TOMBSTONE = (Agsubnode_t *)-1;
  352. /// get the allocated size of the backing storage of a node set
  353. ///
  354. /// The capacity of a set is represented as its base-2 exponent, to make clearer
  355. /// to the compiler that it can implement `% capacity` as a mask, avoiding the
  356. /// expense of a modulo operation.
  357. ///
  358. /// @param self Set to inspect
  359. /// @return Capacity of the given set
  360. static size_t node_set_get_capacity(const node_set_t *self) {
  361. assert(self != NULL);
  362. return self->slots == NULL ? 0 : 1ul << self->capacity_exp;
  363. }
  364. node_set_t *node_set_new(void) { return gv_alloc(sizeof(node_set_t)); }
  365. /// compute a hash of a node
  366. ///
  367. /// If the suboptimal choice of using the ID here turns out to be bad for
  368. /// performance, this could be converted to a more sophisticated hashing
  369. /// algorithm. None of the callers depend on the exact implementation.
  370. ///
  371. /// @param id Identifier of element being sought/added
  372. /// @return Hash digest of the target node
  373. static size_t node_set_hash(IDTYPE id) { return (size_t)id; }
  374. void node_set_add(node_set_t *self, Agsubnode_t *item) {
  375. assert(self != NULL);
  376. assert(item != NULL);
  377. // a watermark ratio at which the set capacity should be expanded
  378. static const size_t OCCUPANCY_THRESHOLD_PERCENT = 70;
  379. // do we need to expand the backing store?
  380. size_t capacity = node_set_get_capacity(self);
  381. const bool grow = 100 * self->size >= OCCUPANCY_THRESHOLD_PERCENT * capacity;
  382. if (grow) {
  383. const size_t new_c = capacity == 0 ? 10 : self->capacity_exp + 1;
  384. Agsubnode_t **new_slots = gv_calloc(1ul << new_c, sizeof(Agsubnode_t *));
  385. // Construct a new set and copy everything into it. Note we need to rehash
  386. // because capacity (and hence modulo wraparound behavior) has changed. This
  387. // conveniently flushes out the tombstones too.
  388. node_set_t new_self = {.slots = new_slots, .capacity_exp = new_c};
  389. for (size_t i = 0; i < capacity; ++i) {
  390. // skip empty slots
  391. if (self->slots[i] == NULL) {
  392. continue;
  393. }
  394. // skip deleted slots
  395. if (self->slots[i] == TOMBSTONE) {
  396. continue;
  397. }
  398. node_set_add(&new_self, self->slots[i]);
  399. }
  400. // replace ourselves with this new set
  401. free(self->slots);
  402. *self = new_self;
  403. }
  404. // update bounds of what we have seen
  405. if (!self->min_initialized || item->node->base.tag.id < self->min) {
  406. self->min_initialized = true;
  407. self->min = item->node->base.tag.id;
  408. }
  409. if (item->node->base.tag.id > self->max) {
  410. self->max = item->node->base.tag.id;
  411. }
  412. capacity = node_set_get_capacity(self);
  413. assert(capacity > self->size);
  414. const size_t hash = node_set_hash(item->node->base.tag.id);
  415. for (size_t i = 0; i < capacity; ++i) {
  416. const size_t candidate = (hash + i) % capacity;
  417. // if we found an empty slot or a previously deleted slot, we can insert
  418. if (self->slots[candidate] == NULL || self->slots[candidate] == TOMBSTONE) {
  419. self->slots[candidate] = item;
  420. ++self->size;
  421. return;
  422. }
  423. }
  424. UNREACHABLE();
  425. }
  426. Agsubnode_t *node_set_find(node_set_t *self, IDTYPE key) {
  427. assert(self != NULL);
  428. // do we know immediately a node of this key has never been inserted?
  429. if (self->min_initialized && key < self->min) {
  430. return NULL;
  431. }
  432. if (key > self->max) {
  433. return NULL;
  434. }
  435. const size_t hash = node_set_hash(key);
  436. const size_t capacity = node_set_get_capacity(self);
  437. for (size_t i = 0; i < capacity; ++i) {
  438. const size_t candidate = (hash + i) % capacity;
  439. // if we found an empty slot, the sought item does not exist
  440. if (self->slots[candidate] == NULL) {
  441. return NULL;
  442. }
  443. // if we found a previously deleted slot, skip over it
  444. if (self->slots[candidate] == TOMBSTONE) {
  445. continue;
  446. }
  447. if (agsubnodeideq(self->slots[candidate], key)) {
  448. return self->slots[candidate];
  449. }
  450. }
  451. return NULL;
  452. }
  453. void node_set_remove(node_set_t *self, IDTYPE item) {
  454. assert(self != NULL);
  455. const size_t hash = node_set_hash(item);
  456. const size_t capacity = node_set_get_capacity(self);
  457. for (size_t i = 0; i < capacity; ++i) {
  458. const size_t candidate = (hash + i) % capacity;
  459. // if we found an empty slot, the sought item does not exist
  460. if (self->slots[candidate] == NULL) {
  461. return;
  462. }
  463. // if we found a previously deleted slot, skip over it
  464. if (self->slots[candidate] == TOMBSTONE) {
  465. continue;
  466. }
  467. if (agsubnodeideq(self->slots[candidate], item)) {
  468. assert(self->size > 0);
  469. self->slots[candidate] = TOMBSTONE;
  470. --self->size;
  471. return;
  472. }
  473. }
  474. }
  475. size_t node_set_size(const node_set_t *self) {
  476. assert(self != NULL);
  477. return self->size;
  478. }
  479. void node_set_free(node_set_t **self) {
  480. assert(self != NULL);
  481. if (*self != NULL) {
  482. free((*self)->slots);
  483. }
  484. free(*self);
  485. *self = NULL;
  486. }