Skip to content

Commit 3af2603

Browse files
MaxGraeydcodeIO
authored andcommitted
Add String#replace & String#replaceAll (AssemblyScript#653)
1 parent d4313f1 commit 3af2603

15 files changed

+9686
-7493
lines changed

std/assembly/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,8 @@ declare class String {
12401240
padStart(targetLength: i32, padString?: string): string;
12411241
padEnd(targetLength: i32, padString?: string): string;
12421242
repeat(count?: i32): string;
1243+
replace(search: string, replacement: string): string;
1244+
replaceAll(search: string, replacement: string): string;
12431245
slice(beginIndex: i32, endIndex?: i32): string;
12441246
split(separator?: string, limit?: i32): string[];
12451247
toString(): string;

std/assembly/string.ts

Lines changed: 115 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import { idof } from "./builtins";
4343
}
4444

4545
@operator("[]") charAt(pos: i32): String {
46-
assert(this !== null);
4746
if (<u32>pos >= <u32>this.length) return changetype<String>("");
4847
var out = __alloc(2, idof<String>());
4948
store<u16>(out, load<u16>(changetype<usize>(this) + (<usize>pos << 1)));
@@ -81,7 +80,6 @@ import { idof } from "./builtins";
8180
}
8281

8382
endsWith(search: String, end: i32 = String.MAX_LENGTH): bool {
84-
assert(this !== null);
8583
if (search === null) return false;
8684
end = min(max(end, 0), this.length);
8785
var searchLength = <isize>search.length;
@@ -169,7 +167,6 @@ import { idof } from "./builtins";
169167
}
170168

171169
startsWith(search: String, start: i32 = 0): bool {
172-
assert(this !== null);
173170
if (search === null) search = changetype<String>("null");
174171
var len = <isize>this.length;
175172
var searchStart = min(max(<isize>start, 0), len);
@@ -180,7 +177,6 @@ import { idof } from "./builtins";
180177
}
181178

182179
substr(start: i32, length: i32 = i32.MAX_VALUE): String { // legacy
183-
assert(this !== null);
184180
var intStart: isize = start;
185181
var end: isize = length;
186182
var size: isize = this.length;
@@ -193,7 +189,6 @@ import { idof } from "./builtins";
193189
}
194190

195191
substring(start: i32, end: i32 = i32.MAX_VALUE): String {
196-
assert(this !== null);
197192
var len: isize = this.length;
198193
var finalStart = min<isize>(max(start, 0), len);
199194
var finalEnd = min<isize>(max(end, 0), len);
@@ -208,7 +203,6 @@ import { idof } from "./builtins";
208203
}
209204

