refstr.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * @file
  3. * @brief API: cgraph.h, cghdr.h
  4. * @ingroup cgraph_utils
  5. */
  6. /*************************************************************************
  7. * Copyright (c) 2011 AT&T Intellectual Property
  8. * All rights reserved. This program and the accompanying materials
  9. * are made available under the terms of the Eclipse Public License v1.0
  10. * which accompanies this distribution, and is available at
  11. * https://www.eclipse.org/legal/epl-v10.html
  12. *
  13. * Contributors: Details at https://graphviz.org
  14. *************************************************************************/
  15. #include <cgraph/cghdr.h>
  16. #include <stddef.h>
  17. #include <stdbool.h>
  18. #include <stdio.h>
  19. /*
  20. * reference counted strings.
  21. */
  22. typedef struct {
  23. Dtlink_t link;
  24. uint64_t refcnt: sizeof(uint64_t) * 8 - 1;
  25. uint64_t is_html: 1;
  26. char *s;
  27. char store[1]; /* this is actually a dynamic array */
  28. } refstr_t;
  29. static Dtdisc_t Refstrdisc = {
  30. offsetof(refstr_t, s), /* key */
  31. -1, /* size */
  32. 0, /* link offset */
  33. NULL,
  34. agdictobjfree,
  35. NULL,
  36. };
  37. static Dict_t *Refdict_default;
  38. /* refdict:
  39. * Return the string dictionary associated with g.
  40. * If necessary, create it.
  41. * As a side-effect, set html masks. This assumes 8-bit bytes.
  42. */
  43. static Dict_t *refdict(Agraph_t * g)
  44. {
  45. Dict_t **dictref;
  46. if (g)
  47. dictref = &(g->clos->strdict);
  48. else
  49. dictref = &Refdict_default;
  50. if (*dictref == NULL) {
  51. *dictref = agdtopen(g, &Refstrdisc, Dttree);
  52. }
  53. return *dictref;
  54. }
  55. int agstrclose(Agraph_t * g)
  56. {
  57. return agdtclose(g, refdict(g));
  58. }
  59. static refstr_t *refsymbind(Dict_t * strdict, const char *s)
  60. {
  61. refstr_t key, *r;
  62. // Suppress Clang/GCC -Wcast-qual warning. Casting away const here is acceptable
  63. // as dtsearch does not modify its input key.
  64. #ifdef __GNUC__
  65. #pragma GCC diagnostic push
  66. #pragma GCC diagnostic ignored "-Wcast-qual"
  67. #endif
  68. key.s = (char*)s;
  69. #ifdef __GNUC__
  70. #pragma GCC diagnostic pop
  71. #endif
  72. r = dtsearch(strdict, &key);
  73. return r;
  74. }
  75. static char *refstrbind(Dict_t * strdict, const char *s)
  76. {
  77. refstr_t *r;
  78. r = refsymbind(strdict, s);
  79. if (r)
  80. return r->s;
  81. else
  82. return NULL;
  83. }
  84. char *agstrbind(Agraph_t * g, const char *s)
  85. {
  86. return refstrbind(refdict(g), s);
  87. }
  88. static char *agstrdup_internal(Agraph_t *g, const char *s, bool is_html) {
  89. refstr_t *r;
  90. Dict_t *strdict;
  91. size_t sz;
  92. if (s == NULL)
  93. return NULL;
  94. strdict = refdict(g);
  95. r = refsymbind(strdict, s);
  96. if (r)
  97. r->refcnt++;
  98. else {
  99. sz = sizeof(refstr_t) + strlen(s);
  100. if (g)
  101. r = agalloc(g, sz);
  102. else {
  103. r = malloc(sz);
  104. if (sz > 0 && r == NULL) {
  105. return NULL;
  106. }
  107. }
  108. r->refcnt = 1;
  109. r->is_html = is_html;
  110. strcpy(r->store, s);
  111. r->s = r->store;
  112. dtinsert(strdict, r);
  113. }
  114. return r->s;
  115. }
  116. char *agstrdup(Agraph_t *g, const char *s) {
  117. return agstrdup_internal(g, s, false);
  118. }
  119. char *agstrdup_html(Agraph_t *g, const char *s) {
  120. return agstrdup_internal(g, s, true);
  121. }
  122. int agstrfree(Agraph_t * g, const char *s)
  123. {
  124. refstr_t *r;
  125. Dict_t *strdict;
  126. if (s == NULL)
  127. return FAILURE;
  128. strdict = refdict(g);
  129. r = refsymbind(strdict, s);
  130. if (r && r->s == s) {
  131. r->refcnt--;
  132. if (r->refcnt == 0) {
  133. agdtdelete(g, strdict, r);
  134. }
  135. }
  136. if (r == NULL)
  137. return FAILURE;
  138. return SUCCESS;
  139. }
  140. /* aghtmlstr:
  141. * Return true if s is an HTML string.
  142. * We assume s points to the datafield store[0] of a refstr.
  143. */
  144. int aghtmlstr(const char *s)
  145. {
  146. const refstr_t *key;
  147. if (s == NULL)
  148. return 0;
  149. key = (const refstr_t *) (s - offsetof(refstr_t, store[0]));
  150. return key->is_html;
  151. }
  152. void agmarkhtmlstr(char *s)
  153. {
  154. refstr_t *key;
  155. if (s == NULL)
  156. return;
  157. key = (refstr_t *) (s - offsetof(refstr_t, store[0]));
  158. key->is_html = 1;
  159. }
  160. #ifdef DEBUG
  161. static int refstrprint(void *ptr, void *user) {
  162. refstr_t *r = ptr;
  163. (void)user;
  164. fprintf(stderr, "%s\n", r->s);
  165. return 0;
  166. }
  167. void agrefstrdump(Agraph_t * g)
  168. {
  169. dtwalk(refdict(g), refstrprint, 0);
  170. }
  171. #endif