1919
2020#if defined(DUK_USE_STRING_BUILTIN )
2121
22+ /*
23+ * Helpers
24+ */
25+
26+ DUK_LOCAL duk_hstring * duk__str_tostring_notregexp (duk_context * ctx , duk_idx_t idx ) {
27+ duk_hstring * h ;
28+
29+ if (duk_get_class_number (ctx , idx ) == DUK_HOBJECT_CLASS_REGEXP ) {
30+ DUK_ERROR_TYPE_INVALID_ARGS ((duk_hthread * ) ctx );
31+ }
32+ h = duk_to_hstring (ctx , idx );
33+ DUK_ASSERT (h != NULL );
34+
35+ return h ;
36+ }
37+
38+ DUK_LOCAL duk_int_t duk__str_search_shared (duk_context * ctx , duk_hstring * h_this , duk_hstring * h_search , duk_int_t start_cpos , duk_bool_t backwards ) {
39+ duk_int_t cpos ;
40+ duk_int_t bpos ;
41+ const duk_uint8_t * p_start , * p_end , * p ;
42+ const duk_uint8_t * q_start ;
43+ duk_int_t q_blen ;
44+ duk_uint8_t firstbyte ;
45+ duk_uint8_t t ;
46+
47+ cpos = start_cpos ;
48+
49+ /* Empty searchstring always matches; cpos must be clamped here.
50+ * (If q_blen were < 0 due to clamped coercion, it would also be
51+ * caught here.)
52+ */
53+ q_start = DUK_HSTRING_GET_DATA (h_search );
54+ q_blen = (duk_int_t ) DUK_HSTRING_GET_BYTELEN (h_search );
55+ if (q_blen <= 0 ) {
56+ return cpos ;
57+ }
58+ DUK_ASSERT (q_blen > 0 );
59+
60+ bpos = (duk_int_t ) duk_heap_strcache_offset_char2byte ((duk_hthread * ) ctx , h_this , (duk_uint32_t ) cpos );
61+
62+ p_start = DUK_HSTRING_GET_DATA (h_this );
63+ p_end = p_start + DUK_HSTRING_GET_BYTELEN (h_this );
64+ p = p_start + bpos ;
65+
66+ /* This loop is optimized for size. For speed, there should be
67+ * two separate loops, and we should ensure that memcmp() can be
68+ * used without an extra "will searchstring fit" check. Doing
69+ * the preconditioning for 'p' and 'p_end' is easy but cpos
70+ * must be updated if 'p' is wound back (backward scanning).
71+ */
72+
73+ firstbyte = q_start [0 ]; /* leading byte of match string */
74+ while (p <= p_end && p >= p_start ) {
75+ t = * p ;
76+
77+ /* For Ecmascript strings, this check can only match for
78+ * initial UTF-8 bytes (not continuation bytes). For other
79+ * strings all bets are off.
80+ */
81+
82+ if ((t == firstbyte ) && ((duk_size_t ) (p_end - p ) >= (duk_size_t ) q_blen )) {
83+ DUK_ASSERT (q_blen > 0 ); /* no issues with memcmp() zero size, even if broken */
84+ if (DUK_MEMCMP ((const void * ) p , (const void * ) q_start , (size_t ) q_blen ) == 0 ) {
85+ return cpos ;
86+ }
87+ }
88+
89+ /* track cpos while scanning */
90+ if (backwards ) {
91+ /* when going backwards, we decrement cpos 'early';
92+ * 'p' may point to a continuation byte of the char
93+ * at offset 'cpos', but that's OK because we'll
94+ * backtrack all the way to the initial byte.
95+ */
96+ if ((t & 0xc0 ) != 0x80 ) {
97+ cpos -- ;
98+ }
99+ p -- ;
100+ } else {
101+ if ((t & 0xc0 ) != 0x80 ) {
102+ cpos ++ ;
103+ }
104+ p ++ ;
105+ }
106+ }
107+
108+ /* Not found. Empty string case is handled specially above. */
109+ return -1 ;
110+ }
111+
22112/*
23113 * Constructor
24114 */
@@ -369,12 +459,6 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx)
369459 duk_hstring * h_search ;
370460 duk_int_t clen_this ;
371461 duk_int_t cpos ;
372- duk_int_t bpos ;
373- const duk_uint8_t * p_start , * p_end , * p ;
374- const duk_uint8_t * q_start ;
375- duk_int_t q_blen ;
376- duk_uint8_t firstbyte ;
377- duk_uint8_t t ;
378462 duk_small_int_t is_lastindexof = duk_get_current_magic (ctx ); /* 0=indexOf, 1=lastIndexOf */
379463
380464 h_this = duk_push_this_coercible_to_string (ctx );
@@ -383,8 +467,6 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx)
383467
384468 h_search = duk_to_hstring (ctx , 0 );
385469 DUK_ASSERT (h_search != NULL );
386- q_start = DUK_HSTRING_GET_DATA (h_search );
387- q_blen = (duk_int_t ) DUK_HSTRING_GET_BYTELEN (h_search );
388470
389471 duk_to_number (ctx , 1 );
390472 if (duk_is_nan (ctx , 1 ) && is_lastindexof ) {
@@ -397,67 +479,8 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx)
397479 cpos = duk_to_int_clamped (ctx , 1 , 0 , clen_this );
398480 }
399481
400- /* Empty searchstring always matches; cpos must be clamped here.
401- * (If q_blen were < 0 due to clamped coercion, it would also be
402- * caught here.)
403- */
404- if (q_blen <= 0 ) {
405- duk_push_int (ctx , cpos );
406- return 1 ;
407- }
408- DUK_ASSERT (q_blen > 0 );
409-
410- bpos = (duk_int_t ) duk_heap_strcache_offset_char2byte (thr , h_this , (duk_uint32_t ) cpos );
411-
412- p_start = DUK_HSTRING_GET_DATA (h_this );
413- p_end = p_start + DUK_HSTRING_GET_BYTELEN (h_this );
414- p = p_start + bpos ;
415-
416- /* This loop is optimized for size. For speed, there should be
417- * two separate loops, and we should ensure that memcmp() can be
418- * used without an extra "will searchstring fit" check. Doing
419- * the preconditioning for 'p' and 'p_end' is easy but cpos
420- * must be updated if 'p' is wound back (backward scanning).
421- */
422-
423- firstbyte = q_start [0 ]; /* leading byte of match string */
424- while (p <= p_end && p >= p_start ) {
425- t = * p ;
426-
427- /* For Ecmascript strings, this check can only match for
428- * initial UTF-8 bytes (not continuation bytes). For other
429- * strings all bets are off.
430- */
431-
432- if ((t == firstbyte ) && ((duk_size_t ) (p_end - p ) >= (duk_size_t ) q_blen )) {
433- DUK_ASSERT (q_blen > 0 ); /* no issues with memcmp() zero size, even if broken */
434- if (DUK_MEMCMP ((const void * ) p , (const void * ) q_start , (size_t ) q_blen ) == 0 ) {
435- duk_push_int (ctx , cpos );
436- return 1 ;
437- }
438- }
439-
440- /* track cpos while scanning */
441- if (is_lastindexof ) {
442- /* when going backwards, we decrement cpos 'early';
443- * 'p' may point to a continuation byte of the char
444- * at offset 'cpos', but that's OK because we'll
445- * backtrack all the way to the initial byte.
446- */
447- if ((t & 0xc0 ) != 0x80 ) {
448- cpos -- ;
449- }
450- p -- ;
451- } else {
452- if ((t & 0xc0 ) != 0x80 ) {
453- cpos ++ ;
454- }
455- p ++ ;
456- }
457- }
458-
459- /* Not found. Empty string case is handled specially above. */
460- duk_push_int (ctx , -1 );
482+ cpos = duk__str_search_shared (ctx , h_this , h_search , cpos , is_lastindexof /*backwards*/ );
483+ duk_push_int (ctx , cpos );
461484 return 1 ;
462485}
463486
@@ -1460,10 +1483,8 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *
14601483 h = duk_push_this_coercible_to_string (ctx );
14611484 DUK_ASSERT (h != NULL );
14621485
1463- if (duk_get_class_number (ctx , 0 ) == DUK_HOBJECT_CLASS_REGEXP ) {
1464- DUK_DCERROR_TYPE_INVALID_ARGS ((duk_hthread * ) ctx );
1465- }
1466- h_search = duk_to_hstring (ctx , 0 );
1486+ h_search = duk__str_tostring_notregexp (ctx , 0 );
1487+ DUK_ASSERT (h_search = NULL );
14671488
14681489 magic = duk_get_current_magic (ctx );
14691490
@@ -1502,7 +1523,7 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *
15021523
15031524 result = 0 ;
15041525 if (p_cmp_start >= DUK_HSTRING_GET_DATA (h ) &&
1505- p_cmp_start + blen_search <= DUK_HSTRING_GET_DATA (h ) + DUK_HSTRING_GET_BYTELEN (h )) {
1526+ p_cmp_start - ( const duk_uint8_t * ) DUK_HSTRING_GET_DATA (h ) + blen_search <= DUK_HSTRING_GET_BYTELEN (h )) {
15061527 if (DUK_MEMCMP ((const void * ) p_cmp_start ,
15071528 (const void * ) DUK_HSTRING_GET_DATA (h_search ),
15081529 (size_t ) blen_search ) == 0 ) {
@@ -1515,4 +1536,26 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *
15151536}
15161537#endif /* DUK_USE_ES6 */
15171538
1539+ #if defined(DUK_USE_ES6 )
1540+ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_includes (duk_context * ctx ) {
1541+ duk_hstring * h ;
1542+ duk_hstring * h_search ;
1543+ duk_int_t len ;
1544+ duk_int_t pos ;
1545+
1546+ h = duk_push_this_coercible_to_string (ctx );
1547+ DUK_ASSERT (h != NULL );
1548+
1549+ h_search = duk__str_tostring_notregexp (ctx , 0 );
1550+ DUK_ASSERT (h_search = NULL );
1551+
1552+ len = (duk_int_t ) DUK_HSTRING_GET_CHARLEN (h );
1553+ pos = duk_to_int_clamped (ctx , 1 , 0 , len );
1554+ DUK_ASSERT (pos >= 0 && pos <= len );
1555+
1556+ pos = duk__str_search_shared (ctx , h , h_search , pos , 0 /*backwards*/ );
1557+ duk_push_boolean (ctx , pos >= 0 );
1558+ return 1 ;
1559+ }
1560+ #endif /* DUK_USE_ES6 */
15181561#endif /* DUK_USE_STRING_BUILTIN */
0 commit comments