210205
trim(): String {
211-
assert(this !== null);
212206
var length = this.length;
213207
var size: usize = length << 1;
214208
while (
@@ -246,7 +240,6 @@ import { idof } from "./builtins";
246240
}
247241

248242
trimStart(): String {
249-
assert(this !== null);
250243
var size = <usize>this.length << 1;
251244
var offset: usize = 0;
252245
while (
@@ -266,7 +259,6 @@ import { idof } from "./builtins";
266259
}
267260

268261
trimEnd(): String {
269-
assert(this !== null);
270262
var originalSize = <usize>this.length << 1;
271263
var size = originalSize;
272264
while (
@@ -285,7 +277,6 @@ import { idof } from "./builtins";
285277
}
286278

287279
padStart(length: i32, pad: string = " "): String {
288-
assert(this !== null);
289280
var thisSize = <usize>this.length << 1;
290281
var targetSize = <usize>length << 1;
291282
var padSize = <usize>pad.length << 1;
@@ -306,7 +297,6 @@ import { idof } from "./builtins";
306297
}
307298

308299
padEnd(length: i32, pad: string = " "): String {
309-
assert(this !== null);
310300
var thisSize = <usize>this.length << 1;
311301
var targetSize = <usize>length << 1;
312302
var padSize = <usize>pad.length << 1;
@@ -327,7 +317,6 @@ import { idof } from "./builtins";
327317
}
328318

329319
repeat(count: i32 = 0): String {
330-
assert(this !== null);
331320
var length = this.length;
332321

333322
// Most browsers can't handle strings 1 << 28 chars or longer
@@ -342,8 +331,121 @@ import { idof } from "./builtins";
342331
return changetype<String>(out); // retains
343332
}
344333

334+
replace(search: String, replacement: String): String {
335+
var len: usize = this.length;
336+
var slen: usize = search.length;
337+
if (len <= slen) {
338+
return len < slen ? this : select<String>(replacement, this, search == this);
339+
}
340+
var index: isize = this.indexOf(search);
341+
if (~index) {
342+
let rlen: usize = replacement.length;
343+
len -= slen;
344+
let olen = len + rlen;
345+
if (olen) {
346+
let out = __alloc(olen << 1, idof<String>());
347+
memory.copy(out, changetype<usize>(this), index << 1);
348+
memory.copy(
349+
out + (index << 1),
350+
changetype<usize>(replacement),
351+
rlen << 1
352+
);
353+
memory.copy(
354+
out + ((index + rlen) << 1),
355+
changetype<usize>(this) + ((index + slen) << 1),
356+
(len - index) << 1
357+
);
358+
return changetype<String>(out);
359+
}
360+
}
361+
return this;
362+
}
363+
364+
replaceAll(search: String, replacement: String): String {
365+
var len: usize = this.length;
366+
var slen: usize = search.length;
367+
if (len <= slen) {
368+
return len < slen ? this : select<String>(replacement, this, search == this);
369+
}
370+
var rlen: usize = replacement.length;
371+
if (!slen) {
372+
if (!rlen) return this;
373+
// Special case: 'abc'.replaceAll('', '-') -> '-a-b-c-'
374+
let out = __alloc((len + (len + 1) * rlen) << 1, idof<String>());
375+
memory.copy(out, changetype<usize>(replacement), rlen << 1);
376+
let offset = rlen;
377+
for (let i: usize = 0; i < len; ++i) {
378+
store<u16>(
379+
changetype<usize>(out) + (offset++ << 1),
380+
load<u16>(changetype<usize>(this) + (i << 1))
381+
);
382+
memory.copy(
383+
out + (offset << 1),
384+
changetype<usize>(replacement),
385+
rlen << 1
386+
);
387+
offset += rlen;
388+
}
389+
return changetype<String>(out);
390+
}
391+
var prev: isize = 0, next: isize = 0;
392+
if (slen == rlen) {
393+
// Fast path when search and replacement have same length
394+
let size = len << 1;
395+
let out = __alloc(size, idof<String>());
396+
memory.copy(out, changetype<usize>(this), size);
397+
while (~(next = <isize>this.indexOf(search, <i32>prev))) {
398+
memory.copy(out + (next << 1), changetype<usize>(replacement), rlen << 1);
399+
prev = next + slen;
400+
}
401+
return changetype<String>(out);
402+
}
403+
var out: usize = 0, offset: usize = 0, resLen = len;
404+
while (~(next = <isize>this.indexOf(search, <i32>prev))) {
405+
if (!out) out = __alloc(len << 1, idof<String>());
406+
if (offset > resLen) {
407+
let newLength = resLen << 1;
408+
out = __realloc(out, newLength << 1);
409+
resLen = newLength;
410+
}
411+
let chunk = next - prev;
412+
memory.copy(
413+
out + (offset << 1),
414+
changetype<usize>(this) + (prev << 1),
415+
chunk << 1
416+
);
417+
offset += chunk;
418+
memory.copy(
419+
out + (offset << 1),
420+
changetype<usize>(replacement),
421+
rlen << 1
422+
);
423+
offset += rlen;
424+
prev = next + slen;
425+
}
426+
if (offset) {
427+
if (offset > resLen) {
428+
let newLength = resLen << 1;
429+
out = __realloc(out, newLength << 1);
430+
resLen = newLength;
431+
}
432+
let rest = len - prev;
433+
if (rest) {
434+
memory.copy(
435+
out + (offset << 1),
436+
changetype<usize>(this) + (prev << 1),
437+
rest << 1
438+
);
439+
}
440+
rest += offset;
441+
if (resLen > rest) out = __realloc(out, rest << 1);
442+
return changetype<String>(out);
443+
}
444+
return this;
445+
}
446+
345447
slice(start: i32, end: i32 = i32.MAX_VALUE): String {
346-
var len = this.length;
448+
var len = this.length;
347449
start = start < 0 ? max(start + len, 0) : min(start, len);
348450
end = end < 0 ? max(end + len, 0) : min(end, len);
349451
len = end - start;
@@ -354,7 +456,6 @@ import { idof } from "./builtins";
354456
}
355457

356458
split(separator: String | null = null, limit: i32 = i32.MAX_VALUE): String[] {
357-
assert(this !== null);
358459
if (!limit) return changetype<Array<String>>(__allocArray(0, alignof<String>(), idof<Array<String>>())); // retains
359460
if (separator === null) return <String[]>[this];
360461
var length: isize = this.length;
@@ -380,7 +481,7 @@ import { idof } from "./builtins";
380481
}
381482
var result = changetype<Array<String>>(__allocArray(0, alignof<String>(), idof<Array<String>>())); // retains
382483
var end = 0, start = 0, i = 0;
383-
while ((end = this.indexOf(separator, start)) != -1) {
484+
while (~(end = this.indexOf(separator, start))) {
384485
let len = end - start;
385486
if (len > 0) {
386487
let out = __alloc(<usize>len << 1, idof<String>());

std/portable/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ declare class String {
461461
padStart(targetLength: i32, padString?: string): string;
462462
padEnd(targetLength: i32, padString?: string): string;
463463
replace(search: string, replacement: string): string;
464+
replaceAll(search: string, replacement: string): string;
464465
repeat(count?: i32): string;
465466
slice(beginIndex: i32, endIndex?: i32): string;
466467
split(separator?: string, limit?: i32): string[];

std/portable/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,16 @@ String["fromCodePoints"] = function fromCodePoints(arr) {
196196
return String.fromCodePoint.apply(String, arr);
197197
};
198198

199+
if (!String.prototype.replaceAll) {
200+
Object.defineProperty(String.prototype, "replaceAll", {
201+
value: function replaceAll(search, replacment) {
202+
var res = this.split(search).join(replacment);
203+
if (!search.length) res = replacment + res + replacment;
204+
return res;
205+
}
206+
});
207+
}
208+
199209
globalScope["isInteger"] = Number.isInteger;
200210

201211
globalScope["isFloat"] = function isFloat(arg) {

0 commit comments

Comments
 (0)