• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package escompat;
17
18// NOTE: autogenerated file
19
20% template = ERB.new(File.read("Array_header.erb"), nil, '%', eoutvar: '_sub04')
21<%= template.result(binding) %>
22
23class ArrayKeysIterator<T> implements IterableIterator<number> {
24    private parent: Array<T>
25    private idx: int = 0
26
27    constructor(parent: Array<T>) {
28        this.parent = parent
29    }
30
31    override next(): IteratorResult<number> {
32        if (this.idx >= this.parent.actualLength) {
33            return new IteratorResult<number>()
34        }
35        return new IteratorResult<number>(this.idx++ as number)
36    }
37
38    override $_iterator(): IterableIterator<number> {
39        return this
40    }
41}
42
43class FromBuffer {}
44const FROM_BUFFER = new FromBuffer()
45
46/**
47 * Represents JS API-compatible Array
48 */
49export class Array<T> implements ReadonlyArray<T>, Iterable<T> {
50    private buffer: NullishType[]
51    internal actualLength: int
52
53    override get length(): number {
54        return this.actualLength as number
55    }
56
57    set length(newLen: number): void {
58        const len = newLen as int
59        if (len < 0 || len > this.actualLength) {
60            throw new RangeError("can't change length to bigger or negative")
61        }
62        this.actualLength = len
63    }
64
65    public override $_get(index: number): T {
66        return this.$_get(index as int)
67    }
68
69    public $_set(i: number, val: T): void {
70        this.$_set(i as int, val)
71    }
72
73    public $_get(idx: int): T {
74        if (idx >= this.actualLength || idx < 0) {
75            throw new RangeError("Out of bounds")
76        }
77        return this.buffer[idx] as T
78    }
79
80    internal $_get_unsafe(idx: int): T {
81        return this.buffer[idx] as T
82    }
83
84    public $_set(idx: int, val: T): void {
85        if (idx >= this.actualLength) {
86            throw new RangeError("Out of bounds")
87        }
88        this.buffer[idx] = val
89    }
90
91    private $_set_unsafe(idx: int, val: T): void {
92        this.buffer[idx] = val
93    }
94
95    /**
96     * Creates a new instance of Array
97     */
98    public constructor(arrayLen: int) {
99        this.buffer = new NullishType[arrayLen]
100        this.actualLength = arrayLen
101    }
102
103    public constructor(arrayLen: number) {
104        this(arrayLen as int)
105    }
106
107    internal constructor(_tag: FromBuffer, buf: NullishType[]) {
108        this.buffer = buf
109        this.actualLength = buf.length
110    }
111
112    internal constructor() {
113        this.buffer = new NullishType[4]
114        this.actualLength = 0
115    }
116
117    /**
118     * Creates a new instance of Array based on Object[]
119     *
120     * @param d Array initializer
121     */
122    public constructor(first: T, ...d: T[]) {
123        this.buffer = new NullishType[d.length + 1]
124        this.actualLength = d.length + 1
125
126        this.buffer[0] = first
127
128        for (let k: int = 0; k < d.length; k++) {
129            this.$_set_unsafe(k + 1, d[k])
130        }
131    }
132
133    /**
134     * Creates a new instance of an Array with the specified length
135     *
136     * @param arrayLength The length of the array to be created (optional).
137     *
138     * @returns A new Array instance with the specified length
139     */
140    static invoke<T>(arrayLength?: number): Array<T> {
141        if (arrayLength != undefined) {
142            return new Array<T>(arrayLength);
143        } else {
144            return new Array<T>();
145        }
146    }
147
148    /**
149     * Creates a new `Array` instance from `Object[]` primitive array.
150     *
151     * @param arr an iterable object to convert to an array.
152     *
153     * @returns `Array` intance constructed from `Object[]` primitive array.
154     */
155    public static from<T>(iterable: ArrayLike<T> | Iterable<T>): Array<T> {
156        return Array.from<T, T>(iterable, (x: T, k: number): T => x)
157    }
158
159    /**
160     * Creates a new `Array` instance from `Object[]` primitive array.
161     *
162     * @param iterable an iterable object to convert to an array.
163     *
164     * @param mapfn a mapping function to call on every element of the array.
165     * Every value to be added to the array is first passed through this function, and `mapfn`'s return value
166     * is added to the array instead.
167     *
168     * @returns `Array` intance constructed from `Object[]` primitive array and given function.
169     */
170    public static from<T, U>(iterable: ArrayLike<T> | Iterable<T>, mapfn: (v: T, k: number) => U): Array<U> {
171        const ret = new Array<U>()
172        // NOTE (ikorobkov): Please don't replace idx as int[1] with int-variable, because of value of single variable doesn't change (idx++) into lambda call by unknown reason
173        const idx = new int[1]
174        idx[0] = 0
175        iteratorForEach<T>(iterable.$_iterator(), (x: T): void => {
176            ret.push(mapfn(x, idx[0] as number))
177            idx[0] += 1
178        })
179        return ret
180    }
181
182    /**
183    * Creates a new `Array` instance from `Object[]` primitive array.
184    *
185    * @param arr primitive array.
186    *
187    * @returns `Array` intance constructed from `Object[]` primitive array.
188    */
189    public static from<T>(arr: T[]): Array<T> {
190        const len = arr.length
191        const ret = new NullishType[len as int]
192        for (let i: int = 0; i < len; i++) {
193            ret[i] = arr[i] as NullishType
194        }
195        return new Array<T>(FROM_BUFFER, ret)
196    }
197
198    private lastIndexOfUndefined(fi: int): int {
199        for (let i = fi; i >= 0; i--) {
200            if (this.$_get_unsafe(i) instanceof undefined) {
201                return i
202            }
203        }
204        return -1
205    }
206
207    private lastIndexOfNull(fi: int): int {
208        for (let i = fi; i >= 0; i--) {
209            if (this.$_get_unsafe(i) instanceof null) {
210                return i
211            }
212        }
213        return -1
214    }
215
216    private lastIndexOfString(val: String, fi: int): int {
217        for (let i = fi; i >= 0; i--) {
218            const tmp = this.$_get_unsafe(i)
219            if (tmp instanceof String) {
220                if (tmp == val) {
221                    return i
222                }
223            }
224        }
225        return -1
226    }
227
228    private lastIndexOfNumber(val: Number, fi: int): int {
229        const unboxedVal: number = val.valueOf()
230        if (isNaN(val)) {
231            return -1
232        }
233        for (let i = fi; i >= 0; i--) {
234            const tmp = this.$_get_unsafe(i)
235            if (tmp instanceof Number) {
236                if (unboxedVal == tmp.unboxed()) {
237                    return i
238                }
239            }
240        }
241        return -1
242    }
243
244    private lastIndexOfFloat(val: Float, fi: int): int {
245        const unboxedVal: float = val.unboxed()
246        if (isNaN(val)) {
247            return -1
248        }
249        for (let i = fi; i >= 0; i--) {
250            const tmp = this.$_get_unsafe(i)
251            if (tmp instanceof Float) {
252                if (unboxedVal == tmp.unboxed()) {
253                        return i
254                }
255            }
256        }
257        return -1
258    }
259
260    private lastIndexOfLong(val: Long, fi: int): int {
261        const unboxedVal: long = val.unboxed()
262        for (let i = fi; i >= 0; i--) {
263            const tmp = this.$_get_unsafe(i)
264            if (tmp instanceof Long) {
265                if (tmp == unboxedVal) {
266                    return i
267                }
268            }
269        }
270        return -1
271    }
272
273    private lastIndexOfInt(val: Int, fi: int): int {
274        const unboxedVal: int = val.unboxed()
275        for (let i = fi; i >= 0; i--) {
276            const tmp = this.$_get_unsafe(i)
277            if (tmp instanceof Int) {
278                if (tmp == unboxedVal) {
279                    return i
280                }
281            }
282        }
283        return -1
284    }
285
286    private lastIndexOfCommon(val: T, fi: int): int {
287        for (let i = fi; i >= 0; i--) {
288            if (val == this.$_get_unsafe(i)) {
289                return i
290            }
291        }
292        return -1
293    }
294
295    private searchUndefined(fi: int, len: int): boolean {
296        for (let i = fi; i < len; i++) {
297            if (this.$_get_unsafe(i) instanceof undefined) {
298                return true
299            }
300        }
301        return false
302    }
303
304    private searchNull(fi: int, len: int): boolean {
305        for (let i = fi; i < len; i++) {
306            if (this.$_get_unsafe(i) instanceof null) {
307                return true
308            }
309        }
310        return false
311    }
312
313    private searchString(val: String, fi: int, len: int): boolean {
314        for (let i = fi; i < len; i++) {
315            const tmp = this.$_get_unsafe(i)
316            if (tmp instanceof String) {
317                if (tmp == val) {
318                    return true
319                }
320            }
321        }
322        return false
323    }
324
325    private searchNumber(val: Number, fi: int, len: int): boolean {
326        const unboxedVal: number = val.valueOf()
327        if (isNaN(unboxedVal)) {
328            for (let i = fi; i < len; i++) {
329                const tmp = this.$_get_unsafe(i)
330                if (tmp instanceof Number) {
331                    if (isNaN(tmp.valueOf())) {
332                        return true
333                    }
334                }
335            }
336        } else {
337            for (let i = fi; i < len; i++) {
338                const tmp = this.$_get_unsafe(i)
339                if (tmp instanceof Number) {
340                    if (unboxedVal == tmp.unboxed()) {
341                        return true
342                    }
343                }
344            }
345        }
346        return false
347    }
348
349    private searchFloat(val: Float, fi: int, len: int): boolean {
350        const unboxedVal: float = val.unboxed()
351        if (isNaN(unboxedVal)) {
352            for (let i = fi; i < len; i++) {
353                const tmp = this.$_get_unsafe(i)
354                if (tmp instanceof Float) {
355                    if (isNaN(tmp.unboxed())) {
356                        return true
357                    }
358                }
359            }
360        } else {
361            for (let i = fi; i < len; i++) {
362                const tmp = this.$_get_unsafe(i)
363                if (tmp instanceof Float) {
364                    if (unboxedVal == tmp.unboxed()) {
365                        return true
366                    }
367                }
368            }
369        }
370        return false
371    }
372
373    private searchLong(val: Long, fi: int, len: int): boolean {
374        const unboxedVal: long = val.unboxed()
375        for (let i = fi; i < len; i++) {
376            const tmp = this.$_get_unsafe(i)
377            if (tmp instanceof Long) {
378                if (tmp == unboxedVal) {
379                    return true
380                }
381            }
382        }
383        return false
384    }
385
386    private searchInt(val: Int, fi: int, len: int): boolean {
387        const unboxedVal: int = val.unboxed()
388        for (let i = fi; i < len; i++) {
389            const tmp = this.$_get_unsafe(i)
390            if (tmp instanceof Int) {
391                if (tmp == unboxedVal) {
392                    return true
393                }
394            }
395        }
396        return false
397    }
398
399    private searchCommon(val: T, fi: int, len: int): boolean {
400        for (let i = fi; i < len; i++) {
401            if (val == this.$_get_unsafe(i)) {
402                return true;
403            }
404        }
405        return false
406    }
407
408    private indexOfUndefined(fi: int, len: int): int {
409        for (let i = fi; i < len; i++) {
410            if (this.$_get_unsafe(i) instanceof undefined) {
411                return i
412            }
413        }
414        return -1
415    }
416
417    private indexOfNull(fi: int, len: int): int {
418        for (let i = fi; i < len; i++) {
419            if (this.$_get_unsafe(i) instanceof null) {
420                return i
421            }
422        }
423        return -1
424    }
425
426    private indexOfString(val: String, fi: int, len: int): int {
427        for (let i = fi; i < len; i++) {
428            const tmp = this.$_get_unsafe(i)
429            if (tmp instanceof String) {
430                if (tmp == val) {
431                    return i
432                }
433            }
434        }
435        return -1
436    }
437
438    private indexOfNumber(val: Number, fi: int, len: int): int {
439        const unboxedVal: number = val.valueOf()
440        if (isNaN(val)) {
441            return -1
442        }
443        for (let i = fi; i < len; i++) {
444            const tmp = this.$_get_unsafe(i)
445            if (tmp instanceof Number) {
446                if (unboxedVal == tmp.unboxed()) {
447                    return i
448                }
449            }
450        }
451        return -1
452    }
453
454    private indexOfFloat(val: Float, fi: int, len: int): int {
455        const unboxedVal: float = val.unboxed()
456        if (isNaN(val)) {
457            return -1
458        }
459        for (let i = fi; i < len; i++) {
460            const tmp = this.$_get_unsafe(i)
461            if (tmp instanceof Float) {
462                if (unboxedVal == tmp.unboxed()) {
463                    return i
464                }
465            }
466        }
467        return -1
468    }
469
470    private indexOfLong(val: Long, fi: int, len: int): int {
471        const unboxedVal: long = val.unboxed()
472        for (let i = fi; i < len; i++) {
473            const tmp = this.$_get_unsafe(i)
474            if (tmp instanceof Long) {
475                if (tmp == unboxedVal) {
476                    return i
477                }
478            }
479        }
480        return -1
481    }
482
483    private indexOfInt(val: Int, fi: int, len: int): int {
484        const unboxedVal: int = val.unboxed()
485        for (let i = fi; i < len; i++) {
486            const tmp = this.$_get_unsafe(i)
487            if (tmp instanceof Int) {
488                if (tmp == unboxedVal) {
489                    return i
490                }
491            }
492        }
493        return -1
494    }
495
496    private indexOfCommon(val: T, fi: int, len: int): int {
497        for (let i = fi; i < len; i++) {
498            if (val == this.$_get_unsafe(i)) {
499                return i
500            }
501        }
502        return -1
503    }
504    /**
505     * Default comparison function for sort algorithm.
506     * Objects are compared as string. Both objects are convereted to string
507     * using `toString()` method and compared using `compareTo() method of `string` class.
508     *
509     * @param a: Object - Object to be compared
510     *
511     * @param b: Object - Object to be compared
512     *
513     * @returns Returns one of values -1, 0, 1 (_less_, _equal_, _greater_ respectively).
514     */
515    private static defaultComparator(a: NullishType, b: NullishType): number {
516        if (a instanceof Number && b instanceof Number) {
517            const x = (a as Number).valueOf()
518            const y = (b as Number).valueOf()
519            if (Number.isInteger(x) && Number.isInteger(y) &&
520                x <= Int.MAX_VALUE / 128 && x >= Int.MIN_VALUE / 128 &&
521                y <= Int.MAX_VALUE / 128 && y >= Int.MIN_VALUE / 128) {
522                let z = x as int
523                let w = y as int
524                return Array.defaultComparatorInts(z, w)
525            }
526        } else if (a instanceof String && b instanceof String) {
527            return a.compareTo(b)
528        }
529        let sa = new String(a)
530        let sb = new String(b)
531        return sa.compareTo(sb)
532    }
533
534    private static defaultComparatorInts(a: int, b: int): number {
535        if (a < 0) {
536            if (b >= 0) {
537                return -1
538            }
539            a *= -1
540            b *= -1
541        } else if (b < 0) {
542            return 1
543        }
544        let aDigs = 1
545        while (10 * aDigs <= a) {
546            aDigs *= 10
547        }
548        let bDigs = 1
549        while (10 * bDigs <= b) {
550            bDigs *= 10
551        }
552
553        while (aDigs > 0 && bDigs > 0) {
554            let r = (a / aDigs) - (b / bDigs)
555            if (r != 0) {
556                return r
557            }
558            aDigs /= 10
559            bDigs /= 10
560        }
561        return (aDigs - bDigs)
562    }
563
564    private static defaultComparatorStr(a: String, b: String) {
565        return a.compareTo(b)
566    }
567
568    /**
569     * Helper function preparing copy of `this` instance of `Array` class' data array.
570     *
571     * @returns Copy of an `Array`'s primitive array data.
572     */
573    private copyArray(): NullishType[] {
574        let len: int = this.actualLength
575        let res = new NullishType[len]
576        for (let i = 0; i < len; i++) {
577            res[i] = this.$_get_unsafe(i)
578        }
579        return res
580    }
581
582    private wrap_default_sort(): void {
583        let idxNonUndef = 0
584        type arrType = String | null
585        try {
586            let strArr = new arrType[this.actualLength]
587            for (let i = 0; i < this.actualLength; i++) {
588                const vl = this.$_get_unsafe(i)
589                // NOTE(aleksander-sotov) We can't use === to compare with undefined due to 18518
590                if (!__runtimeIsSameReference(vl, undefined)) {
591                    if (vl == null) {
592                        this.$_set_unsafe(idxNonUndef, vl as T)
593                        strArr[idxNonUndef] = "null"
594                    } else {
595                        this.$_set_unsafe(idxNonUndef, vl)
596                        strArr[idxNonUndef] = vl.toString()
597                    }
598                    idxNonUndef++
599                }
600            }
601            let sortTo = idxNonUndef
602            for (let i = idxNonUndef; i < this.actualLength; i++) {
603                this.$_set_unsafe(i, undefined as T)
604            }
605
606            sort_default<NullishType>(this.buffer, strArr, 0, sortTo)
607        }
608        catch (e) {
609            if (e instanceof OutOfMemoryError) {
610                this.slow_default_sort()
611            } else {
612                throw e as Error
613            }
614        }
615    }
616
617    private slow_default_sort(): void {
618        let idxNonUndef = 0
619        const cmp: (l: NullishType, r: NullishType) => number = (l: NullishType, r: NullishType): number => {
620            return Array.defaultComparator(l, r)
621        }
622        for (let i = 0; i < this.actualLength; i++) {
623            const vl = this.$_get_unsafe(i)
624            if (!__runtimeIsSameReference(vl, undefined)) {
625                this.$_set_unsafe(idxNonUndef, vl)
626                idxNonUndef++
627            }
628        }
629        let sortTo = idxNonUndef
630        for (let i = idxNonUndef; i < this.actualLength; i++) {
631            this.$_set_unsafe(i, undefined as T)
632        }
633        sort_stable<NullishType>(this.buffer, 0, sortTo, cmp)
634    }
635
636    /**
637     * Reorders elements of `this` using comparator function.
638     *
639     * @param comparator function that defines the sort order.
640     *
641     * @note Mutating method
642     *
643     * NOTE clarify UTF-16 or UTF-8
644     */
645    public sort(comparator?: (a: T, b: T) => number): this {
646        let cmp: (l: NullishType, r: NullishType) => number = (l: NullishType, r: NullishType): number => {
647            return Array.defaultComparator(l, r)
648        }
649        let sortTo = this.actualLength
650        if (!__runtimeIsSameReference(comparator, undefined)) {
651            cmp = (l: NullishType, r: NullishType): number => {
652                return comparator!(l as T, r as T)
653            }
654            sort_stable<NullishType>(this.buffer, 0, sortTo, cmp)
655        } else {
656            this.wrap_default_sort()
657        }
658        return this
659    }
660
661    /**
662     * Removes the first element from an array and returns that removed element.
663     * This method changes the length of the array.
664     *
665     * @returns shifted element, i.e. that was at index zero
666     */
667    public shift(): T | undefined {
668        if(this.actualLength == 0) {
669            return undefined
670        }
671        let obj = this.$_get_unsafe(0)
672        const other = this.slice(1, this.actualLength)
673        this.buffer = other.buffer
674        this.actualLength = other.actualLength
675        return obj
676    }
677
678    /**
679     * Removes the last element from an array and returns that element.
680     * This method changes the length of the array.
681     *
682     * @returns removed element
683     */
684    public pop(): T | undefined {
685        if(this.actualLength == 0) {
686            return undefined
687        }
688        let obj = this.$_get_unsafe(this.actualLength - 1)
689        this.buffer[this.actualLength - 1] = null
690        this.actualLength--
691        return obj
692    }
693
694    /**
695     * Adds the specified elements to the end of an array and returns the new length of the array.
696     *
697     * @returns new length
698     */
699    public push(...val: T[]): number {
700        this.ensureUnusedCapacity(val.length)
701        for (let i = 0; i < val.length; i++) {
702            this.buffer[this.actualLength + i] = val[i]
703        }
704        this.actualLength += val.length
705        return this.actualLength
706    }
707
708    /**
709     * Adds the specified elements to the end of an array and returns the new length of the array.
710     *
711     * @returns new length
712     */
713    public pushECMA(...val: T[]): number {
714        this.ensureUnusedCapacity(val.length)
715        for (let i = 0; i < val.length; i++) {
716            this.buffer[this.actualLength + i] = val[i]
717        }
718        this.actualLength += val.length
719        return this.actualLength
720    }
721
722    private ensureUnusedCapacity(cap: int): void {
723        if (this.actualLength + cap > this.buffer.length) {
724            const copy = new NullishType[this.buffer.length * 2 + cap]
725            for (let i = 0; i < this.actualLength; i++) {
726                copy[i] = this.buffer[i]
727            }
728            this.buffer = copy
729        }
730    }
731
732    /**
733     * Changes the contents of an array by removing or replacing existing elements
734     * and/or adding new elements in place.
735     *
736     * @param start index
737     *
738     * @param delete number of items after start index
739     *
740     * @returns an Array with deleted elements
741     */
742    public splice(start: number, delete: Number | undefined, ...items: T[]): Array<T> {
743        return this.splice(start as int, asIntOrDefault(delete, this.actualLength), ...items)
744    }
745
746    /**
747     * Changes the contents of an array by removing or replacing existing elements
748     * and/or adding new elements in place.
749     *
750     * @param start index
751     *
752     * @param delete number of items after start index
753     *
754     * @returns an Array with deleted elements
755     */
756    public splice(start: int, delete: int, ...items: T[]): Array<T> {
757        start = normalizeIndex(start, this.actualLength)
758        if (delete < 0) {
759            delete = 0
760        }
761        if (start > this.actualLength - delete) {
762            delete = this.actualLength - start
763        }
764        // this: [left middle right], we must replace middle with `items`
765
766        this.ensureUnusedCapacity(items.length - delete)
767        const oldLen = this.actualLength
768        this.actualLength = this.actualLength - delete + items.length
769
770        let ret = new Array<T>(delete)
771        let lastSet = start
772        // left part remains unchanged
773        // copy excluded part
774        for (let i = 0; i < delete; i++) {
775            ret.buffer[i] = this.buffer[start + i]
776        }
777        // move right part to the right of the buffer
778        const rightLen = oldLen - start - delete
779        if (items.length > delete) {
780            for (let i = 0; i < rightLen; i++) {
781                this.buffer[this.actualLength - 1 - i] = this.buffer[oldLen - 1 - i]
782            }
783        } else {
784            for (let i = 0; i < rightLen; i++) {
785                this.buffer[start + items.length + i] = this.buffer[start + delete + i]
786            }
787        }
788        // insert middle part
789        for (let i = 0; i < items.length; i++) {
790            this.buffer[start + i] = items[i]
791        }
792        return ret
793    }
794
795    /**
796     * Changes the contents of an array by removing or replacing existing elements
797     * and/or adding new elements in place.
798     *
799     * @param start index
800     *
801     * @returns an Array with deleted elements from start to the last element of the current instance
802     */
803    public splice(start: number): Array<T> {
804        return this.splice(start as int)
805    }
806
807    /**
808     * Changes the contents of an array by removing or replacing existing elements
809     * and/or adding new elements in place.
810     *
811     * @param start index
812     *
813     * @returns an Array with deleted elements from start to the last element of the current instance
814     */
815    public splice(start: int): Array<T> {
816        return this.splice(start, this.actualLength)
817    }
818
819    /**
820     * Checks whether the passed value is an Array.
821     *
822     * @param arr
823     *
824     * @returns true is arr is a non-nullish array, false otherwise
825     */
826    public static isArray(o: NullishType): boolean {
827        if (o instanceof Array) {
828            return true
829        }
830        return (Type.of(o) instanceof ArrayType)
831    }
832
833    /**
834     * Creates a new Array instance from a variable number of arguments,
835     * regardless of number or type of the arguments.
836     *
837     * @param values an initilizer
838     *
839     * @returns a newly created Array
840     */
841    public static of<T>(...values: T[]): Array<T> {
842        const ret = new Array<T>()
843        ret.ensureUnusedCapacity(values.length)
844        for (let i = 0; i < values.length; i++) {
845            ret.push(values[i])
846        }
847        return ret
848    }
849
850    /**
851     * Adds the specified elements to the beginning of an Array
852     * and returns the new length of the Array.
853     *
854     * @param values data to be added
855     *
856     * @returns new length of the Array
857     */
858    public unshift(...values: T[]): number {
859        let buffer = this.buffer
860        if (this.buffer.length <= values.length + this.actualLength) {
861            buffer = new NullishType[this.buffer.length * 2 + values.length]
862        }
863        for (let i = 0; i < this.actualLength; i++) {
864            buffer[this.actualLength + values.length - i - 1] = this.buffer[this.actualLength - 1 - i]
865        }
866        for (let i = 0; i < values.length; i++) {
867            buffer[i] = values[i]
868        }
869        this.buffer = buffer
870        this.actualLength += values.length
871        return this.actualLength
872    }
873
874    /**
875     * Returns an iterator over all indices
876     */
877    public override keys(): IterableIterator<Number> {
878        return new ArrayKeysIterator<T>(this)
879    }
880
881    /**
882     * Returns an iterator over all values
883     */
884    public override $_iterator(): IterableIterator<T> {
885        return this.values()
886    }
887
888    // === methods with uncompatible implementation ===
889    /**
890     * Returns the elements of an array that meet the condition specified in a callback function.
891     *
892     * @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.
893     *
894     * @returns New `Array` instance constructed from `this` with elements filtered using test function `predicate`.
895     */
896    public override filter(predicate: (value: T, index: number) => boolean): Array<T> {
897        const res = new Array<T>()
898        for (let i: int = 0; i < this.actualLength; i++) {
899            const val = this.$_get_unsafe(i)
900            if (predicate(val, i as number)) {
901                res.push(val)
902            }
903        }
904        return res
905    }
906
907    /**
908     * Creates a new Array with all sub-array elements concatenated
909     * into it recursively up to the specified depth.
910     *
911     * @param depth
912     *
913     * @returns a flattened Array with respect to depth
914     */
915    public flat<U>(depth: number): Array<U> {
916        return this.flat<U>(depth as int)
917    }
918
919    /**
920     * Creates a new Array with all sub-array elements concatenated
921     * into it recursively up to the specified depth.
922     *
923     * @param depth
924     *
925     * @returns a flattened Array with respect to depth
926     */
927    public flat<U>(depth: int): Array<U> {
928        let ret = new Array<U>()
929        this.flatImpl<U>(depth, ret)
930        return ret
931    }
932
933    private flatImpl<U>(depth: int, to: Array<U>) {
934        throw new Error("not implemented")
935    }
936
937    /**
938     * Creates a new Array with all sub-array elements concatenated
939     *
940     * @returns a flattened Array
941     */
942    public flat<U>(): Array<U> {
943        return this.flat<U>(1)
944    }
945
946    /**
947     * Applies flat and than map
948     *
949     * fn a function to apply
950     *
951     * @return new Array after map and than flat
952     */
953    // NOTE(ivan-tyulyandin): TBD, flatMap may be not subset, see ReadonlyArray
954    public flatMap<U>(fn: (v: T, k: number, arr: Array<T>) => U): Array<U> {
955        let mapped: Array<U> = this.map<U>(fn)
956        return mapped.flat<U>()
957    }
958
959    /**
960     * Applies flat and than map
961     *
962     * fn a function to apply
963     *
964     * @return new Array after map and than flat
965     */
966     // NOTE(ivan-tyulyandin): TBD, flatMap may be not subset, see ReadonlyArray
967    public flatMap<U>(fn: (v: T, k: number) => U): Array<U> {
968        let mapped: Array<U> = this.map<U>(fn)
969        return mapped.flat<U>()
970    }
971
972    /**
973     * Applies flat and than map
974     *
975     * fn a function to apply
976     *
977     * @return new Array after map and than flat
978     */
979    // NOTE(ivan-tyulyandin): TBD, flatMap may be not subset, see ReadonlyArray
980    public flatMap<U>(fn: (v: T) => U): Array<U> {
981        let mapped: Array<U> = this.map<U>(fn)
982        return mapped.flat<U>()
983    }
984
985    // === methods common among all arrays ===
986% require 'ostruct'
987% ctx = OpenStruct.new
988% $ctx = ctx
989% ctx.this = 'this'
990% ctx.this_arg = ''
991% ctx.this_len_int = 'this.actualLength'
992% ctx.array_len_int = Proc.new { |v| "#{v}.actualLength" }
993% ctx.access_public = 'public'
994% ctx.access_private = 'private'
995% ctx.override = 'override'
996% ctx.override_expected_here = '' # hide 'override' when it cannot be implemented
997% ctx.get_unsafe = Proc.new { |t, i| "#{t}.$_get_unsafe(#{i})" }
998% ctx.set_unsafe = Proc.new { |t, i, v| "#{t}.$_set_unsafe(#{i}, #{v})" }
999% ctx.el_type = 'T'
1000% ctx.el_type_boxed = 'T'
1001% ctx.this_type = 'Array<T>'
1002% ctx.this_return_type = 'this'
1003% ctx.clone_this = 'new Array<T>(FROM_BUFFER, this.copyArray())'
1004% ctx.make_buffer = Proc.new { |l, elt|
1005%   "new NullishType[#{l}]"
1006% }
1007% ctx.from_buffer = Proc.new { |b, elt|
1008%   elt ||= ctx.el_type
1009%   "new Array<#{elt}>(FROM_BUFFER, #{b})"
1010% }
1011% ctx.array_of_type = Proc.new { |t| "Array<#{t}>" }
1012% ctx.this_call = Proc.new { |f| "this.#{f}(" }
1013% ctx.arr_method_call = Proc.new { |t, f| "#{t}.#{f}(" }
1014% ctx.this_generic = ''
1015% ctx.this_generic_one = ''
1016% ctx.this_iterator_generic = '<T>'
1017% template = ERB.new(File.read("Array_common.erb"), nil, '%', eoutvar: '_sub01')
1018<%= template.result(ctx.instance_eval { binding }).gsub(/^/, '    ') %>
1019% template = ERB.new(File.read("Array_map.erb"), nil, '%', eoutvar: '_sub02')
1020% ctx.mapped_type = 'U'
1021% ctx.map_generic = '<U>'
1022<%= template.result(ctx.instance_eval { binding }).gsub(/^/, '    ') %>
1023}
1024% template = ERB.new(File.read("Array_common_top_scope.erb"), nil, '%', eoutvar: '_sub03')
1025<%= template.result(ctx.instance_eval { binding }).rstrip %>
1026