1/*
2 * Copyright (c) 2022 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 */
15interface ArkPrivate {
16  Stack: number;
17  Load(key: number): Object;
18}
19interface errorUtil{
20  checkRangeError(paramName: string, receivedValue: unknown, min?: number, max?: number, options?: string): void;
21  checkNewTargetIsNullError(className: string, isNull: boolean): void;
22  checkBindError(methodName: string, className: Function, self: unknown): void;
23  checkTypeError(paramName: string, type: string, receivedValue: unknown): void;
24}
25let flag: boolean = false;
26let fastStack: Object = undefined;
27let arkPritvate: ArkPrivate = globalThis.ArkPrivate || undefined;
28if (arkPritvate !== undefined) {
29  fastStack = arkPritvate.Load(arkPritvate.Stack);
30} else {
31  flag = true;
32}
33declare function requireNapi(s: string): errorUtil;
34if (flag || fastStack === undefined) {
35  const errorUtil = requireNapi('util.struct');
36  class HandlerStack<T> {
37    private isOutBounds(obj: Stack<T>, prop: string): void {
38      let index: number = Number.parseInt(prop);
39      if (Number.isInteger(index)) {
40        errorUtil.checkRangeError('index', index, 0, obj.length - 1);
41      }
42    }
43    get(obj: Stack<T>, prop: string): T {
44      if (typeof prop === 'symbol') {
45        return obj[prop];
46      }
47      this.isOutBounds(obj, prop);
48      return obj[prop];
49    }
50    set(obj: Stack<T>, prop: any, value: T): boolean {
51      if (prop === 'elementNum' || prop === 'capacity') {
52        obj[prop] = value;
53        return true;
54      }
55      this.isOutBounds(obj, prop);
56      let index: number = Number.parseInt(prop);
57      if (index >= 0 && index < obj.length && Number.isInteger(index)) {
58        obj[index] = value;
59        return true;
60      }
61      return false;
62    }
63    ownKeys(obj: Stack<T>): Array<string> {
64      let keys: string[] = [];
65      let length: number = obj.length;
66      for (let i: number = 0; i < length; i++) {
67        keys.push(i.toString());
68      }
69      return keys;
70    }
71    defineProperty(): boolean {
72      return true;
73    }
74    getOwnPropertyDescriptor(obj: Stack<T>, prop: string): Object {
75      this.isOutBounds(obj, prop);
76      let index: number = Number.parseInt(prop);
77      if (index >= 0 && index < obj.length && Number.isInteger(index)) {
78        return Object.getOwnPropertyDescriptor(obj, prop);
79      }
80      return Object;
81    }
82    setPrototypeOf(): T {
83      throw new Error(`Can't setPrototype on Stack Object`);
84    }
85  }
86  interface IterableIterator<T> {
87    next: () => {
88      value: T;
89      done: boolean;
90    };
91  }
92  class Stack<T> {
93    private elementNum: number = 0;
94    private capacity: number = 10; // 10 : means number
95    constructor() {
96      errorUtil.checkNewTargetIsNullError('Stack', !new.target);
97      return new Proxy(this, new HandlerStack());
98    }
99    get length(): number {
100      return this.elementNum;
101    }
102    push(item: T): T {
103      errorUtil.checkBindError('push', Stack, this);
104      if (this.isFull()) {
105        this.increaseCapacity();
106      }
107      this[this.elementNum++] = item;
108      return item;
109    }
110    pop(): T {
111      errorUtil.checkBindError('pop', Stack, this);
112      if (this.isEmpty()) {
113        return undefined;
114      }
115      let result: T = undefined;
116      result = this[this.length - 1];
117      this.elementNum--;
118      return result;
119    }
120    peek(): T {
121      errorUtil.checkBindError('peek', Stack, this);
122      if (this.isEmpty()) {
123        return undefined;
124      }
125      return this[this.length - 1];
126    }
127    locate(element: T): number {
128      errorUtil.checkBindError('locate', Stack, this);
129      for (let i: number = 0; i < this.length; i++) {
130        if (this[i] === element) {
131          return i;
132        }
133      }
134      return -1;
135    }
136    isEmpty(): boolean {
137      errorUtil.checkBindError('isEmpty', Stack, this);
138      return this.elementNum === 0;
139    }
140    forEach(callbackfn: (value: T, index?: number, stack?: Stack<T>) => void,
141      thisArg?: Object): void {
142      errorUtil.checkBindError('forEach', Stack, this);
143      errorUtil.checkTypeError('callbackfn', 'callable', callbackfn);
144      for (let i: number = 0; i < this.length; i++) {
145        callbackfn.call(thisArg, this[i], i, this);
146      }
147    }
148    private isFull(): boolean {
149      return this.elementNum === this.capacity;
150    }
151    private increaseCapacity(): void {
152      this.capacity = 1.5 * this.capacity; // 1.5 : means number
153    }
154    [Symbol.iterator](): IterableIterator<T> {
155      errorUtil.checkBindError('Symbol.iterator', Stack, this);
156      let count: number = 0;
157      return {
158        next: function (): { done: boolean, value: T } {
159          let done: boolean = false;
160          let value: T = undefined;
161          done = count >= this.elementNum;
162          value = done ? undefined : this[count++];
163          return {
164            done: done,
165            value: value,
166          };
167        },
168      };
169    }
170  }
171  Object.freeze(Stack);
172  fastStack = Stack;
173}
174export default fastStack;
175