diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index e3e201d61a5af493c0a6bd9be32a35d90433d680..b1a433b15ff33d19290d83dda20e594619f3284f 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -60,7 +60,10 @@ ohos_prebuilt_etc("commonsdk_arkts_etc") { generate_static_abc("commonsdk_arkts") { base_url = "./arkts" - files = [ "./arkts/@arkts.math.Decimal.ets" ] + files = [ + "./arkts/@arkts.math.Decimal.ets", + "./arkts/@arkts.collections.ets", + ] is_boot_abc = "True" device_dst_file = "/system/framework/commonsdk_arkts.abc" } diff --git a/sdk/arkts/@arkts.collections.ets b/sdk/arkts/@arkts.collections.ets new file mode 100644 index 0000000000000000000000000000000000000000..3bf63600a32cfef0c479021789ae59782a446129 --- /dev/null +++ b/sdk/arkts/@arkts.collections.ets @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import { BusinessError } from "@ohos.base"; + +const OutOfBoundsErrorCodeId: number = 10200001; +const TypeErrorCodeId: number = 401; + +export namespace collections { + + final class BitVectorIterator_T implements IterableIterator { + private index: number = 0; + private parent: BitVector; + + constructor(parent: BitVector) { + this.parent = parent; + } + + override next(): IteratorResult { + if (this.parent.length === 0 || this.index >= this.parent.length) { + return new IteratorResult(); + } + + return new IteratorResult(this.parent[this.index++]); + } + + override $_iterator(): IterableIterator { + return this; + } + } + /** + * A class representing a bit vector. + * The bit vector is implemented using an array of int integers. + * Each int integer can store 64 bits, and the bit vector can store a specified number of bits. + */ + export class BitVector implements Iterable { + + static readonly BIT_SET_LENGTH = 32; // ArkTS Specification, Release 1.1.0 + + private buffer: Array; + private _length: number = 0; + + public constructor(length: number) { + if (length === 0) { + this.buffer = new Array(); + } else { + this._length = length; + this.buffer = Array.create(this.getUsedCapacity(), 0); + } + } + + public get length(): number { + return this._length; + } + + public push(element: number): boolean { + let index: [number, number] = this.computeElementIdAndBitId(this._length); + this.checkAndIncrement(index[0]); + + this.setBitUnsafe(this._length++, element); + + return true; + } + + public pop(): number | undefined { + if (this._length === 0) { + return undefined; + } + + return this.getBitUnsafe(--this._length); + } + + public $_get(index: number): number { + if (index < 0 || index >= this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"index\" is out of range. It must be >= 0 && <= ${this._length - 1}. Received value is: ${index}`)); + } + + return this.getBitUnsafe(index); + } + + public $_set(index: number, value: number): void { + if (index < 0 || index >= this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"index\" is out of range. It must be >= 0 && <= ${this._length - 1}. Received value is: ${index}`)); + } + + this.setBitUnsafe(index, value); + } + + public has(element: number, fromIndex: number, toIndex: number): boolean { + this.integerCheckRange(fromIndex, toIndex); + + if (toIndex < 0 || toIndex > this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"toIndex\" is out of range. It must be >= 0 && <= ${this._length}. Received value is: ${toIndex}`)); + } + + if (fromIndex < 0 || fromIndex >= toIndex) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"fromIndex\" is out of range. It must be >= 0 && <= ${toIndex - 1}. Received value is: ${fromIndex}`)); + } + + let startIndex = this.computeElementIdAndBitId(fromIndex as int); + let endIndex = this.computeElementIdAndBitId(toIndex as int); + + if (startIndex[0] === endIndex[0]) { + return this.checkBit(this.buffer[startIndex[0]], startIndex[1], endIndex[1] - 1, element); + } + + if (this.checkBit(this.buffer[startIndex[0]], startIndex[1], BitVector.BIT_SET_LENGTH - 1, element)) { + return true; + } + + startIndex[0]++; + while (startIndex[0] < endIndex[0]) { + if (this.checkBit(this.buffer[startIndex[0]], 0, BitVector.BIT_SET_LENGTH - 1, element)) { + return true; + } + startIndex[0]++; + } + + if (this.checkBit(this.buffer[endIndex[0]], 0, endIndex[1], element)) { + return true; + } + + return false; + } + + public override $_iterator(): IterableIterator { + return new BitVectorIterator_T(this); + } + + public values(): IterableIterator { + return this.$_iterator(); + } + + public flipBitByIndex(index: number): void { + this.integerCheck("index", index); + + if (index < 0 || index >= this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"index\" is out of range. It must be >= 0 && <= ${this._length - 1}. Received value is: ${index}`)); + } + + let bitIndex: [number, number] = this.computeElementIdAndBitId(index); + this.buffer[bitIndex[0]] = this.flipBits(this.buffer[bitIndex[0]], bitIndex[1], bitIndex[1]); + } + + public flipBitsByRange(fromIndex: number, toIndex: number): void { + this.integerCheckRange(fromIndex, toIndex); + + if (toIndex < 0 || toIndex > this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"toIndex\" is out of range. It must be >= 0 && <= ${this._length}. Received value is: ${toIndex}`)); + } + + if (fromIndex < 0 || fromIndex >= toIndex) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"fromIndex\" is out of range. It must be >= 0 && <= ${toIndex - 1}. Received value is: ${fromIndex}`)); + } + + let startIndex = this.computeElementIdAndBitId(fromIndex); + let endIndex = this.computeElementIdAndBitId(toIndex); + + if (startIndex[0] === endIndex[0]) { + this.buffer[startIndex[0]] = this.flipBits(this.buffer[startIndex[0]], startIndex[1], endIndex[1] - 1); + return; + } + + this.buffer[startIndex[0]] = this.flipBits(this.buffer[startIndex[0]], startIndex[1], BitVector.BIT_SET_LENGTH - 1); + + ++startIndex[0]; + while (startIndex[0] < endIndex[0]) { + this.buffer[startIndex[0]] = this.flipBits(this.buffer[startIndex[0]], 0, BitVector.BIT_SET_LENGTH - 1); + ++startIndex[0]; + } + + if (endIndex[1] > 0) { + this.buffer[endIndex[0]] = this.flipBits(this.buffer[endIndex[0]], 0, endIndex[1] - 1); + } + } + + public getIndexOf(element: number, fromIndex: number, toIndex: number): number { + this.integerCheckRange(fromIndex, toIndex); + + if (toIndex < 0 || toIndex > this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"toIndex\" is out of range. It must be >= 0 && <= ${this._length}. Received value is: ${toIndex}`)); + } + + if (fromIndex < 0 || fromIndex >= toIndex) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"fromIndex\" is out of range. It must be >= 0 && <= ${toIndex - 1}. Received value is: ${fromIndex}`)); + } + + let startIndex = this.computeElementIdAndBitId(fromIndex); + let endIndex = this.computeElementIdAndBitId(toIndex); + + if (startIndex[0] === endIndex[0]) { + let bitIndex = this.findFirstBit(this.buffer[startIndex[0]], startIndex[1], endIndex[1] - 1, element); + return bitIndex != -1 ? this.computeIndex(startIndex[0], bitIndex) : -1; + } + + let bitIndex = this.findFirstBit(this.buffer[startIndex[0]], startIndex[1], BitVector.BIT_SET_LENGTH - 1, element); + if (bitIndex != -1) { + return this.computeIndex(startIndex[0], bitIndex) + } + + ++startIndex[0]; + while (startIndex[0] < endIndex[0]) { + bitIndex = this.findFirstBit(this.buffer[startIndex[0]], 0, BitVector.BIT_SET_LENGTH - 1, element); + if (bitIndex != -1) { + return this.computeIndex(startIndex[0], bitIndex); + } + + ++startIndex[0]; + } + + bitIndex = this.findFirstBit(this.buffer[endIndex[0]], 0, BitVector.BIT_SET_LENGTH - 1, element); + if (bitIndex != -1 && bitIndex < endIndex[1]) { + return this.computeIndex(endIndex[0], bitIndex); + } + + return -1; + } + + public getLastIndexOf(element: number, fromIndex: number, toIndex: number): number { + this.integerCheckRange(fromIndex, toIndex); + + if (toIndex < 0 || toIndex > this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"toIndex\" is out of range. It must be >= 0 && <= ${this._length}. Received value is: ${toIndex}`)); + } + + if (fromIndex < 0 || fromIndex >= toIndex) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"fromIndex\" is out of range. It must be >= 0 && <= ${toIndex - 1}. Received value is: ${fromIndex}`)); + } + + let startIndex = this.computeElementIdAndBitId(fromIndex); + let endIndex = this.computeElementIdAndBitId(toIndex); + + if (startIndex[0] === endIndex[0]) { + let bitIndex = this.findLastBit(this.buffer[startIndex[0]], startIndex[1], endIndex[1] - 1, element); + return bitIndex != -1 ? this.computeIndex(startIndex[0], bitIndex) : -1; + } + + if (endIndex[1] > 0) { + let bitIndex = this.findLastBit(this.buffer[endIndex[0]], 0, endIndex[1] - 1, element); + if (bitIndex != -1) { + return this.computeIndex(endIndex[0], bitIndex); + } + } + + --endIndex[0]; + while (startIndex[0] < endIndex[0]) { + let bitIndex = this.findLastBit(this.buffer[endIndex[0]], 0, BitVector.BIT_SET_LENGTH - 1, element); + if (bitIndex != -1) { + return this.computeIndex(endIndex[0], bitIndex); + } + + --endIndex[0]; + } + + let bitIndex = this.findLastBit(this.buffer[startIndex[0]], startIndex[1], BitVector.BIT_SET_LENGTH - 1, element); + if (bitIndex != -1) { + return this.computeIndex(startIndex[0], bitIndex); + } + + return -1; + } + + public getBitsByRange(fromIndex: number, toIndex: number): BitVector { + this.integerCheckRange(fromIndex, toIndex); + + if (toIndex < 0 || toIndex > this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"toIndex\" is out of range. It must be >= 0 && <= ${this._length}. Received value is: ${toIndex}`)); + } + + if (fromIndex < 0 || fromIndex >= toIndex) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"fromIndex\" is out of range. It must be >= 0 && <= ${toIndex - 1}. Received value is: ${fromIndex}`)); + } + + let startIndex = this.computeElementIdAndBitId(fromIndex); + let endIndex = this.computeElementIdAndBitId(toIndex); + + let newBitVector = new BitVector(toIndex - fromIndex); + + if (startIndex[0] === endIndex[0]) { + newBitVector.buffer[0] = this.getBits(this.buffer[startIndex[0]], startIndex[1], endIndex[1] - 1); + return newBitVector; + } + + for (let bit of this) { + newBitVector.push(bit); + } + + return newBitVector; + } + + public setBitsByRange(element: number, fromIndex: number, toIndex: number): void { + this.integerCheckRange(fromIndex, toIndex); + + if (toIndex < 0 || toIndex > this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"toIndex\" is out of range. It must be >= 0 && <= ${this._length}. Received value is: ${toIndex}`)); + } + + if (fromIndex < 0 || fromIndex >= toIndex) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"fromIndex\" is out of range. It must be >= 0 && <= ${toIndex - 1}. Received value is: ${fromIndex}`)); + } + + let startIndex = this.computeElementIdAndBitId(fromIndex); + let endIndex = this.computeElementIdAndBitId(toIndex); + + if (startIndex[0] === endIndex[0]) { + this.buffer[startIndex[0]] = this.setBits(this.buffer[startIndex[0]], startIndex[1], endIndex[1] - 1, element); + return; + } + + this.buffer[startIndex[0]] = this.setBits(this.buffer[startIndex[0]], startIndex[1], BitVector.BIT_SET_LENGTH - 1, element); + + ++startIndex[0]; + while (startIndex[0] < endIndex[0]) { + this.buffer[startIndex[0]] = this.setBits(this.buffer[startIndex[0]], 0, BitVector.BIT_SET_LENGTH - 1, element); + ++startIndex[0]; + } + + if (endIndex[1] != 0) { + this.buffer[startIndex[0]] = this.setBits(this.buffer[endIndex[0]], 0, endIndex[1] - 1, element); + } + } + + public setAllBits(element: number): void { + if (element === 0) { + for (let i = 0; i < this.getUsedCapacity(); i++) { + this.buffer[i] = 0; + } + } else { + for (let i = 0; i < this.getUsedCapacity(); i++) { + this.buffer[i] = ~0x0; + } + } + } + + public resize(size: number): void { + this.integerCheck("size", size); + + if (size < 0) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"length\" is out of range. It must be >= 0. Received value is: ${size}`)); + } + + if (this._length >= size) { + this._length = size; + return; + } + + let newIndex = this.computeElementIdAndBitId(size); + let index = this.computeElementIdAndBitId(this.length); + + this.checkAndIncrement(index[0]); + this.buffer[index[0]] = this.setBits(this.buffer[index[0]], index[1], BitVector.BIT_SET_LENGTH - 1, 0); + + if (index[0] != newIndex[0]) { + ++index[0]; + while (index[0] <= newIndex[0]) { + this.buffer.push(0); + ++index[0]; + } + } + this._length = size; + } + + public getBitCountByRange(element: number, fromIndex: number, toIndex: number): number { + this.integerCheckRange(fromIndex, toIndex); + + if (toIndex < 0 || toIndex > this._length) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"toIndex\" is out of range. It must be >= 0 && <= ${this._length}. Received value is: ${toIndex}`)); + } + + if (fromIndex < 0 || fromIndex >= toIndex) { + throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"fromIndex\" is out of range. It must be >= 0 && <= ${toIndex - 1}. Received value is: ${fromIndex}`)); + } + + let startIndex = this.computeElementIdAndBitId(fromIndex); + let endIndex = this.computeElementIdAndBitId(toIndex); + + if (startIndex[0] === endIndex[0]) { + let count = this.countBit1InRange(this.buffer[startIndex[0]], startIndex[1], endIndex[1] - 1); + return element === 0 ? (toIndex - fromIndex - count) : count; + } + + let count = this.countBit1InRange(this.buffer[startIndex[0]], startIndex[1], BitVector.BIT_SET_LENGTH - 1); + + ++startIndex[0]; + while (startIndex[0] < endIndex[0]) { + count += this.countBit1InRange(this.buffer[startIndex[0]], 0, BitVector.BIT_SET_LENGTH - 1); + ++startIndex[0]; + } + + if (endIndex[1] != 0) { + count += this.countBit1InRange(this.buffer[endIndex[0]], 0, endIndex[1] - 1); + } + + return element === 0 ? (toIndex - fromIndex - count) : count; + } + + private checkAndIncrement(index: number): void { + if (this.buffer.length === index) { + this.buffer.push(0); + } + } + + private integerCheckRange(fromIndex: number, toIndex: number): void { + this.integerCheck("fromIndex", fromIndex); + this.integerCheck("toIndex", toIndex); + } + + private integerCheck(name: string, value: number): void { + if (!Double.isInteger(value)) { + throw new BusinessError(TypeErrorCodeId, new Error(`The type of \"${name}\" must be integer. Received value is: ${value}`)); + } + } + + private getUsedCapacity(): number { + return Math.ceil(this._length / BitVector.BIT_SET_LENGTH); + } + + private computeElementIdAndBitId(index: number): [number, number] { + let elementId = Math.floor(index / BitVector.BIT_SET_LENGTH); + let bitId = index % BitVector.BIT_SET_LENGTH; + return [elementId, bitId]; + } + + private computeIndex(elementId: number, bitId: number): number { + return elementId * BitVector.BIT_SET_LENGTH + bitId; + } + + private getBitUnsafe(index: number): number { + let bitIndex: [number, number] = this.computeElementIdAndBitId(index); + let value = (this.buffer[bitIndex[0]] >> bitIndex[1]) & 1; + + return value; + } + + private setBitUnsafe(index: number, value: number): void { + let bitIndex: [number, number] = this.computeElementIdAndBitId(index); + if (value === 0) { + let mask = ~(1 << bitIndex[1]); + this.buffer[bitIndex[0]] = (this.buffer[bitIndex[0]] & mask); + } else { + this.buffer[bitIndex[0]] = (this.buffer[bitIndex[0]] | (1 << bitIndex[1])); + } + } + + private checkBit(element: int, fromIndex: number, toIndex: number, target_bit: number): boolean { + let length = toIndex - fromIndex + 1; + let mask: int = this.getRangeMask(fromIndex, length); + + if (target_bit !== 0) { + return (element & mask) != 0; + } else { + return (element & mask) != mask; + } + } + + private flipBits(num: int, fromIndex: number, toIndex: number): int { + let length = toIndex - fromIndex + 1; + let mask = this.getRangeMask(fromIndex, length); + + return num ^ mask; + } + + private getRangeMask(fromIndex: number, length: number): int { + let mask: int = 0x0; + + if (length == BitVector.BIT_SET_LENGTH) { + mask = ~mask; + } else { + mask = ((1 << length) - 1) << fromIndex; + } + + return mask; + } + + private getBits(num: int, fromIndex: number, toIndex: number): int { + let length = toIndex - fromIndex + 1; + let mask: int = this.getRangeMask(fromIndex, length); + return (num & mask) >> fromIndex; + } + + private setBits(num: int, fromIndex: number, toIndex: number, element: number): int { + let length = toIndex - fromIndex + 1; + let mask: int = this.getRangeMask(fromIndex, length); + if (element != 0) { + return num | mask; + } else { + return num & ~mask; + } + } + + private findFirstBit(element: int, fromIndex: number, toIndex: number, target: number): number { + let length = toIndex - fromIndex + 1; + let mask: int = this.getRangeMask(fromIndex, length); + + if (target != 0) { + let valid: int = element & mask; + + if (valid == 0) { + return -1; + } + return Math.log2((valid & -valid)); + } else { + let inverted: int = ~element; + let valid: int = inverted & mask; + if (valid == 0) { + return -1; + } + + return Math.log2((valid & -valid)); + } + } + + private findLastBit(num: int, fromIndex: number, toIndex: number, target: number): number { + let length = toIndex - fromIndex + 1; + let mask: int = this.getRangeMask(fromIndex, length); + + let masked_value: int; + if (target !== 0) { + masked_value = num & mask; + } else { + masked_value = (~num) & mask; + } + + if (masked_value == 0) { + return -1; + } + + let pos = Math.floor(Math.log2(masked_value >> fromIndex)); + + return pos + fromIndex; + } + + private countBit1InRange(num: int, fromIndex: number, toIndex: number): number { + let length = toIndex - fromIndex + 1; + let mask: int = this.getRangeMask(fromIndex, length); + + let masked_value = num & mask; + + let count = 0; + while (masked_value != 0) { + masked_value = masked_value & (masked_value - 1); + count++; + } + + return count; + } + } +}