1/* $Id$ $Revision$ */
2/* vim:set shiftwidth=4 ts=8: */
3
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 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14#include <cghdr.h>
15
16#ifdef DMALLOC
17#include "dmalloc.h"
18#endif
19
20/*
21 * reference counted strings.
22 */
23
24static uint64_t HTML_BIT; /* msbit of uint64_t */
25static uint64_t CNT_BITS; /* complement of HTML_BIT */
26
27typedef struct refstr_t {
28 Dtlink_t link;
29 uint64_t refcnt;
30 char *s;
31 char store[1]; /* this is actually a dynamic array */
32} refstr_t;
33
34static Dtdisc_t Refstrdisc = {
35 offsetof(refstr_t, s), /* key */
36 -1, /* size */
37 0, /* link offset */
38 NIL(Dtmake_f),
39 agdictobjfree,
40 NIL(Dtcompar_f),
41 NIL(Dthash_f),
42 agdictobjmem,
43 NIL(Dtevent_f)
44};
45
46static Dict_t *Refdict_default;
47
48/* refdict:
49 * Return the string dictionary associated with g.
50 * If necessary, create it.
51 * As a side-effect, set html masks. This assumes 8-bit bytes.
52 */
53static Dict_t *refdict(Agraph_t * g)
54{
55 Dict_t **dictref;
56
57 if (g)
58 dictref = &(g->clos->strdict);
59 else
60 dictref = &Refdict_default;
61 if (*dictref == NIL(Dict_t *)) {
62 *dictref = agdtopen(g, &Refstrdisc, Dttree);
63 HTML_BIT = ((unsigned int) 1) << (sizeof(unsigned int) * 8 - 1);
64 CNT_BITS = ~HTML_BIT;
65 }
66 return *dictref;
67}
68
69int agstrclose(Agraph_t * g)
70{
71 return agdtclose(g, refdict(g));
72}
73
74static refstr_t *refsymbind(Dict_t * strdict, char *s)
75{
76 refstr_t key, *r;
77 key.s = s;
78 r = (refstr_t *) dtsearch(strdict, &key);
79 return r;
80}
81
82static char *refstrbind(Dict_t * strdict, char *s)
83{
84 refstr_t *r;
85 r = refsymbind(strdict, s);
86 if (r)
87 return r->s;
88 else
89 return NIL(char *);
90}
91
92char *agstrbind(Agraph_t * g, char *s)
93{
94 return refstrbind(refdict(g), s);
95}
96
97char *agstrdup(Agraph_t * g, char *s)
98{
99 refstr_t *r;
100 Dict_t *strdict;
101 size_t sz;
102
103 if (s == NIL(char *))
104 return NIL(char *);
105 strdict = refdict(g);
106 r = refsymbind(strdict, s);
107 if (r)
108 r->refcnt++;
109 else {
110 sz = sizeof(refstr_t) + strlen(s);
111 if (g)
112 r = (refstr_t *) agalloc(g, sz);
113 else
114 r = (refstr_t *) malloc(sz);
115 r->refcnt = 1;
116 strcpy(r->store, s);
117 r->s = r->store;
118 dtinsert(strdict, r);
119 }
120 return r->s;
121}
122
123char *agstrdup_html(Agraph_t * g, char *s)
124{
125 refstr_t *r;
126 Dict_t *strdict;
127 size_t sz;
128
129 if (s == NIL(char *))
130 return NIL(char *);
131 strdict = refdict(g);
132 r = refsymbind(strdict, s);
133 if (r)
134 r->refcnt++;
135 else {
136 sz = sizeof(refstr_t) + strlen(s);
137 if (g)
138 r = (refstr_t *) agalloc(g, sz);
139 else
140 r = (refstr_t *) malloc(sz);
141 r->refcnt = 1 | HTML_BIT;
142 strcpy(r->store, s);
143 r->s = r->store;
144 dtinsert(strdict, r);
145 }
146 return r->s;
147}
148
149int agstrfree(Agraph_t * g, char *s)
150{
151 refstr_t *r;
152 Dict_t *strdict;
153
154 if (s == NIL(char *))
155 return FAILURE;
156
157 strdict = refdict(g);
158 r = refsymbind(strdict, s);
159 if (r && (r->s == s)) {
160 r->refcnt--;
161 if ((r->refcnt && CNT_BITS) == 0) {
162 agdtdelete(g, strdict, r);
163 /*
164 if (g) agfree(g,r);
165 else free(r);
166 */
167 }
168 }
169 if (r == NIL(refstr_t *))
170 return FAILURE;
171 return SUCCESS;
172}
173
174/* aghtmlstr:
175 * Return true if s is an HTML string.
176 * We assume s points to the datafield store[0] of a refstr.
177 */
178int aghtmlstr(char *s)
179{
180 refstr_t *key;
181
182 if (s == NULL)
183 return 0;
184 key = (refstr_t *) (s - offsetof(refstr_t, store[0]));
185 return (key->refcnt & HTML_BIT);
186}
187
188void agmarkhtmlstr(char *s)
189{
190 refstr_t *key;
191
192 if (s == NULL)
193 return;
194 key = (refstr_t *) (s - offsetof(refstr_t, store[0]));
195 key->refcnt |= HTML_BIT;
196}
197
198#ifdef DEBUG
199static int refstrprint(Dict_t * dict, void *ptr, void *user)
200{
201 refstr_t *r;
202
203 NOTUSED(dict);
204 r = ptr;
205 NOTUSED(user);
206 write(2, r->s, strlen(r->s));
207 write(2, "\n", 1);
208 return 0;
209}
210
211void agrefstrdump(Agraph_t * g)
212{
213 dtwalk(Refdict_default, refstrprint, 0);
214}
215#endif
216