mirror of
https://github.com/ccxvii/mujs.git
synced 2026-02-05 17:29:43 +08:00
Check string length when creating strings to not exceed a maximum, so we avoid integer overflows when concatenating strings. The string limit must be small enough that we'll not integer overflow in one concatenation (A + B + 1 must not overflow while still exceeding the string limit). Set the limit to 64KB for now. If we need 2GB strings then we will have to use double or int64 variables when calculating string lengths.
138 lines
3.2 KiB
C
138 lines
3.2 KiB
C
#include "jsi.h"
|
|
|
|
/* Dynamically grown string buffer */
|
|
|
|
void js_putc(js_State *J, js_Buffer **sbp, int c)
|
|
{
|
|
js_Buffer *sb = *sbp;
|
|
if (!sb) {
|
|
sb = js_malloc(J, sizeof *sb);
|
|
sb->n = 0;
|
|
sb->m = sizeof sb->s;
|
|
*sbp = sb;
|
|
} else if (sb->n == sb->m) {
|
|
sb = js_realloc(J, sb, (sb->m *= 2) + soffsetof(js_Buffer, s));
|
|
*sbp = sb;
|
|
}
|
|
sb->s[sb->n++] = c;
|
|
}
|
|
|
|
void js_puts(js_State *J, js_Buffer **sb, const char *s)
|
|
{
|
|
while (*s)
|
|
js_putc(J, sb, *s++);
|
|
}
|
|
|
|
void js_putm(js_State *J, js_Buffer **sb, const char *s, const char *e)
|
|
{
|
|
while (s < e)
|
|
js_putc(J, sb, *s++);
|
|
}
|
|
|
|
/* Use an AA-tree to quickly look up interned strings. */
|
|
|
|
struct js_StringNode
|
|
{
|
|
js_StringNode *left, *right;
|
|
int level;
|
|
char string[1];
|
|
};
|
|
|
|
static js_StringNode jsS_sentinel = { &jsS_sentinel, &jsS_sentinel, 0, ""};
|
|
|
|
static js_StringNode *jsS_newstringnode(js_State *J, const char *string, const char **result)
|
|
{
|
|
size_t n = strlen(string);
|
|
if (n > JS_STRLIMIT)
|
|
js_rangeerror(J, "invalid string length");
|
|
js_StringNode *node = js_malloc(J, soffsetof(js_StringNode, string) + n + 1);
|
|
node->left = node->right = &jsS_sentinel;
|
|
node->level = 1;
|
|
memcpy(node->string, string, n + 1);
|
|
return *result = node->string, node;
|
|
}
|
|
|
|
static js_StringNode *jsS_skew(js_StringNode *node)
|
|
{
|
|
if (node->left->level == node->level) {
|
|
js_StringNode *temp = node;
|
|
node = node->left;
|
|
temp->left = node->right;
|
|
node->right = temp;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
static js_StringNode *jsS_split(js_StringNode *node)
|
|
{
|
|
if (node->right->right->level == node->level) {
|
|
js_StringNode *temp = node;
|
|
node = node->right;
|
|
temp->right = node->left;
|
|
node->left = temp;
|
|
++node->level;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
static js_StringNode *jsS_insert(js_State *J, js_StringNode *node, const char *string, const char **result)
|
|
{
|
|
if (node != &jsS_sentinel) {
|
|
int c = strcmp(string, node->string);
|
|
if (c < 0)
|
|
node->left = jsS_insert(J, node->left, string, result);
|
|
else if (c > 0)
|
|
node->right = jsS_insert(J, node->right, string, result);
|
|
else
|
|
return *result = node->string, node;
|
|
node = jsS_skew(node);
|
|
node = jsS_split(node);
|
|
return node;
|
|
}
|
|
return jsS_newstringnode(J, string, result);
|
|
}
|
|
|
|
static void dumpstringnode(js_StringNode *node, int level)
|
|
{
|
|
int i;
|
|
if (node->left != &jsS_sentinel)
|
|
dumpstringnode(node->left, level + 1);
|
|
printf("%d: ", node->level);
|
|
for (i = 0; i < level; ++i)
|
|
putchar('\t');
|
|
printf("'%s'\n", node->string);
|
|
if (node->right != &jsS_sentinel)
|
|
dumpstringnode(node->right, level + 1);
|
|
}
|
|
|
|
void jsS_dumpstrings(js_State *J)
|
|
{
|
|
js_StringNode *root = J->strings;
|
|
printf("interned strings {\n");
|
|
if (root && root != &jsS_sentinel)
|
|
dumpstringnode(root, 1);
|
|
printf("}\n");
|
|
}
|
|
|
|
static void jsS_freestringnode(js_State *J, js_StringNode *node)
|
|
{
|
|
if (node->left != &jsS_sentinel) jsS_freestringnode(J, node->left);
|
|
if (node->right != &jsS_sentinel) jsS_freestringnode(J, node->right);
|
|
js_free(J, node);
|
|
}
|
|
|
|
void jsS_freestrings(js_State *J)
|
|
{
|
|
if (J->strings && J->strings != &jsS_sentinel)
|
|
jsS_freestringnode(J, J->strings);
|
|
}
|
|
|
|
const char *js_intern(js_State *J, const char *s)
|
|
{
|
|
const char *result;
|
|
if (!J->strings)
|
|
J->strings = &jsS_sentinel;
|
|
J->strings = jsS_insert(J, J->strings, s, &result);
|
|
return result;
|
|
}
|