summaryrefslogtreecommitdiff
path: root/source/luametatex/source/mp/mpw/mpstrings.w
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/mp/mpw/mpstrings.w')
-rw-r--r--source/luametatex/source/mp/mpw/mpstrings.w452
1 files changed, 452 insertions, 0 deletions
diff --git a/source/luametatex/source/mp/mpw/mpstrings.w b/source/luametatex/source/mp/mpw/mpstrings.w
new file mode 100644
index 000000000..07236bd9c
--- /dev/null
+++ b/source/luametatex/source/mp/mpw/mpstrings.w
@@ -0,0 +1,452 @@
+% This file is part of MetaPost. The MetaPost program is in the public domain.
+
+@* String handling.
+
+@ First, we will need some stuff from other files.
+@c
+# include "mpconfig.h"
+# include "mpstrings.h"
+
+@ Then there is some stuff we need to prepare ourselves.
+
+@(mpstrings.h@>=
+# ifndef MPSTRINGS_H
+# define MPSTRINGS_H 1
+
+# include "mp.h"
+
+@<Definitions@>
+
+# endif
+
+@ Here are the functions needed for the avl construction.
+
+@<Definitions@>=
+void *mp_aux_copy_strings_entry (const void *p);
+
+@ An earlier version of this function used |strncmp|, but that produces wrong
+results in some cases.
+
+@c
+# define STRCMP_RESULT(a) ((a) < 0 ? -1 : ((a) > 0 ? 1 : 0))
+
+static int mp_aux_comp_strings_entry(void *p, const void *pa, const void *pb)
+{
+ const mp_lstring *a = (const mp_lstring *) pa;
+ const mp_lstring *b = (const mp_lstring *) pb;
+ unsigned char *s = a->str;
+ unsigned char *t = b->str;
+ size_t l = a->len <= b->len ? a->len : b->len;
+ (void) p;
+ while (l-- > 0) {
+ if (*s != *t) {
+ return STRCMP_RESULT(*s - *t);
+ } else {
+ s++;
+ t++;
+ }
+ }
+ return STRCMP_RESULT((int)(a->len - b->len));
+}
+
+void *mp_aux_copy_strings_entry(const void *p)
+{
+ mp_string ff = mp_memory_allocate(sizeof(mp_lstring));
+ if (ff) {
+ const mp_lstring *fp = (const mp_lstring *) p;
+ ff->str = mp_memory_allocate((size_t) fp->len + 1);
+ if (ff->str) {
+ memcpy((char *) ff->str, (char *) fp->str, fp->len + 1);
+ ff->len = fp->len;
+ ff->refs = 0;
+ return ff;
+ }
+ }
+ return NULL;
+}
+
+static void *delete_strings_entry(void *p)
+{
+ mp_string ff = (mp_string) p;
+ mp_memory_free(ff->str);
+ mp_memory_free(ff);
+ return NULL;
+}
+
+@ Actually creating strings is done by |make_string|, but in order to do so it
+needs a way to create a new, empty string structure.
+
+@ @c
+static mp_string new_strings_entry(void)
+{
+ mp_string ff = mp_memory_allocate(sizeof(mp_lstring));
+ ff->str = NULL;
+ ff->len = 0;
+ ff->refs = 0;
+ return ff;
+}
+
+@ Some even more low-level functions are these:
+
+@<Definitions@>=
+extern char *mp_strdup (const char *s);
+extern char *mp_strndup (const char *s, size_t l);
+extern int mp_strcmp (const char *a, const char *b);
+
+@ @c
+char *mp_strdup(const char *s)
+{
+ if (s) {
+ char *w = lmt_memory_strdup(s);
+ if (w) {
+ return w;
+ } else {
+ printf("mplib ran out of memory, case 3");
+ exit(EXIT_FAILURE);
+ }
+ }
+ return NULL;
+}
+
+
+char *mp_strndup(const char *p, size_t l)
+{
+ if (p) {
+ char *r = mp_memory_allocate(l * sizeof(char) + 1);
+ if (r) {
+ char *s = memcpy(r, p, l);
+ *(s + l) = '\0';
+ return s;
+ } else {
+ printf("mplib ran out of memory, case 4");
+ exit(EXIT_FAILURE);
+ }
+ }
+ return NULL;
+}
+
+/*
+char *mp_strndup(const char *p, size_t l)
+{
+ if (p) {
+ char *w = strndup(p, l);
+ if (w) {
+ return w;
+ } else {
+ printf("mplib ran out of memory, case 4");
+ exit(EXIT_FAILURE);
+ }
+ }
+ return NULL;
+}
+*/
+
+int mp_strcmp(const char *a, const char *b)
+{
+ return a == NULL ? (b == NULL ? 0 : -1) : (b == NULL ? 1 : strcmp(a, b));
+}
+
+@ @c
+void mp_initialize_strings(MP mp)
+{
+ mp->strings = avl_create(mp_aux_comp_strings_entry, mp_aux_copy_strings_entry, delete_strings_entry, mp_memory_allocate, mp_memory_free, NULL);
+ mp->cur_string = NULL;
+ mp->cur_length = 0;
+ mp->cur_string_size = 0;
+}
+
+@ @c
+void mp_dealloc_strings(MP mp)
+{
+ if (mp->strings != NULL) {
+ avl_destroy(mp->strings);
+ } else {
+ mp->strings = NULL;
+ mp_memory_free(mp->cur_string);
+ mp->cur_string = NULL;
+ mp->cur_length = 0;
+ mp->cur_string_size = 0;
+ }
+}
+
+@ Here are the definitions:
+
+@<Definitions@>=
+extern void mp_initialize_strings (MP mp);
+extern void mp_dealloc_strings (MP mp);
+
+@ Most printing is done from |char *|s, but sometimes not. Here are functions
+that convert an internal string into a |char *| for use by the printing routines,
+and vice versa.
+
+@<Definitions@>=
+char *mp_str (MP mp, mp_string s);
+mp_string mp_rtsl (MP mp, const char *s, size_t l);
+mp_string mp_rts (MP mp, const char *s);
+mp_string mp_make_string (MP mp);
+
+@ @c
+char *mp_str(MP mp, mp_string ss)
+{
+ (void) mp;
+ return (char *) ss->str;
+}
+
+@ @c
+mp_string mp_rtsl(MP mp, const char *s, size_t l)
+{
+ mp_string nstr;
+ mp_string str = new_strings_entry();
+ str->str = (unsigned char *) mp_strndup(s, l);
+ str->len = l;
+ nstr = (mp_string) avl_find(str, mp->strings);
+ if (nstr == NULL) {
+ avl_ins(str, mp->strings, avl_false);
+ nstr = (mp_string) avl_find(str, mp->strings);
+ }
+ delete_strings_entry(str);
+ add_str_ref(nstr);
+ return nstr;
+}
+
+@ @c
+mp_string mp_rts(MP mp, const char *s)
+{
+ return mp_rtsl(mp, s, strlen(s));
+}
+
+@ Strings are created by appending character codes to |cur_string|. The
+|mp_append_char| function, defined here, does not check to see if the buffer overflows;
+this test is supposed to be made before |mp_append_char| is used.
+
+To test if there is room to append |l| more characters to |cur_string|, we shall
+write |str_room(l)|, which tries to make sure there is enough room in the
+|cur_string|.
+
+@<Definitions@>=
+extern void mp_append_char (MP mp, unsigned char c);
+extern void mp_append_str (MP mp, const char *s);
+extern void mp_str_room (MP mp, int wsize);
+
+@ @c
+# define EXTRA_STRING 500
+
+void mp_str_room(MP mp, int wsize)
+{
+ /* we always add one more */
+ if ((mp->cur_length + (size_t) wsize + 1) > mp->cur_string_size) {
+ size_t nsize = mp->cur_string_size + mp->cur_string_size / 5 + EXTRA_STRING;
+ if (nsize < (size_t) wsize) {
+ nsize = (size_t) wsize + EXTRA_STRING;
+ }
+ mp->cur_string = (unsigned char *) mp_memory_reallocate(mp->cur_string, (size_t) nsize * sizeof(unsigned char));
+ memset(mp->cur_string + mp->cur_length, 0, nsize-mp->cur_length);
+ mp->cur_string_size = nsize;
+ }
+}
+
+void mp_append_char(MP mp, unsigned char c)
+{
+ *(mp->cur_string + mp->cur_length) = c;
+ mp->cur_length++;
+}
+
+void mp_append_str(MP mp, const char *s)
+{
+ int j = 0;
+ while ((unsigned char) s[j]) {
+ *(mp->cur_string + mp->cur_length) = s[j++];
+ mp->cur_length++;
+ }
+}
+
+@ At the very start of the metapost run and each time after |make_string| has
+stored a new string in the avl tree, the |cur_string| variable has to be prepared
+so that it will be ready to start creating a new string. The initial size is
+fairly arbitrary, but setting it a little higher than expected helps prevent
+|reallocs|.
+
+@<Definitions@>=
+void mp_reset_cur_string (MP mp);
+
+@ @c
+void mp_reset_cur_string(MP mp)
+{
+ mp_memory_free(mp->cur_string);
+ mp->cur_length = 0;
+ mp->cur_string_size = 63;
+ mp->cur_string = (unsigned char *) mp_memory_allocate(64 * sizeof(unsigned char));
+ memset(mp->cur_string, 0, 64);
+}
+
+@ \MP's string expressions are implemented in a brute-force way: Every new string
+or substring that is needed is simply stored into the string pool. Space is
+eventually reclaimed using the aid of a simple system system of reference counts.
+@^reference counts@>
+
+The number of references to string number |s| will be |s->refs|. The special
+value |s->refs=MAX_STR_REF=127| is used to denote an unknown positive number of
+references; such strings will never be recycled. If a string is ever referred to
+more than 126 times, simultaneously, we put it in this category.
+
+@<Definitions@>=
+# define MAX_STR_REF 127 /* \quote {infinite} number of references */
+# define add_str_ref(A) { if ( (A)->refs < MAX_STR_REF ) ((A)->refs)++; }
+
+@ Here's what we do when a string reference disappears:
+
+@<Definitions@>=
+# define delete_str_ref(A) do { \
+ if ((A)->refs < MAX_STR_REF) { \
+ if ((A)->refs > 1) \
+ ((A)->refs)--; \
+ else \
+ mp_flush_string(mp, (A)); \
+ } \
+ } while (0)
+
+@ @<Definitions@>=
+void mp_flush_string (MP mp, mp_string s);
+
+@ @c
+void mp_flush_string(MP mp, mp_string s) {
+ if (s->refs == 0) {
+ mp->strs_in_use--;
+ mp->pool_in_use = mp->pool_in_use - (int) s->len;
+ avl_del(s, mp->strings, NULL);
+ }
+}
+
+@ Some C literals that are used as values cannot be simply added, their reference
+count has to be set such that they can not be flushed.
+
+@c
+mp_string mp_intern(MP mp, const char *s)
+{
+ mp_string r = mp_rts(mp, s);
+ r->refs = MAX_STR_REF;
+ return r;
+}
+
+@ @<Definitions@>=
+mp_string mp_intern (MP mp, const char *s);
+
+@ Once a sequence of characters has been appended to |cur_string|, it officially
+becomes a string when the function |make_string| is called. This function returns
+a pointer to the new string as its value.
+
+@<Definitions@>=
+mp_string mp_make_string (MP mp);
+
+@ @c
+mp_string mp_make_string(MP mp)
+{
+ /* current string enters the pool */
+ mp_string str;
+ mp_lstring tmp;
+ tmp.str = mp->cur_string;
+ tmp.len = mp->cur_length;
+ str = (mp_string) avl_find(&tmp, mp->strings);
+ if (str == NULL) {
+ str = mp_memory_allocate(sizeof(mp_lstring));
+ str->str = mp->cur_string;
+ str->len = tmp.len;
+ avl_ins(str, mp->strings, avl_false);
+ str = (mp_string) avl_find(&tmp, mp->strings);
+ mp->pool_in_use = mp->pool_in_use + (int) str->len;
+ if (mp->pool_in_use > mp->max_pl_used) {
+ mp->max_pl_used = mp->pool_in_use;
+ }
+ mp->strs_in_use++;
+ if (mp->strs_in_use > mp->max_strs_used) {
+ mp->max_strs_used = mp->strs_in_use;
+ }
+ }
+ add_str_ref(str);
+ mp_reset_cur_string (mp);
+ return str;
+}
+
+@ Here is a routine that compares two strings in the string pool, and it does not
+assume that they have the same length. If the first string is lexicographically
+greater than, less than, or equal to the second, the result is respectively
+positive, negative, or zero.
+
+@<Definitions@>=
+int mp_str_vs_str (MP mp, mp_string s, mp_string t);
+
+@ @c
+int mp_str_vs_str(MP mp, mp_string s, mp_string t)
+{
+ (void) mp;
+ return mp_aux_comp_strings_entry(NULL, (const void *) s, (const void *) t);
+}
+
+@ @<Definitions@>=
+mp_string mp_cat (MP mp, mp_string a, mp_string b);
+
+@ @c
+mp_string mp_cat(MP mp, mp_string a, mp_string b)
+{
+ mp_string str;
+ size_t saved_cur_length = mp->cur_length;
+ unsigned char *saved_cur_string = mp->cur_string;
+ size_t saved_cur_string_size = mp->cur_string_size;
+ size_t needed = a->len + b->len;
+ mp->cur_length = 0;
+ /* |mp->cur_string = NULL;| needs malloc, spotted by clang */
+ mp->cur_string = (unsigned char *) mp_memory_allocate((size_t) (needed + 1) * sizeof(unsigned char));
+ mp->cur_string_size = 0;
+ mp_str_room(mp, (int) needed + 1);
+ memcpy(mp->cur_string, a->str, a->len);
+ memcpy(mp->cur_string + a->len, b->str, b->len);
+ mp->cur_length = needed;
+ mp->cur_string[needed] = '\0';
+ str = mp_make_string(mp);
+ mp_memory_free(mp->cur_string); /* created by |mp_make_string| */
+ mp->cur_length = saved_cur_length;
+ mp->cur_string = saved_cur_string;
+ mp->cur_string_size = saved_cur_string_size;
+ return str;
+}
+
+@ @<Definitions@>=
+mp_string mp_chop_string (MP mp, mp_string s, int a, int b);
+
+@ @c
+mp_string mp_chop_string(MP mp, mp_string s, int a, int b)
+{
+ int l = (int) s->len;
+ int reversed;
+ if (a <= b) {
+ reversed = 0;
+ } else {
+ int k = a;
+ a = b;
+ b = k;
+ reversed = 1;
+ }
+ if (a < 0) {
+ a = 0;
+ if (b < 0) {
+ b = 0;
+ }
+ }
+ if (b > l) {
+ b = l;
+ if (a > l) {
+ a = l;
+ }
+ }
+ mp_str_room(mp, (size_t) (b - a));
+ if (reversed) {
+ for (int k = b - 1; k >= a; k--) {
+ mp_append_char(mp, *(s->str + k));
+ }
+ } else {
+ for (int k = a; k < b; k++) {
+ mp_append_char(mp, *(s->str + k));
+ }
+ }
+ return mp_make_string(mp);
+}