@@ -545,7 +545,12 @@ typedef enum {
545
545
JS_ATOM_KIND_PRIVATE,
546
546
} JSAtomKindEnum;
547
547
548
- #define JS_ATOM_HASH_MASK ((1 << 30) - 1)
548
+ typedef enum {
549
+ JS_STRING_KIND_NORMAL,
550
+ JS_STRING_KIND_SLICE,
551
+ } JSStringKind;
552
+
553
+ #define JS_ATOM_HASH_MASK ((1 << 29) - 1)
549
554
550
555
struct JSString {
551
556
JSRefCountHeader header; /* must come first, 32-bit */
@@ -554,7 +559,8 @@ struct JSString {
554
559
/* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,
555
560
for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3
556
561
XXX: could change encoding to have one more bit in hash */
557
- uint32_t hash : 30;
562
+ uint32_t hash : 29;
563
+ uint8_t kind : 1;
558
564
uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
559
565
uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
560
566
JSWeakRefRecord *first_weak_ref;
@@ -563,14 +569,39 @@ struct JSString {
563
569
#endif
564
570
};
565
571
572
+ typedef struct JSStringSlice {
573
+ JSString *parent;
574
+ uint32_t start; // in characters, not bytes
575
+ } JSStringSlice;
576
+
566
577
static inline uint8_t *str8(JSString *p)
567
578
{
568
- return (void *)(p + 1);
579
+ JSStringSlice *slice;
580
+
581
+ switch (p->kind) {
582
+ case JS_STRING_KIND_NORMAL:
583
+ return (void *)&p[1];
584
+ case JS_STRING_KIND_SLICE:
585
+ slice = (void *)&p[1];
586
+ return str8(slice->parent) + slice->start;
587
+ }
588
+ abort();
589
+ return NULL;
569
590
}
570
591
571
592
static inline uint16_t *str16(JSString *p)
572
593
{
573
- return (void *)(p + 1);
594
+ JSStringSlice *slice;
595
+
596
+ switch (p->kind) {
597
+ case JS_STRING_KIND_NORMAL:
598
+ return (void *)&p[1];
599
+ case JS_STRING_KIND_SLICE:
600
+ slice = (void *)&p[1];
601
+ return str16(slice->parent) + slice->start;
602
+ }
603
+ abort();
604
+ return NULL;
574
605
}
575
606
576
607
typedef struct JSClosureVar {
@@ -2049,6 +2080,7 @@ static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char
2049
2080
str->header.ref_count = 1;
2050
2081
str->is_wide_char = is_wide_char;
2051
2082
str->len = max_len;
2083
+ str->kind = JS_STRING_KIND_NORMAL;
2052
2084
str->atom_type = 0;
2053
2085
str->hash = 0; /* optional but costless */
2054
2086
str->hash_next = 0; /* optional */
@@ -2069,18 +2101,28 @@ static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
2069
2101
return p;
2070
2102
}
2071
2103
2104
+ static inline void js_free_string0(JSRuntime *rt, JSString *str);
2105
+
2072
2106
/* same as JS_FreeValueRT() but faster */
2073
2107
static inline void js_free_string(JSRuntime *rt, JSString *str)
2074
2108
{
2075
- if (--str->header.ref_count <= 0) {
2076
- if (str->atom_type) {
2077
- JS_FreeAtomStruct(rt, str);
2078
- } else {
2109
+ if (--str->header.ref_count <= 0)
2110
+ js_free_string0(rt, str);
2111
+ }
2112
+
2113
+ static inline void js_free_string0(JSRuntime *rt, JSString *str)
2114
+ {
2115
+ if (str->atom_type) {
2116
+ JS_FreeAtomStruct(rt, str);
2117
+ } else {
2079
2118
#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS
2080
- list_del(&str->link);
2119
+ list_del(&str->link);
2081
2120
#endif
2082
- js_free_rt(rt, str);
2121
+ if (str->kind == JS_STRING_KIND_SLICE) {
2122
+ JSStringSlice *slice = (void *)&str[1];
2123
+ js_free_string(rt, slice->parent); // safe, recurses only 1 level
2083
2124
}
2125
+ js_free_rt(rt, str);
2084
2126
}
2085
2127
}
2086
2128
@@ -2962,6 +3004,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
2962
3004
p->header.ref_count = 1;
2963
3005
p->is_wide_char = str->is_wide_char;
2964
3006
p->len = str->len;
3007
+ p->kind = JS_STRING_KIND_NORMAL;
2965
3008
#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS
2966
3009
list_add_tail(&p->link, &rt->string_list);
2967
3010
#endif
@@ -2976,6 +3019,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
2976
3019
p->header.ref_count = 1;
2977
3020
p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
2978
3021
p->len = 0;
3022
+ p->kind = JS_STRING_KIND_NORMAL;
2979
3023
#ifdef ENABLE_DUMPS // JS_DUMP_LEAKS
2980
3024
list_add_tail(&p->link, &rt->string_list);
2981
3025
#endif
@@ -3680,13 +3724,39 @@ static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
3680
3724
3681
3725
static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
3682
3726
{
3683
- int len = end - start;
3727
+ JSStringSlice *slice;
3728
+ JSString *q;
3729
+ int len;
3730
+
3731
+ len = end - start;
3684
3732
if (start == 0 && end == p->len) {
3685
3733
return js_dup(JS_MKPTR(JS_TAG_STRING, p));
3686
3734
}
3687
3735
if (len <= 0) {
3688
3736
return js_empty_string(ctx->rt);
3689
3737
}
3738
+ // 1024 is about the cutoff point where it starts getting more profitable
3739
+ // to ref slice than to copy
3740
+ if (len > (1024 >> p->is_wide_char)) {
3741
+ if (p->kind == JS_STRING_KIND_SLICE) {
3742
+ slice = (void *)&p[1];
3743
+ p = slice->parent;
3744
+ start += slice->start;
3745
+ }
3746
+ // allocate as 16 bit wide string to avoid wastage;
3747
+ // js_alloc_string allocates 1 byte extra for 8 bit strings;
3748
+ q = js_alloc_string(ctx, sizeof(*slice)/2, /*is_wide_char*/true);
3749
+ if (!q)
3750
+ return JS_EXCEPTION;
3751
+ q->is_wide_char = p->is_wide_char;
3752
+ q->kind = JS_STRING_KIND_SLICE;
3753
+ q->len = len;
3754
+ slice = (void *)&q[1];
3755
+ slice->parent = p;
3756
+ slice->start = start;
3757
+ p->header.ref_count++;
3758
+ return JS_MKPTR(JS_TAG_STRING, q);
3759
+ }
3690
3760
if (p->is_wide_char) {
3691
3761
JSString *str;
3692
3762
int i;
@@ -5750,17 +5820,7 @@ static void js_free_value_rt(JSRuntime *rt, JSValue v)
5750
5820
5751
5821
switch(tag) {
5752
5822
case JS_TAG_STRING:
5753
- {
5754
- JSString *p = JS_VALUE_GET_STRING(v);
5755
- if (p->atom_type) {
5756
- JS_FreeAtomStruct(rt, p);
5757
- } else {
5758
- #ifdef ENABLE_DUMPS // JS_DUMP_LEAKS
5759
- list_del(&p->link);
5760
- #endif
5761
- js_free_rt(rt, p);
5762
- }
5763
- }
5823
+ js_free_string0(rt, JS_VALUE_GET_STRING(v));
5764
5824
break;
5765
5825
case JS_TAG_OBJECT:
5766
5826
case JS_TAG_FUNCTION_BYTECODE:
@@ -58038,6 +58098,13 @@ uintptr_t js_std_cmd(int cmd, ...) {
58038
58098
*pv = ctx->error_back_trace;
58039
58099
ctx->error_back_trace = JS_UNDEFINED;
58040
58100
break;
58101
+ case 3: // GetStringKind
58102
+ ctx = va_arg(ap, JSContext *);
58103
+ pv = va_arg(ap, JSValue *);
58104
+ rv = -1;
58105
+ if (JS_IsString(*pv))
58106
+ rv = JS_VALUE_GET_STRING(*pv)->kind;
58107
+ break;
58041
58108
default:
58042
58109
rv = -1;
58043
58110
}
0 commit comments