mpstrings.c /size: 9931 b    last modification: 2025-02-21 11:03
1/* 
2    This file was generated by "mtxrun --script "mtx-wtoc.lua" from the metapost cweb files but 
3    now maintained as C file.
4*/
5
6# include "mpstrings.h"
7
8/*tex
9    Here is a routine that compares two strings in the string pool, and it does not assume that 
10    they have the same length. If the first string is lexicographically greater than, less than, 
11    or equal to the second, the result is respectively positive, negative, or zero.
12
13    An earlier version of this function used |strncmp|, but that produces wrong results in some 
14    cases.
15*/
16
17# define STRCMP_RESULT(a) ((a) < 0 ? -1 : ((a) > 0 ? 1 : 0))
18
19static int mp_aux_comp_strings_entry(void *p, const void *pa, const void *pb)
20{
21    const mp_lstring *a = (const mp_lstring *) pa;
22    const mp_lstring *b = (const mp_lstring *) pb;
23    unsigned char *s = a->str;
24    unsigned char *t = b->str;
25    size_t l = a->len <= b->len ? a->len : b->len;
26    (void) p;
27    while (l-- > 0) {
28        if (*s != *t) {
29            return STRCMP_RESULT(*s - *t);
30        } else {
31            s++;
32            t++;
33        }
34    }
35    return STRCMP_RESULT((int)(a->len - b->len));
36}
37
38void *mp_aux_copy_strings_entry(const void *p)
39{
40    mp_string ff = mp_memory_allocate(sizeof(mp_lstring));
41    if (ff) {
42        const mp_lstring *fp = (const mp_lstring *) p;
43        ff->str = mp_memory_allocate((size_t) fp->len + 1);
44        if (ff->str) {
45            memcpy((char *) ff->str, (char *) fp->str, fp->len + 1);
46            ff->len = fp->len;
47            ff->refs = 0;
48            return ff;
49        }
50    }
51    return NULL;
52}
53
54static void *delete_strings_entry(void *p)
55{
56    mp_string ff = (mp_string) p;
57    mp_memory_free(ff->str);
58    mp_memory_free(ff);
59    return NULL;
60}
61
62static mp_string new_strings_entry(void)
63{
64    mp_string ff = mp_memory_allocate(sizeof(mp_lstring));
65    ff->str = NULL;
66    ff->len = 0;
67    ff->refs = 0;
68    return ff;
69}
70
71char *mp_strdup(const char *s)
72{
73    if (s) {
74        char *w = lmt_memory_strdup(s);
75        if (w) {
76            return w;
77        } else {
78            printf("mplib ran out of memory, case 3");
79            exit(EXIT_FAILURE);
80        }
81    }
82    return NULL;
83}
84
85char *mp_strndup(const char *p, size_t l)
86{
87    if (p) {
88        char *r = mp_memory_allocate(l * sizeof(char) + 1);
89        if (r) {
90            char *s = memcpy(r, p, l);
91            *(s + l) = '\0';
92            return s;
93        } else {
94            printf("mplib ran out of memory, case 4");
95            exit(EXIT_FAILURE);
96        }
97    }
98    return NULL;
99}
100
101// int mp_strcmp(const char *a, const char *b)
102// {
103//     return a == NULL ? (b == NULL ? 0 : -1) : (b == NULL ? 1 : strcmp(a, b));
104// }
105
106void mp_initialize_strings(MP mp)
107{
108    mp->strings = avl_create(mp_aux_comp_strings_entry, mp_aux_copy_strings_entry, delete_strings_entry, mp_memory_allocate, mp_memory_free, NULL);
109    mp->cur_string = NULL;
110    mp->cur_length = 0;
111    mp->cur_string_size = 0;
112}
113
114void mp_dealloc_strings(MP mp)
115{
116    if (mp->strings != NULL) {
117        avl_destroy(mp->strings);
118    } else {
119        mp->strings = NULL;
120        mp_memory_free(mp->cur_string);
121        mp->cur_string = NULL;
122        mp->cur_length = 0;
123        mp->cur_string_size = 0;
124    }
125}
126
127/*tex
128    Most printing is done from |char *|s, but sometimes not. Here are functions that convert an 
129    internal string into a |char *| for use by the printing routines, and vice versa.
130*/
131
132// char *mp_str(MP mp, mp_string ss)
133// {
134//     (void) mp;
135//     return (char *) ss->str;
136// }
137
138mp_string mp_rtsl(MP mp, const char *s, size_t l)
139{
140    mp_string nstr;
141    mp_string str = new_strings_entry();
142    str->str = (unsigned char *) mp_strndup(s, l);
143    str->len = l;
144    nstr = (mp_string) avl_find(str, mp->strings);
145    if (nstr == NULL) {
146        avl_ins(str, mp->strings, avl_false);
147        nstr = (mp_string) avl_find(str, mp->strings);
148    }
149    delete_strings_entry(str);
150    add_str_ref(nstr);
151    return nstr;
152}
153
154mp_string mp_rts(MP mp, const char *s)
155{
156    return mp_rtsl(mp, s, strlen(s));
157}
158
159
160/*tex
161    Strings are created by appending character codes to |cur_string|. The |mp_append_char| 
162    function, defined here, does not check to see if the buffer overflows; this test is supposed 
163    to be made before |mp_append_char| is used.
164
165    To test if there is room to append |l| more characters to |cur_string|, we shall write 
166    |str_room(l)|, which tries to make sure there is enough room in the |cur_string|. At the very 
167    start of the metapost run and each time after |make_string| has stored a new string in the 
168    avl tree, the |cur_string| variable has to be prepared so that it will be ready to start 
169    creating a new string. The initial size is fairly arbitrary, but setting it a little higher 
170    than expected helps prevent |reallocs|.
171*/
172
173# define EXTRA_STRING 500
174
175void mp_str_room(MP mp, int wsize)
176{
177    /* we always add one more */
178    if ((mp->cur_length + (size_t) wsize + 1) > mp->cur_string_size) {
179        size_t nsize = mp->cur_string_size + mp->cur_string_size / 5 + EXTRA_STRING;
180        if (nsize < (size_t) wsize) {
181            nsize = (size_t) wsize + EXTRA_STRING;
182        }
183        mp->cur_string = (unsigned char *) mp_memory_reallocate(mp->cur_string, (size_t) nsize * sizeof(unsigned char));
184        memset(mp->cur_string + mp->cur_length, 0, nsize-mp->cur_length);
185        mp->cur_string_size = nsize;
186    }
187}
188
189void mp_append_char(MP mp, unsigned char c)
190{
191    *(mp->cur_string + mp->cur_length) = c;
192    mp->cur_length++;
193}
194
195void mp_append_str(MP mp, const char *s)
196{
197    int j = 0;
198    while ((unsigned char) s[j]) {
199        *(mp->cur_string + mp->cur_length) = s[j++];
200        mp->cur_length++;
201    }
202}
203
204void mp_reset_cur_string(MP mp)
205{
206    mp_memory_free(mp->cur_string);
207    mp->cur_length = 0;
208    mp->cur_string_size = 63;
209    mp->cur_string = (unsigned char *) mp_memory_allocate(64 * sizeof(unsigned char));
210    memset(mp->cur_string, 0, 64);
211}
212
213/*tex
214
215    \MP's string expressions are implemented in a brute-force way: Every new string or substring 
216    that is needed is simply stored into the string pool. Space is eventually reclaimed using the 
217    aid of a simple system system of reference counts.
218
219    The number of references to string number |s| will be |s->refs|. The special value |s->refs =
220    MAX_STR_REF=127| is used to denote an unknown positive number of references; such strings will
221    never be recycled. If a string is ever referred to more than 126 times, simultaneously, we put 
222    it in this category. Here's what we do when a string reference disappears:
223
224*/
225
226void mp_flush_string(MP mp, mp_string s) {
227    if (s->refs == 0) {
228        mp->strs_in_use--;
229        mp->pool_in_use = mp->pool_in_use - (int) s->len;
230        avl_del(s, mp->strings, NULL);
231    }
232}
233
234/*tex
235    Some C literals that are used as values cannot be simply added, their reference count has to be
236    set such that they can not be flushed.
237*/
238
239mp_string mp_intern(MP mp, const char *s)
240{
241    mp_string r = mp_rts(mp, s);
242    r->refs = MAX_STR_REF;
243    return r;
244}
245
246/*tex
247
248    Once a sequence of characters has been appended to |cur_string|, it officially becomes a string
249    when the function |make_string| is called. This function returns a pointer to the new string as 
250    its value.
251
252 */
253
254mp_string mp_make_string(MP mp)
255{
256    /* current string enters the pool */
257    mp_string str;
258    mp_lstring tmp;
259    tmp.str = mp->cur_string;
260    tmp.len = mp->cur_length;
261    str = (mp_string) avl_find(&tmp, mp->strings);
262    if (str == NULL) {
263        str = mp_memory_allocate(sizeof(mp_lstring));
264        str->str = mp->cur_string;
265        str->len = tmp.len;
266        avl_ins(str, mp->strings, avl_false);
267        str = (mp_string) avl_find(&tmp, mp->strings);
268        mp->pool_in_use = mp->pool_in_use + (int) str->len;
269        if (mp->pool_in_use > mp->max_pl_used) {
270            mp->max_pl_used = mp->pool_in_use;
271        }
272        mp->strs_in_use++;
273        if (mp->strs_in_use > mp->max_strs_used) {
274            mp->max_strs_used = mp->strs_in_use;
275        }
276    }
277    add_str_ref(str);
278    mp_reset_cur_string (mp);
279    return str;
280}
281
282int mp_str_vs_str(MP mp, mp_string s, mp_string t)
283{
284    (void) mp;
285    return mp_aux_comp_strings_entry(NULL, (const void *) s, (const void *) t);
286}
287
288mp_string mp_cat(MP mp, mp_string a, mp_string b)
289{
290    mp_string str;
291    size_t saved_cur_length = mp->cur_length;
292    unsigned char *saved_cur_string = mp->cur_string;
293    size_t saved_cur_string_size = mp->cur_string_size;
294    size_t needed = a->len + b->len;
295    mp->cur_length = 0;
296    /* |mp->cur_string = NULL;|  needs malloc, spotted by clang */
297    mp->cur_string = (unsigned char *) mp_memory_allocate((size_t) (needed + 1) * sizeof(unsigned char));
298    mp->cur_string_size = 0;
299    mp_str_room(mp, (int) needed + 1);
300    memcpy(mp->cur_string, a->str, a->len);
301    memcpy(mp->cur_string + a->len, b->str, b->len);
302    mp->cur_length = needed;
303    mp->cur_string[needed] = '\0';
304    str = mp_make_string(mp);
305    mp_memory_free(mp->cur_string); /* created by |mp_make_string| */
306    mp->cur_length = saved_cur_length;
307    mp->cur_string = saved_cur_string;
308    mp->cur_string_size = saved_cur_string_size;
309    return str;
310}
311
312mp_string mp_chop_string(MP mp, mp_string s, int a, int b)
313{
314    int l = (int) s->len;
315    int reversed;
316    if (a <= b) {
317        reversed = 0;
318    } else {
319        int k = a;
320        a = b;
321        b = k;
322        reversed = 1;
323    }
324    if (a < 0) {
325        a = 0;
326        if (b < 0) {
327            b = 0;
328        }
329    }
330    if (b > l) {
331        b = l;
332        if (a > l) {
333            a = l;
334        }
335    }
336    mp_str_room(mp, (b - a));
337    if (reversed) {
338        for (int k = b - 1; k >= a; k--) {
339            mp_append_char(mp, *(s->str + k));
340        }
341    } else {
342        for (int k = a; k < b; k++) {
343            mp_append_char(mp, *(s->str + k));
344        }
345    }
346    return mp_make_string(mp);
347}
348