Add String#lastIndexOf and improve tests (#163)

This commit is contained in:
Max Graey 2018-07-10 04:31:51 +03:00 committed by Daniel Wirtz
parent c4199673ef
commit 365884ff73
11 changed files with 2112 additions and 1172 deletions

View File

@ -431,7 +431,8 @@ declare class String {
charCodeAt(index: u32): u16; charCodeAt(index: u32): u16;
concat(other: string): string; concat(other: string): string;
endsWith(other: string): bool; endsWith(other: string): bool;
indexOf(other: string): u32; indexOf(other: string, fromIndex?: i32): u32;
lastIndexOf(other: string, fromIndex?: i32): i32;
includes(other: string): bool; includes(other: string): bool;
startsWith(other: string): bool; startsWith(other: string): bool;
substr(start: u32, length?: u32): string; substr(start: u32, length?: u32): string;

View File

@ -15,6 +15,11 @@ export const MAX_LENGTH = (<i32>MAX_SIZE_32 - HEADER_SIZE) >>> 1;
/** Singleton empty String. */ /** Singleton empty String. */
export const EMPTY = changetype<String>(""); // TODO: is this a bad idea with '===' in place? export const EMPTY = changetype<String>(""); // TODO: is this a bad idea with '===' in place?
@inline
export function clamp<T>(val: T, lo: T, hi: T): T {
return max<T>(min<T>(val, hi), lo);
}
/** Allocates a raw String with uninitialized contents. */ /** Allocates a raw String with uninitialized contents. */
export function allocate(length: i32): String { export function allocate(length: i32): String {
assert(length > 0 && length <= MAX_LENGTH); assert(length > 0 && length <= MAX_LENGTH);

View File

@ -2,6 +2,7 @@ import {
HEADER_SIZE, HEADER_SIZE,
MAX_LENGTH, MAX_LENGTH,
EMPTY, EMPTY,
clamp,
allocate, allocate,
isWhiteSpaceOrLineTerminator, isWhiteSpaceOrLineTerminator,
CharCode, CharCode,
@ -97,7 +98,7 @@ export class String {
endsWith(searchString: String, endPosition: i32 = MAX_LENGTH): bool { endsWith(searchString: String, endPosition: i32 = MAX_LENGTH): bool {
assert(this !== null); assert(this !== null);
if (searchString === null) return false; if (searchString === null) return false;
var end: isize = <isize>min(max(endPosition, 0), this.length); var end = clamp<isize>(endPosition, 0, this.length);
var searchLength: isize = searchString.length; var searchLength: isize = searchString.length;
var start: isize = end - searchLength; var start: isize = end - searchLength;
if (start < 0) return false; if (start < 0) return false;
@ -206,21 +207,44 @@ export class String {
return this.indexOf(searchString, position) != -1; return this.indexOf(searchString, position) != -1;
} }
indexOf(searchString: String, position: i32 = 0): i32 { indexOf(searchString: String, fromIndex: i32 = 0): i32 {
assert(this !== null); assert(this !== null);
if (searchString === null) searchString = changetype<String>("null"); if (searchString === null) searchString = changetype<String>("null");
var pos: isize = position; var searchLen: isize = searchString.length;
if (!searchLen) return 0;
var len: isize = this.length; var len: isize = this.length;
var start: isize = min<isize>(max<isize>(pos, 0), len); if (!len) return -1;
var searchLen: isize = <isize>searchString.length; var start = clamp<isize>(fromIndex, 0, len);
len -= searchLen;
// TODO: two-way, multiple char codes // TODO: multiple char codes
for (let k: usize = start; <isize>k + searchLen <= len; ++k) { for (let k: isize = start; k <= len; ++k) {
if (!compare_memory( if (!compare_memory(
changetype<usize>(this) + HEADER_SIZE + (k << 1), changetype<usize>(this) + HEADER_SIZE + (k << 1),
changetype<usize>(searchString) + HEADER_SIZE, changetype<usize>(searchString) + HEADER_SIZE,
searchLen << 1) searchLen << 1
) { )) {
return <i32>k;
}
}
return -1;
}
lastIndexOf(searchString: String, fromIndex: i32 = 0): i32 {
assert(this !== null);
if (searchString === null) searchString = changetype<String>("null");
var len: isize = this.length;
var searchLen: isize = searchString.length;
if (!searchLen) return len;
if (!len) return -1;
var start = clamp<isize>(fromIndex - searchLen, 0, len);
// TODO: multiple char codes
for (let k: isize = len - 1; k >= start; --k) {
if (!compare_memory(
changetype<usize>(this) + HEADER_SIZE + (k << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLen << 1
)) {
return <i32>k; return <i32>k;
} }
} }
@ -233,8 +257,8 @@ export class String {
var pos: isize = position; var pos: isize = position;
var len: isize = this.length; var len: isize = this.length;
var start: isize = min<isize>(max<isize>(pos, 0), len); var start = clamp<isize>(pos, 0, len);
var searchLength: isize = <isize>searchString.length; var searchLength: isize = searchString.length;
if (searchLength + start > len) { if (searchLength + start > len) {
return false; return false;
} }
@ -253,7 +277,7 @@ export class String {
if (intStart < 0) { if (intStart < 0) {
intStart = max<isize>(size + intStart, 0); intStart = max<isize>(size + intStart, 0);
} }
var resultLength: isize = min<isize>(max<isize>(end, 0), size - intStart); var resultLength = clamp<isize>(end, 0, size - intStart);
if (resultLength <= 0) { if (resultLength <= 0) {
return EMPTY; return EMPTY;
} }
@ -269,8 +293,8 @@ export class String {
substring(start: i32, end: i32 = i32.MAX_VALUE): String { substring(start: i32, end: i32 = i32.MAX_VALUE): String {
assert(this !== null); assert(this !== null);
var len = this.length; var len = this.length;
var finalStart = min<i32>(max<i32>(start, 0), len); var finalStart = clamp<isize>(start, 0, len);
var finalEnd = min<i32>(max<i32>(end, 0), len); var finalEnd = clamp<isize>(end, 0, len);
var from = min<i32>(finalStart, finalEnd); var from = min<i32>(finalStart, finalEnd);
var to = max<i32>(finalStart, finalEnd); var to = max<i32>(finalStart, finalEnd);
len = to - from; len = to - from;

View File

@ -280,14 +280,14 @@ declare class String {
private constructor(); private constructor();
indexOf(subject: string, position?: i32): i32; indexOf(other: string, fromIndex?: i32): i32;
lastIndexOf(other: string, fromIndex?: i32): i32;
includes(other: string): bool; includes(other: string): bool;
lastIndexOf(subject: string, position?: i32): i32;
charAt(index: i32): string; charAt(index: i32): string;
charCodeAt(index: i32): i32; charCodeAt(index: i32): i32;
substring(from: i32, to?: i32): string; substring(from: i32, to?: i32): string;
startsWith(subject: string): bool; startsWith(other: string): bool;
endsWith(subject: string): bool; endsWith(other: string): bool;
replace(search: string, replacement: string): string; replace(search: string, replacement: string): string;
repeat(count?: i32): string; repeat(count?: i32): string;
toString(): string; toString(): string;

View File

@ -132,7 +132,7 @@
(call $~lib/env/abort (call $~lib/env/abort
(i32.const 0) (i32.const 0)
(i32.const 12) (i32.const 12)
(i32.const 231) (i32.const 255)
(i32.const 4) (i32.const 4)
) )
(unreachable) (unreachable)
@ -154,31 +154,31 @@
(get_local $1) (get_local $1)
) )
) )
(tee_local $3 (tee_local $2
(select (select
(tee_local $3 (tee_local $2
(select (select
(get_local $2) (get_local $2)
(i32.const 0) (tee_local $3
(i32.gt_s (i32.load
(get_local $0)
)
)
(i32.lt_s
(get_local $2) (get_local $2)
(i32.const 0) (get_local $3)
) )
) )
) )
(tee_local $2 (i32.const 0)
(i32.load (i32.gt_s
(get_local $0)
)
)
(i32.lt_s
(get_local $3)
(get_local $2) (get_local $2)
(i32.const 0)
) )
) )
) )
) )
(get_local $2) (get_local $3)
) )
(return (return
(i32.const 0) (i32.const 0)
@ -192,7 +192,7 @@
(i32.const 4) (i32.const 4)
) )
(i32.shl (i32.shl
(get_local $3) (get_local $2)
(i32.const 1) (i32.const 1)
) )
) )

View File

@ -201,6 +201,7 @@
(local $6 i32) (local $6 i32)
(local $7 i32) (local $7 i32)
(local $8 i32) (local $8 i32)
(local $9 i32)
(if (if
(i32.eqz (i32.eqz
(i32.ne (i32.ne
@ -212,7 +213,7 @@
(call $~lib/env/abort (call $~lib/env/abort
(i32.const 0) (i32.const 0)
(i32.const 12) (i32.const 12)
(i32.const 231) (i32.const 255)
(i32.const 4) (i32.const 4)
) )
(unreachable) (unreachable)
@ -235,32 +236,37 @@
(get_local $0) (get_local $0)
) )
) )
(set_local $7 (set_local $8
(select (block $~lib/internal/string/clamp<isize>|inlined.0 (result i32)
(tee_local $5 (set_local $5
(select (i32.const 0)
(tee_local $5 )
(get_local $3) (select
) (tee_local $6
(tee_local $6 (select
(i32.const 0) (tee_local $6
) (get_local $3)
(i32.gt_s )
(get_local $5) (tee_local $7
(get_local $6) (get_local $4)
)
(i32.lt_s
(get_local $6)
(get_local $7)
)
) )
) )
) (tee_local $7
(tee_local $6 (get_local $5)
(get_local $4) )
) (i32.gt_s
(i32.lt_s (get_local $6)
(get_local $5) (get_local $7)
(get_local $6) )
) )
) )
) )
(set_local $8 (set_local $9
(i32.load (i32.load
(get_local $1) (get_local $1)
) )
@ -268,8 +274,8 @@
(if (if
(i32.gt_s (i32.gt_s
(i32.add (i32.add
(get_local $9)
(get_local $8) (get_local $8)
(get_local $7)
) )
(get_local $4) (get_local $4)
) )
@ -285,7 +291,7 @@
(i32.const 4) (i32.const 4)
) )
(i32.shl (i32.shl
(get_local $7) (get_local $8)
(i32.const 1) (i32.const 1)
) )
) )
@ -294,7 +300,7 @@
(i32.const 4) (i32.const 4)
) )
(i32.shl (i32.shl
(get_local $8) (get_local $9)
(i32.const 1) (i32.const 1)
) )
) )

View File

@ -5823,7 +5823,7 @@
(call $~lib/env/abort (call $~lib/env/abort
(i32.const 0) (i32.const 0)
(i32.const 728) (i32.const 728)
(i32.const 20) (i32.const 25)
(i32.const 2) (i32.const 2)
) )
(unreachable) (unreachable)
@ -5855,7 +5855,7 @@
(call $~lib/env/abort (call $~lib/env/abort
(i32.const 0) (i32.const 0)
(i32.const 696) (i32.const 696)
(i32.const 18) (i32.const 19)
(i32.const 4) (i32.const 4)
) )
(unreachable) (unreachable)
@ -5903,7 +5903,7 @@
(call $~lib/env/abort (call $~lib/env/abort
(i32.const 0) (i32.const 0)
(i32.const 696) (i32.const 696)
(i32.const 74) (i32.const 75)
(i32.const 4) (i32.const 4)
) )
(unreachable) (unreachable)

View File

@ -8611,7 +8611,7 @@
(call $~lib/env/abort (call $~lib/env/abort
(i32.const 0) (i32.const 0)
(i32.const 728) (i32.const 728)
(i32.const 20) (i32.const 25)
(i32.const 2) (i32.const 2)
) )
(unreachable) (unreachable)
@ -8647,7 +8647,7 @@
(call $~lib/env/abort (call $~lib/env/abort
(i32.const 0) (i32.const 0)
(i32.const 696) (i32.const 696)
(i32.const 18) (i32.const 19)
(i32.const 4) (i32.const 4)
) )
(unreachable) (unreachable)
@ -8699,7 +8699,7 @@
(call $~lib/env/abort (call $~lib/env/abort
(i32.const 0) (i32.const 0)
(i32.const 696) (i32.const 696)
(i32.const 74) (i32.const 75)
(i32.const 4) (i32.const 4)
) )
(unreachable) (unreachable)

File diff suppressed because it is too large Load Diff

View File

@ -13,8 +13,24 @@ assert(str.charCodeAt(0) == 0x68);
assert(str.startsWith("hi")); assert(str.startsWith("hi"));
assert(str.endsWith("string")); assert(str.endsWith("string"));
assert(str.includes("I'm")); assert(str.includes("I'm"));
assert("".indexOf("") == 0);
assert("".indexOf("hi") == -1);
assert(str.indexOf("") == 0);
assert(str.indexOf(",") == 2); assert(str.indexOf(",") == 2);
assert(str.indexOf("x") == -1); assert(str.indexOf("x") == -1);
assert(str.indexOf(",", 2) == 2);
assert(str.indexOf(",", 3) == -1);
assert(str.indexOf(", I", -1) == 2);
assert("".lastIndexOf("") == 0);
assert("".lastIndexOf("hi") == -1);
assert(str.lastIndexOf("") == str.length);
assert(str.lastIndexOf(",") == 2);
assert(str.lastIndexOf("x") == -1);
assert(str.lastIndexOf(",", 2) == 2);
assert(str.lastIndexOf(",", 3) == 2);
assert(str.lastIndexOf(", I", -1) == 2);
export function getString(): string { export function getString(): string {
return str; return str;

File diff suppressed because it is too large Load Diff