2 * Copyright 2012 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "wine/debug.h"
26 * This is the length of a string that is considered to be long enough to be
27 * worth the rope to avoid copy.
28 * This probably could be tuned, but keep it low for a while to better test rope's code.
30 #define JSSTR_SHORT_STRING_LENGTH 8
33 * This is the max rope depth. While faster to allocate, ropes may become slow at access.
35 #define JSSTR_MAX_ROPE_DEPTH 100
37 const char *debugstr_jsstr(jsstr_t
*str
)
39 return jsstr_is_inline(str
) ? debugstr_wn(jsstr_as_inline(str
)->buf
, jsstr_length(str
))
40 : jsstr_is_heap(str
) ? debugstr_wn(jsstr_as_heap(str
)->buf
, jsstr_length(str
))
41 : wine_dbg_sprintf("%s...", debugstr_jsstr(jsstr_as_rope(str
)->left
));
44 void jsstr_free(jsstr_t
*str
)
46 switch(jsstr_tag(str
)) {
48 heap_free(jsstr_as_heap(str
)->buf
);
51 jsstr_rope_t
*rope
= jsstr_as_rope(str
);
52 jsstr_release(rope
->left
);
53 jsstr_release(rope
->right
);
63 static inline void jsstr_init(jsstr_t
*str
, unsigned len
, jsstr_tag_t tag
)
65 str
->length_flags
= len
<< JSSTR_LENGTH_SHIFT
| tag
;
69 WCHAR
*jsstr_alloc_buf(unsigned len
, jsstr_t
**r
)
73 if(len
> JSSTR_MAX_LENGTH
)
76 ret
= heap_alloc(FIELD_OFFSET(jsstr_inline_t
, buf
[len
+1]));
80 jsstr_init(&ret
->str
, len
, JSSTR_INLINE
);
86 jsstr_t
*jsstr_alloc_len(const WCHAR
*buf
, unsigned len
)
91 ptr
= jsstr_alloc_buf(len
, &ret
);
93 memcpy(ptr
, buf
, len
*sizeof(WCHAR
));
98 static void jsstr_rope_extract(jsstr_rope_t
*str
, unsigned off
, unsigned len
, WCHAR
*buf
)
100 unsigned left_len
= jsstr_length(str
->left
);
102 if(left_len
<= off
) {
103 jsstr_extract(str
->right
, off
-left_len
, len
, buf
);
104 }else if(left_len
>= len
+off
) {
105 jsstr_extract(str
->left
, off
, len
, buf
);
108 jsstr_extract(str
->left
, off
, left_len
, buf
);
109 jsstr_extract(str
->right
, 0, len
-left_len
, buf
+left_len
);
113 void jsstr_extract(jsstr_t
*str
, unsigned off
, unsigned len
, WCHAR
*buf
)
115 switch(jsstr_tag(str
)) {
117 memcpy(buf
, jsstr_as_inline(str
)->buf
+off
, len
*sizeof(WCHAR
));
120 memcpy(buf
, jsstr_as_heap(str
)->buf
+off
, len
*sizeof(WCHAR
));
123 return jsstr_rope_extract(jsstr_as_rope(str
), off
, len
, buf
);
127 static int jsstr_cmp_str(jsstr_t
*jsstr
, const WCHAR
*str
, unsigned len
)
131 switch(jsstr_tag(jsstr
)) {
133 ret
= memcmp(jsstr_as_inline(jsstr
)->buf
, str
, len
*sizeof(WCHAR
));
134 return ret
|| jsstr_length(jsstr
) == len
? ret
: 1;
136 ret
= memcmp(jsstr_as_heap(jsstr
)->buf
, str
, len
*sizeof(WCHAR
));
137 return ret
|| jsstr_length(jsstr
) == len
? ret
: 1;
139 jsstr_rope_t
*rope
= jsstr_as_rope(jsstr
);
140 unsigned left_len
= jsstr_length(rope
->left
);
142 ret
= jsstr_cmp_str(rope
->left
, str
, min(len
, left_len
));
143 if(ret
|| len
<= left_len
)
145 return jsstr_cmp_str(rope
->right
, str
+left_len
, len
-left_len
);
153 #define TMP_BUF_SIZE 256
155 static int ropes_cmp(jsstr_rope_t
*left
, jsstr_rope_t
*right
)
157 WCHAR left_buf
[TMP_BUF_SIZE
], right_buf
[TMP_BUF_SIZE
];
158 unsigned left_len
= jsstr_length(&left
->str
);
159 unsigned right_len
= jsstr_length(&right
->str
);
160 unsigned cmp_off
= 0, cmp_size
;
163 /* FIXME: We can avoid temporary buffers here. */
164 while(cmp_off
< min(left_len
, right_len
)) {
165 cmp_size
= min(left_len
, right_len
) - cmp_off
;
166 if(cmp_size
> TMP_BUF_SIZE
)
167 cmp_size
= TMP_BUF_SIZE
;
169 jsstr_rope_extract(left
, cmp_off
, cmp_size
, left_buf
);
170 jsstr_rope_extract(right
, cmp_off
, cmp_size
, right_buf
);
171 ret
= memcmp(left_buf
, right_buf
, cmp_size
);
178 return left_len
- right_len
;
181 static inline const WCHAR
*jsstr_try_flat(jsstr_t
*str
)
183 return jsstr_is_inline(str
) ? jsstr_as_inline(str
)->buf
184 : jsstr_is_heap(str
) ? jsstr_as_heap(str
)->buf
188 int jsstr_cmp(jsstr_t
*str1
, jsstr_t
*str2
)
190 unsigned len1
= jsstr_length(str1
);
191 unsigned len2
= jsstr_length(str2
);
195 str
= jsstr_try_flat(str2
);
197 ret
= jsstr_cmp_str(str1
, str
, min(len1
, len2
));
198 return ret
|| len1
== len2
? ret
: -1;
201 str
= jsstr_try_flat(str1
);
203 ret
= jsstr_cmp_str(str2
, str
, min(len1
, len2
));
204 return ret
|| len1
== len2
? -ret
: 1;
207 return ropes_cmp(jsstr_as_rope(str1
), jsstr_as_rope(str2
));
210 jsstr_t
*jsstr_concat(jsstr_t
*str1
, jsstr_t
*str2
)
216 len1
= jsstr_length(str1
);
218 return jsstr_addref(str2
);
220 len2
= jsstr_length(str2
);
222 return jsstr_addref(str1
);
224 if(len1
+ len2
>= JSSTR_SHORT_STRING_LENGTH
) {
225 unsigned depth
, depth2
;
228 depth
= jsstr_is_rope(str1
) ? jsstr_as_rope(str1
)->depth
: 0;
229 depth2
= jsstr_is_rope(str2
) ? jsstr_as_rope(str2
)->depth
: 0;
233 if(depth
++ < JSSTR_MAX_ROPE_DEPTH
) {
234 if(len1
+len2
> JSSTR_MAX_LENGTH
)
237 rope
= heap_alloc(sizeof(*rope
));
241 jsstr_init(&rope
->str
, len1
+len2
, JSSTR_ROPE
);
242 rope
->left
= jsstr_addref(str1
);
243 rope
->right
= jsstr_addref(str2
);
249 ptr
= jsstr_alloc_buf(len1
+len2
, &ret
);
253 jsstr_flush(str1
, ptr
);
254 jsstr_flush(str2
, ptr
+len1
);
259 C_ASSERT(sizeof(jsstr_heap_t
) <= sizeof(jsstr_rope_t
));
261 const WCHAR
*jsstr_rope_flatten(jsstr_rope_t
*str
)
265 buf
= heap_alloc((jsstr_length(&str
->str
)+1) * sizeof(WCHAR
));
269 jsstr_flush(str
->left
, buf
);
270 jsstr_flush(str
->right
, buf
+jsstr_length(str
->left
));
271 buf
[jsstr_length(&str
->str
)] = 0;
273 /* Trasform to heap string */
274 jsstr_release(str
->left
);
275 jsstr_release(str
->right
);
276 str
->str
.length_flags
|= JSSTR_FLAG_FLAT
;
277 return jsstr_as_heap(&str
->str
)->buf
= buf
;
280 static jsstr_t
*empty_str
, *nan_str
, *undefined_str
, *null_bstr_str
;
282 jsstr_t
*jsstr_nan(void)
284 return jsstr_addref(nan_str
);
287 jsstr_t
*jsstr_empty(void)
289 return jsstr_addref(empty_str
);
292 jsstr_t
*jsstr_undefined(void)
294 return jsstr_addref(undefined_str
);
297 jsstr_t
*jsstr_null_bstr(void)
299 return jsstr_addref(null_bstr_str
);
302 BOOL
is_null_bstr(jsstr_t
*str
)
304 return str
== null_bstr_str
;
307 BOOL
init_strings(void)
309 static const WCHAR NaNW
[] = { 'N','a','N',0 };
310 static const WCHAR undefinedW
[] = {'u','n','d','e','f','i','n','e','d',0};
312 if(!jsstr_alloc_buf(0, &empty_str
))
314 if(!(nan_str
= jsstr_alloc(NaNW
)))
316 if(!(undefined_str
= jsstr_alloc(undefinedW
)))
318 if(!jsstr_alloc_buf(0, &null_bstr_str
))
323 void free_strings(void)
326 jsstr_release(empty_str
);
328 jsstr_release(nan_str
);
330 jsstr_release(undefined_str
);
332 jsstr_release(null_bstr_str
);