diff --git a/static_core/plugins/ets/sdk/api/@ohos.buffer.ets b/static_core/plugins/ets/sdk/api/@ohos.buffer.ets index c86e519c54afa9d61d01f6b8383b441b16dc61e1..c4a060d29ac346dabf8cc7e5ee9a27f43485f255 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.buffer.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.buffer.ets @@ -14,12 +14,13 @@ */ import { BusinessError } from "@ohos.base"; +import { jsonx } from "std/core" -const TypeErrorCodeId: number = 401; -const OutOfBoundsErrorCodeId: number = 10200001; -const IncorrectBufferSizeId: number = 10200009; +const TypeErrorCodeId: int = 401; +const OutOfBoundsErrorCodeId: int = 10200001; +const IncorrectBufferSizeId: int = 10200009; -function createBusinessError(code: number, message: string) { +function createBusinessError(code: int, message: string) { let err = new BusinessError(); err.code = code; err.name = 'BusinessError'; @@ -47,11 +48,79 @@ function sanitizeBase64(str: string, urlSafe: boolean = false): string { return str.replace(new RegExp(allowedPattern, "g"), ""); } +function getLength(value: buffer.Buffer | Uint8Array): number { + if (value instanceof Uint8Array) { + return value.length; + } + return value.buffer.byteLength; +} + +function stringify(buffer: ArrayBuffer, encoding: string, resolvedStart: int, resolvedEnd: int): string { + let val = ''; + if (encoding == "utf8" || encoding == "utf-8" || encoding == "ascii") { + return ArrayBuffer.stringify(buffer, encoding, resolvedStart, resolvedEnd); + } else if (encoding == "utf16le" || encoding == "utf-16le" || encoding == "ucs2" || encoding == "ucs-2") { + for (let i = resolvedStart; i + 1 < resolvedEnd; i += 2) { // 2 is array->NextIndex + const a: Int = (buffer.at(i) & 0xff); + const b: Int = (buffer.at(i + 1) & 0xff); + val += String.fromCharCode((b << 8) + a); + } + } else if (encoding === "binary" || encoding === "latin1") { + for (let i = resolvedStart; i < resolvedEnd; i++) { + val += String.fromCharCode(+(buffer.at(i) & 0xff)); + } + } else if (encoding == "base64" || encoding == "base64url") { + let base64 = ''; + let encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + if (encoding == "base64url") { + encodings = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" + } + for (let i = resolvedStart; i < resolvedEnd; i += 3) { + const byte1 = buffer.at(i) & 0xff; + const byte2 = i + 1 < resolvedEnd ? (buffer.at(i + 1) & 0xff) : NaN; + const byte3 = i + 2 < resolvedEnd ? (buffer.at(i + 2) & 0xff) : NaN; + + const enc1 = byte1 >> 2; + const enc2 = ((byte1 & 3) << 4) | (byte2 >> 4); + const enc3 = ((byte2 & 15) << 2) | (byte3 >> 6); + const enc4 = byte3 & 63; + + if (isNaN(byte2)) { + base64 += encodings.charAt(enc1) + encodings.charAt(enc2) + '=='; + } else if (isNaN(byte3)) { + base64 += encodings.charAt(enc1) + encodings.charAt(enc2) + encodings.charAt(enc3) + '='; + } else { + base64 += encodings.charAt(enc1) + encodings.charAt(enc2) + encodings.charAt(enc3) + encodings.charAt(enc4); + } + } + if (encoding == "base64url") { + let lastNonEqualPos = base64.length - 1; + while (lastNonEqualPos >= 0 && base64.charAt(lastNonEqualPos) == c'=') { + lastNonEqualPos--; + } + if (lastNonEqualPos >= 0) { + base64 = base64.substring(0, lastNonEqualPos + 1); + } + } + return base64; + } else if (encoding == "hex") { + for (let i = resolvedStart, len = resolvedEnd; i < len; i++) { + let tmpstr = Number(buffer.at(i) & 0xff).toString(16); // 16 : 16 decimal + tmpstr = (tmpstr.length === 1) ? `0${tmpstr}` : tmpstr; + val += tmpstr; + } + } + return val; +} + function fillInPaddingBase64(str: string): string { const base64Divisible: int = 4; const normalized: String = normalizeBase64Url(str); const clean: String = sanitizeBase64(normalized); - const remainder: int = Double.toInt(clean.length % base64Divisible); + const remainder: int = clean.length.toInt() % base64Divisible; + if (remainder === 1) { + return clean.slice(0, -1); + } const paddingCount: int = remainder != 0 ? base64Divisible - remainder : 0; return clean.padRight(c'=', clean.length.toInt() + paddingCount); } @@ -81,9 +150,9 @@ function sanitizeAscii(str: string): string { function sanitizeUtf8(str: string): string { const sb = new StringBuilder(); for (let i = 0; i < str.length; i++) { - const cu = str.charCodeAt(i) as char; + const cu = str.charCodeAt(i).toInt().toChar(); if (Char.isHighSurrogate(cu) && i + 1 < str.length) { - const cu2 = str.charCodeAt(i + 1) as char; + const cu2 = str.charCodeAt(i + 1).toInt().toChar(); if (Char.isLowSurrogate(cu2)) { sb.append(str.charAt(i)); sb.append(str.charAt(i + 1)); @@ -143,45 +212,50 @@ export default namespace buffer { | BigInt64Array | BigUint64Array; - const U32_MAX: long = 4294967295 as long; - /** * Allocates a new Buffer using an array of bytes in the range 0 – 255. * Array entries outside that range will be truncated to fit into it. * - * @param {number[]} array - An array of bytes in the range 0 – 255 + * @param {double[]} array - An array of bytes in the range 0 – 255 * @returns {Buffer} A new allocated Buffer containing the array data */ - export function from(array: number[]): Buffer { - return new Buffer(ArrayBuffer.from(array)) + export function fromWithArray(array: double[]): Buffer { + let arr: FixedArray = new Byte[array.length] + for (let i = 0; i < array.length; ++i) { + arr[i] = array[i].toByte() + } + return new Buffer(ArrayBuffer.from(arr)) } /** * Creates a view of the ArrayBuffer without copying the underlying memory. * * @param {ArrayBuffer} arrayBuffer - The source ArrayBuffer to create a view from - * @param {number} [byteOffset=0] - Index of first byte to expose - * @param {number} [length=arrayBuffer.byteLength - byteOffset] - Number of bytes to expose + * @param {int} [byteOffset=0] - Index of first byte to expose + * @param {int} [length=arrayBuffer.byteLength - byteOffset] - Number of bytes to expose * @returns {Buffer} A view of the ArrayBuffer * @throws {Error} If byteOffset or length are out of valid range */ - export function from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer { - const resolvedByteOffset: number = byteOffset ?? 0; - const resolvedLength: number = length ?? arrayBuffer.byteLength - resolvedByteOffset; - return new Buffer(ArrayBuffer.from(arrayBuffer, resolvedByteOffset, resolvedLength)); - } - - /** - * For the object whose value returned by valueof() function is strictly equal to object - * or supports symbol To primitive object, a new buffer instance is created. - * - * @param { Object } object - object object An object supporting Symbol.toPrimitive or valueOf() - * @param { number | string } offsetOrEncoding - offsetOrEncoding offsetOrEncoding A byte-offset or encoding - * @param { number } length - length length A length - * @returns { Buffer } Return a new allocated Buffer - */ - function from(object: Object, offsetOrEncoding: number | string, length: number): Buffer { - return new Buffer(ArrayBuffer.fromObject(object, offsetOrEncoding, length)) + export function fromWithArrayBufferByteOffsetLength(arrayBuffer: ArrayBuffer, byteOffset?: int, length?: int): Buffer { + const resolvedByteOffset: int = byteOffset ?? 0; + const resolvedLength: int = length ?? arrayBuffer.byteLength.toInt() - resolvedByteOffset; + if (byteOffset != undefined) { + if (byteOffset < 0 || byteOffset > arrayBuffer.byteLength) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "byteOffset" is out of range. `+ + `It must be >= 0 and <= ${arrayBuffer.byteLength}. Received value is: ${byteOffset}`) + } + } + if (length != undefined) { + if (length < 0 || length > arrayBuffer.byteLength) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "length" is out of range. `+ + `It must be >= 0 and <= ${arrayBuffer.byteLength}. Received value is: ${length}`) + } + } + if ((byteOffset != undefined) || (length != undefined)) { + return new Buffer(ArrayBuffer.from(arrayBuffer, resolvedByteOffset, resolvedLength)); + } + let buffer = new Buffer(arrayBuffer, resolvedByteOffset) + return buffer } /** @@ -190,30 +264,58 @@ export default namespace buffer { * @param {Buffer | Uint8Array} buffer - An existing Buffer or Uint8Array from which to copy data * @returns {Buffer} A new Buffer containing a copy of the provided buffer's data */ - export function from(buff: Buffer | Uint8Array): Buffer { + export function fromWithBuffer(buff: Buffer | Uint8Array): Buffer { if (buff instanceof Buffer) { const arrBuff: ArrayBuffer = (buff as Buffer).buffer; return new Buffer(ArrayBuffer.from(arrBuff)); } - return new Buffer(ArrayBuffer.from(buff as Uint8Array)); + const u8arr = buff as Uint8Array; + return new Buffer(u8arr.buffer as ArrayBuffer); + } + + /** + * For the object whose value returned by valueof() function is strictly equal to object + * or supports symbol To primitive object, a new buffer instance is created. + * + * @param { Object } input - object object An object supporting Symbol.toPrimitive or valueOf() + * @param { int | string } offsetOrEncoding - offsetOrEncoding offsetOrEncoding A byte-offset or encoding + * @param { int } length - length length A length + * @returns { Buffer } Return a new allocated Buffer + */ + export function fromWithObjectTypedInputOffsetOrEncodingLength(input: Object, offsetOrEncoding: int | string, length: int): Buffer { + if (offsetOrEncoding instanceof string && (!isEncoding(offsetOrEncoding))) { + throw createBusinessError(TypeErrorCodeId, `Parameter error. The type of "encoding" must be BufferEncoding. ` + + `the encoding ${offsetOrEncoding} is unknown`); + } + if (input instanceof string || input instanceof ArrayBuffer) { + const resolvedByteOffset: int = (offsetOrEncoding instanceof string) ? 0 : offsetOrEncoding; + return new Buffer(ArrayBuffer.fromObject(input, offsetOrEncoding, length), resolvedByteOffset); + } + if (!(input instanceof Uint8Array)) { + throw createBusinessError(TypeErrorCodeId, 'Parameter error. The type of "input" must be Buffer or ArrayBuffer, Array, Array-like'); + } + if (offsetOrEncoding instanceof string) { + const resolvedByteOffset: int = 0; + return new Buffer(ArrayBuffer.fromObject(input.buffer, offsetOrEncoding, length), resolvedByteOffset); + } + return new Buffer(ArrayBuffer.from(input as Uint8Array)); } /** * Creates a new Buffer containing the provided string encoded using the specified encoding. * - * @param {String} string - The string to encode into the buffer + * @param {string} input - The string to encode into the buffer * @param {BufferEncoding} [encoding='utf8'] - The character encoding to use * @returns {Buffer} A new Buffer containing the encoded string */ - export function from(string: String, encoding?: BufferEncoding): Buffer { + export function fromWithStringTypedInputEncoding(input: string, encoding?: BufferEncoding): Buffer { const resolvedEncoding: string = (encoding ?? "utf8"); - let resolvedString = string; + let resolvedString = input; if (resolvedEncoding == 'ascii') { resolvedString = sanitizeAscii(resolvedString); } else if (resolvedEncoding == 'utf8' || resolvedEncoding == 'utf-8') { resolvedString = sanitizeUtf8(resolvedString); - } - else if (resolvedEncoding == 'latin1' || resolvedEncoding == 'binary') { + } else if (resolvedEncoding == 'latin1' || resolvedEncoding == 'binary') { resolvedString = sanitizeLatin1(resolvedString); } else if (resolvedEncoding == 'hex') { resolvedString = takeValidHexPrefix(resolvedString); @@ -221,26 +323,39 @@ export default namespace buffer { return new Buffer(new ArrayBuffer(0)); } } else if (encoding == 'base64' || encoding == 'base64url') { - resolvedString = fillInPaddingBase64(string); + resolvedString = fillInPaddingBase64(input); + } else if (resolvedEncoding == 'utf16le' || resolvedEncoding == 'ucs2' || resolvedEncoding == 'ucs-2') { + return new Buffer(ArrayBuffer.from(resolvedString, resolvedEncoding)); + } else { + throw createBusinessError(TypeErrorCodeId, 'Parameter error. The type of "encoding" must be BufferEncoding. the encoding test is unknown'); } return new Buffer(ArrayBuffer.from(resolvedString, resolvedEncoding)); } + /** + * Creates a Buffer instance based on a string in the given encoding format. + * + * @overload { fromWithArray, fromWithArrayBufferByteOffsetLength, fromWithBuffer, + fromWithObjectTypedInputOffsetOrEncodingLength, fromWithStringTypedInputEncoding } + */ + export overload from { fromWithArray, fromWithArrayBufferByteOffsetLength, fromWithBuffer, + fromWithObjectTypedInputOffsetOrEncodingLength, fromWithStringTypedInputEncoding } + /** * Returns the byte length of a string when encoded using `encoding`. * This is not the same as String.prototype.length, which does not account * for the encoding that is used to convert the string into bytes. * - * @param {string | Buffer | TypedArray | DataView | ArrayBuffer} string - A value to calculate the length of + * @param {string | Buffer | TypedArray | DataView | ArrayBuffer} doc - A value to calculate the length of * @param {BufferEncoding} [encoding='utf8'] - If `string` is a string, this is its encoding - * @returns {number} The number of bytes contained within `string` + * @returns {int} The number of bytes contained within `string` * @throws {BusinessError} 401 - Parameter error. Possible causes: * 1. Mandatory parameters are left unspecified; */ export function byteLength( doc: string | Buffer | TypedArray | DataView | ArrayBuffer, encoding?: BufferEncoding - ): number { + ): int { if (doc instanceof string) { let resolvedEncoding: string = encoding ?? "utf8"; return ArrayBuffer.bytesLength(doc, resolvedEncoding); @@ -254,29 +369,29 @@ export default namespace buffer { case "Buffer": return (doc as Buffer).length; case "Int8Array": - return (doc as Int8Array).byteLength; + return (doc as Int8Array).byteLength.toInt(); case "Uint8Array": - return (doc as Uint8Array).byteLength; + return (doc as Uint8Array).byteLength.toInt(); case "Uint8ClampedArray": - return (doc as Uint8ClampedArray).byteLength; + return (doc as Uint8ClampedArray).byteLength.toInt(); case "Int16Array": - return (doc as Int16Array).byteLength; + return (doc as Int16Array).byteLength.toInt(); case "Uint16Array": - return (doc as Uint16Array).byteLength; + return (doc as Uint16Array).byteLength.toInt(); case "Int32Array": - return (doc as Int32Array).byteLength; + return (doc as Int32Array).byteLength.toInt(); case "Uint32Array": - return (doc as Uint32Array).byteLength; + return (doc as Uint32Array).byteLength.toInt(); case "Float32Array": - return (doc as Float32Array).byteLength; + return (doc as Float32Array).byteLength.toInt(); case "Float64Array": - return (doc as Float64Array).byteLength; + return (doc as Float64Array).byteLength.toInt(); case "BigInt64Array": - return (doc as BigInt64Array).byteLength; + return (doc as BigInt64Array).byteLength.toInt(); case "BigUint64Array": - return (doc as BigUint64Array).byteLength; + return (doc as BigUint64Array).byteLength.toInt(); case "DataView": - return (doc as DataView).byteLength; + return (doc as DataView).byteLength.toInt(); case "ArrayBuffer": return (doc as ArrayBuffer).getByteLength(); } @@ -286,8 +401,8 @@ export default namespace buffer { /** * Allocates a new Buffer for a fixed size bytes. If fill is undefined, the Buffer will be zero-filled. * - * @param { number } size - The desired length of the new Buffer - * @param { string | Buffer | number } [fill] - A value to pre-fill the new Buffer with + * @param { int } size - The desired length of the new Buffer + * @param { string | Buffer | Uint8Array | int | double | long } [fill] - A value to pre-fill the new Buffer with * @param { BufferEncoding } [encoding] - If `fill` is a string, this is its encoding * @returns { Buffer } Return a new allocated Buffer * @throws { BusinessError } 401 - Parameter error. Possible causes: @@ -295,20 +410,21 @@ export default namespace buffer { * 2. Parameter verification failed. */ export function alloc( - size: number, - fill?: string | Buffer | number, + size: int, + fill?: string | Buffer | Uint8Array | int | double | long, encoding?: BufferEncoding ): Buffer { if (size < 0) { - throw createBusinessError(TypeErrorCodeId, 'Parameter error: size must be a non-negative number.') + throw createBusinessError(TypeErrorCodeId, `Parameter error. The type of "size" must be number and ` + + `the value cannot be negative. Received value is: ${size}`); } const buffer = new Buffer(new ArrayBuffer(size)); const resolvedEncoding = encoding ?? "utf-8"; if (fill != undefined) { - buffer.fill(fill, 0, size, resolvedEncoding); + buffer.fill(fill!, 0, size, resolvedEncoding); } else { - buffer.fill(0, 0); + buffer.fill(0 as long, 0); } return buffer; } @@ -316,15 +432,16 @@ export default namespace buffer { /** * Allocates a new Buffer for a fixed size bytes. The Buffer will not be initially filled. * - * @param { number } size - The desired length of the new Buffer + * @param { int } size - The desired length of the new Buffer * @returns { Buffer } Return a new allocated Buffer * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1. Mandatory parameters are left unspecified; * 2. Parameter verification failed. */ - export function allocUninitializedFromPool(size: number): Buffer { + export function allocUninitializedFromPool(size: int): Buffer { if (size < 0) { - throw createBusinessError(TypeErrorCodeId, 'Parameter error: size must be a non-negative number.') + throw createBusinessError(TypeErrorCodeId, `Parameter error. The type of "size" must be number and ` + + `the value cannot be negative. Received value is: ${size}`); } return new Buffer(new ArrayBuffer(size)); } @@ -332,15 +449,16 @@ export default namespace buffer { /** * Allocates a new un-pooled Buffer for a fixed size bytes. The Buffer will not be initially filled. * - * @param { number } size - The desired length of the new Buffer + * @param { int } size - The desired length of the new Buffer * @returns { Buffer } Return a new allocated Buffer * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1. Mandatory parameters are left unspecified; * 2. Parameter verification failed. */ - export function allocUninitialized(size: number): Buffer { + export function allocUninitialized(size: int): Buffer { if (size < 0) { - throw createBusinessError(TypeErrorCodeId, 'Parameter error: size must be a non-negative number.') + throw createBusinessError(TypeErrorCodeId, `Parameter error. The type of "size" must be number and ` + + `the value cannot be negative. Received value is: ${size}`); } return new Buffer(new ArrayBuffer(size)); } @@ -350,23 +468,27 @@ export default namespace buffer { * * @param { Buffer | Uint8Array } buf1 - A Buffer or Uint8Array instance. * @param { Buffer | Uint8Array } buf2 - A Buffer or Uint8Array instance. - * @returns { number } 0 is returned if target is the same as buf + * @returns { int } 0 is returned if target is the same as buf * 1 is returned if target should come before buf when sorted. * -1 is returned if target should come after buf when sorted. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1. Mandatory parameters are left unspecified; * 2. Parameter verification failed. */ - export function compare(buf1: Buffer | Uint8Array, buf2: Buffer | Uint8Array): number { + export function compare(buf1: Buffer | Uint8Array, buf2: Buffer | Uint8Array): int { const resolvedBuf1 = getArrayBufferFrom(buf1); const resolvedBuf2 = getArrayBufferFrom(buf2); - const len1 = resolvedBuf1.byteLength; - const len2 = resolvedBuf2.byteLength; + const len1 = getLength(buf1); + const len2 = getLength(buf2); const minLength = Math.min(len1, len2); + let i1 = (buf1 instanceof Buffer) ? (buf1 as Buffer).byteOffset : (buf1 as Uint8Array).byteOffset.toInt() + let i2 = (buf2 instanceof Buffer) ? (buf2 as Buffer).byteOffset : (buf2 as Uint8Array).byteOffset.toInt() for (let i = 0; i < minLength; i++) { - if (resolvedBuf1.at(i) != resolvedBuf2.at(i)) { - return resolvedBuf1.at(i) < resolvedBuf2.at(i) ? -1 : 1; + if (resolvedBuf1.at(i1) != resolvedBuf2.at(i2)) { + return resolvedBuf1.at(i1) < resolvedBuf2.at(i2) ? -1 : 1; } + i1++; + i2++; } return len1 == len2 ? 0 : (len1 < len2 ? -1 : 1); } @@ -375,23 +497,23 @@ export default namespace buffer { * Returns a new `Buffer` which is the result of concatenating all the `Buffer`instances in the `list` together. * * @param { Buffer[] | Uint8Array[] } list - List of `Buffer` or Uint8Array instances to concatenate - * @param { number } [totalLength] - Total length of the `Buffer` instances in `list` when concatenated + * @param { int } [totalLength] - Total length of the `Buffer` instances in `list` when concatenated * @returns { Buffer } Return a new allocated Buffer * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1. Mandatory parameters are left unspecified; * 2. Parameter verification failed. - * @throws { BusinessError } 10200001 - The value of "length" is out of range. It must be >= 0 and <= uint32 max. Received value is: [length] + * @throws { BusinessError } 10200001 - The value of "length" is out of range. It must be >= 0 and <= 4294967296. Received value is: [length] */ - export function concat(list: Buffer[] | Uint8Array[], totalLength?: number): Buffer { + export function concat(list: Buffer[] | Uint8Array[], totalLength?: int): Buffer { const resolvedList = new Array(); for (const item of list) { resolvedList.push(getArrayBufferFrom(item)); } - const length = totalLength ?? resolvedList.reduce( - (acc: number, item: ArrayBuffer): number => acc + item.byteLength, 0); - if (length < 0 || length > U32_MAX) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "length" is out of range. -It must be >= 0 and <= uint32 max. Received value is: ${length}`) + const length: int = totalLength ?? resolvedList.reduce( + (acc: number, item: ArrayBuffer): number => acc + item.byteLength.toInt(), 0).toInt() + if (length < 0) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "totalLength" is out of range. ` + + `It must be >= 0 and <= 2147483647. Received value is: ${length}`) } const listOfBytes = new Array(); for (const item of resolvedList) { @@ -455,31 +577,57 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) * 2. Parameter verification failed. */ export function transcode(source: Buffer | Uint8Array, fromEnc: string, toEnc: string): Buffer { - const resolvedSource = getArrayBufferFrom(source); - const sourceBytesLength = resolvedSource.byteLength.toInt(); - const str = ArrayBuffer.stringify(resolvedSource, toEnc, 0, sourceBytesLength); + if (!isEncoding(fromEnc)) { + throw createBusinessError(TypeErrorCodeId, `Parameter error. The type of "fromEnc" must be BufferEncoding. ` + + `the fromEnc ${fromEnc} is unknown`); + } + if (!isEncoding(toEnc)) { + throw createBusinessError(TypeErrorCodeId, `Parameter error. The type of "toEnc" must be BufferEncoding. ` + + `the toEnc ${toEnc} is unknown`); + } + let resolvedSource = getArrayBufferFrom(source); + let sourceBytesLength = resolvedSource.byteLength.toInt(); + let str = ''; + if (source instanceof Uint8Array) { + str = source.toString(); + } else if (source instanceof Buffer) { + str = stringify(resolvedSource, fromEnc, 0, sourceBytesLength); + } + if (toEnc == 'ascii') { + let newStr = ""; + for (let i = 0; i < str.length; ++i) { + if (str.charAt(i).toInt() <= 128) { + newStr += str.charAt(i); + } else { + newStr += '?'; + } + } + str = newStr; + } else if (toEnc == 'base64') { + str = str.replace("/[\r\n]/g", ''); + } const newBytes = ArrayBuffer.from(str, toEnc); return new Buffer(newBytes); } - class BufferIteratorKeys implements IterableIterator { + class BufferIteratorKeys implements IterableIterator { private length: int private idx: int = 0 constructor(parent: Buffer) { this.length = parent.length.toInt() } - public override $_iterator(): IterableIterator { + public override $_iterator(): IterableIterator { return this } - override next(): IteratorResult { + override next(): IteratorResult { if (this.idx < 0 || this.idx >= this.length) { - return new IteratorResult() + return new IteratorResult() } - return new IteratorResult(false, (this.idx++).toDouble()) + return new IteratorResult(false, (this.idx++)) } } - class BufferIteratorValues implements IterableIterator { + class BufferIteratorValues implements IterableIterator { private length: int private parent: Buffer private idx: int = 0 @@ -487,18 +635,18 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) this.length = parent.length.toInt() this.parent = parent } - public override $_iterator(): IterableIterator { + public override $_iterator(): IterableIterator { return this } - override next(): IteratorResult { + override next(): IteratorResult { if (this.idx < 0 || this.idx >= this.length) { - return new IteratorResult() + return new IteratorResult() } - return new IteratorResult(false, this.parent.at(this.idx++)) + return new IteratorResult(false, this.parent.at(this.idx++)) } } - class BufferEntriesIterator implements IterableIterator<[number, number]> { + class BufferEntriesIterator implements IterableIterator<[int, long]> { private length: int private parent: Buffer private idx: int = 0 @@ -506,14 +654,14 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) this.length = parent.length.toInt() this.parent = parent } - public override $_iterator(): IterableIterator<[number, number]> { + public override $_iterator(): IterableIterator<[int, long]> { return this } - override next(): IteratorResult<[number, number]> { + override next(): IteratorResult<[int, long]> { if (this.idx < 0 || this.idx >= this.length) { - return new IteratorResult<[number, number]>() + return new IteratorResult<[int, long]>() } - return new IteratorResult<[number, number]>(false, [this.idx, this.parent.at(this.idx++)]) + return new IteratorResult<[int, long]>(false, [this.idx, this.parent.at(this.idx++)]) } } @@ -521,28 +669,36 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) * A class representing a fixed-length sequence of bytes. * Provides methods for reading and manipulating binary data with various encodings. */ - export class Buffer { + export class Buffer implements JsonElementDeserializable { /** The underlying ArrayBuffer storing the binary data */ - public buffer: ArrayBuffer; + private bufferData: ArrayBuffer; /** The offset into the buffer where this Buffer instance starts */ - internal readonly byteOffsetNumber: number; + private readonly byteOffsetNumber: int; /** * Gets the length of the buffer in bytes - * @returns {number} The number of bytes in the buffer + * @returns {int} The number of bytes in the buffer */ - get length(): number { return this.buffer.getByteLength() } + get length(): int { return this.buffer.getByteLength() } - get byteOffset(): number { return this.byteOffsetNumber } + get byteOffset(): int { return this.byteOffsetNumber } + + get buffer(): ArrayBuffer { return this.bufferData } /** * Creates a new Buffer instance * @param {ArrayBuffer} buffer - The underlying ArrayBuffer to use - * @param {number} [byteOffset=0] - The starting offset into the ArrayBuffer + * @param {int} [byteOffset=0] - The starting offset into the ArrayBuffer */ - public constructor(buffer: ArrayBuffer, byteOffset: number = 0) + public constructor(buffer: ArrayBuffer, byteOffset: int = 0) { - this.buffer = buffer; - this.byteOffsetNumber = 0; + this.bufferData = buffer; + this.byteOffsetNumber = byteOffset; + if (byteOffset >= this.buffer.getByteLength() && this.buffer.getByteLength() > 0) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "byteOffset" is out of range. Received value is: ${byteOffset}`) + } + if (byteOffset < 0) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "byteOffset" is out of range. It must be >= 0. Received value is: ${byteOffset}`) + } } /** @@ -561,68 +717,68 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) * Returns a string decoded from the buffer's contents. * * @param {BufferEncoding} [encoding='utf8'] - Character encoding to use for decoding - * @param {number} [start=0] - Where to start decoding - * @param {number} [end=buffer.length] - Where to stop decoding + * @param {int} [start=0] - Where to start decoding + * @param {int} [end=buffer.length] - Where to stop decoding * @returns {string} The decoded string */ - public toString(encoding?: BufferEncoding, start?: number, end?: number): string { + public toString(encoding?: BufferEncoding, start?: double, end?: double): string { let resolvedEncoding: string = encoding ?? "utf8"; let resolvedStart: int = 0; let resolvedEnd: int = (this.length).toInt(); - if (start && !isNaN(start!) && start! > 0) { - resolvedStart = start.toInt(); + if (start !== undefined && !isNaN(start!) && start! > 0) { + resolvedStart = start!.toInt(); } - if (end && !isNaN(end!)) { - resolvedEnd = end.toInt(); + if (end !== undefined && !isNaN(end!)) { + resolvedEnd = end!.toInt(); } let bufLength = this.length; if (resolvedStart >= bufLength || resolvedStart > resolvedEnd) { return ''; } - resolvedEnd = resolvedEnd > bufLength ? bufLength.toInt() : resolvedEnd - return ArrayBuffer.stringify(this.buffer, resolvedEncoding, resolvedStart, resolvedEnd); + resolvedEnd = resolvedEnd > bufLength ? bufLength.toInt() : resolvedEnd; + return stringify(this.buffer, resolvedEncoding, resolvedStart, resolvedEnd); } /** * Returns the byte at the specified index * * @param {int} index - Index of the byte to return - * @returns {byte} The byte at the specified position + * @returns {long} The byte at the specified position */ - public at(index: int): byte { + public at(index: int): long { return this.buffer.at(index); } /** * Checks if the buffer includes the given value. * - * @param {string | number | Buffer | Uint8Array} value - The value to search for - * @param {number} [byteOffset=0] - The byte position to start searching from + * @param {string | int | double | long | Buffer | Uint8Array} value - The value to search for + * @param {int} [byteOffset=0] - The byte position to start searching from * @param {BufferEncoding} [encoding='utf8'] - Encoding to use if `value` is a string * @returns {boolean} `true` if the value is found, otherwise `false` */ - public includes(value: string | number | Buffer | Uint8Array, byteOffset?: number, encoding?: BufferEncoding): boolean { + public includes(value: string | int | double | long | Buffer | Uint8Array, byteOffset?: int, encoding?: BufferEncoding): boolean { return this.indexOf(value, byteOffset ?? 0, encoding ?? "utf8") != -1; } /** * Returns the first index where `value` is found in the buffer. * - * @param {string | number | Buffer | Uint8Array} value - The value to search for - * @param {number} [byteOffset=0] - The byte position to start searching from. + * @param {string | int | double | long | Buffer | Uint8Array} value - The value to search for + * @param {int} [byteOffset=0] - The byte position to start searching from. * - If negative, it is counted from the end of the buffer (`len + byteOffset`). * - If out of bounds (`>= buffer length`), returns `-1`. * @param {BufferEncoding} [encoding='utf8'] - Encoding to use if `value` is a string - * @returns {number} The index of the first occurrence of `value`, or -1 if not found + * @returns {int} The index of the first occurrence of `value`, or -1 if not found */ - public indexOf(value: string | number | Buffer | Uint8Array, byteOffset?: number, encoding?: BufferEncoding): number { + public indexOf(value: string | int | double | long | Buffer | Uint8Array, byteOffset?: int, encoding?: BufferEncoding): int { const searchBuffer = this.normalizeValueToBuffer(value, encoding ?? "utf8"); - const len = this.length; + const len: int = this.length; const searchLen = searchBuffer.length; - let startIndex = byteOffset ?? 0; + let startIndex: int = byteOffset ?? 0; if (startIndex < 0) { - startIndex = Math.max(0, len + startIndex); + startIndex = Math.max(0, len + startIndex).toInt() } else if (startIndex >= len) { return -1; } @@ -635,14 +791,14 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) /** * Returns the last index where `value` is found in the buffer. * - * @param {string | number | Buffer | Uint8Array} value - The value to search for - * @param {number} [byteOffset=buffer length] - The byte position to start searching from (backwards). + * @param {string | int | double | long | Buffer | Uint8Array} value - The value to search for + * @param {int} [byteOffset=buffer length] - The byte position to start searching from (backwards). * - If negative, it is counted from the end of the buffer (`len + byteOffset`). * - If out of bounds (`>= buffer length`), it is clamped to `buffer length - searchLen`. * @param {BufferEncoding} [encoding='utf8'] - Encoding to use if `value` is a string - * @returns {number} The index of the last occurrence of `value`, or -1 if not found + * @returns {int} The index of the last occurrence of `value`, or -1 if not found */ - public lastIndexOf(value: string | number | Buffer | Uint8Array, byteOffset?: number , encoding?: BufferEncoding): number { + public lastIndexOf(value: string | int | double | long | Buffer | Uint8Array, byteOffset?: int , encoding?: BufferEncoding): int { const searchBuffer = this.normalizeValueToBuffer(value, encoding ?? "utf8"); const searchLen = searchBuffer.length; const len = this.length; @@ -650,9 +806,9 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) if (searchLen == 0 || len < searchLen) { return -1; } - let endIndex = byteOffset ?? len; + let endIndex: int = byteOffset ?? len; if (endIndex < 0) { - endIndex = Math.max(0, len + endIndex); + endIndex = Math.max(0, len + endIndex).toInt() } else if (endIndex >= len) { endIndex = len - searchLen; } @@ -664,11 +820,17 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) return -1; } - private normalizeValueToBuffer(value: string | number | Buffer | Uint8Array, encoding: BufferEncoding): Buffer { + private normalizeValueToBuffer(value: string | int | double | long | Buffer | Uint8Array, encoding: BufferEncoding): Buffer { if (value instanceof string) { return buffer.from(value, encoding ?? "utf8"); - } else if (value instanceof Number) { - const arr: number[] = [value]; + } else if (value instanceof Double) { + const arr: double[] = [value]; + return buffer.from(arr); + } else if (value instanceof int) { + const arr: double[] = [value.toDouble()]; + return buffer.from(arr); + } else if (value instanceof Long) { + const arr: double[] = [value.toDouble()]; return buffer.from(arr); } else if (value instanceof Buffer) { return value; @@ -677,7 +839,7 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) } } - private compareSubarray(startIndex: number, searchBuffer: Buffer): boolean { + private compareSubarray(startIndex: int, searchBuffer: Buffer): boolean { if (startIndex + searchBuffer.length > this.length){ return false; } @@ -694,11 +856,11 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) * or is the same as target in sort order. Comparison is based on the actual sequence of bytes in each Buffer. * * @param { Buffer | Uint8Array } target - A Buffer or Uint8Array with which to compare buf - * @param { number } [targetStart] - The offset within target at which to begin comparison - * @param { number } [targetEnd] - The offset within target at which to end comparison (not inclusive) - * @param { number } [sourceStart] - The offset within buf at which to begin comparison - * @param { number } [sourceEnd] - The offset within buf at which to end comparison (not inclusive) - * @returns { number } 0 is returned if target is the same as buf + * @param { int } [targetStart] - The offset within target at which to begin comparison + * @param { int } [targetEnd] - The offset within target at which to end comparison (not inclusive) + * @param { int } [sourceStart] - The offset within buf at which to begin comparison + * @param { int } [sourceEnd] - The offset within buf at which to end comparison (not inclusive) + * @returns { int } 0 is returned if target is the same as buf * 1 is returned if target should come before buf when sorted. * -1 is returned if target should come after buf when sorted. * @throws { BusinessError } 401 - Parameter error. Possible causes: @@ -709,15 +871,36 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) */ public compare( target: Buffer | Uint8Array, - targetStart?: number, - targetEnd?: number, - sourceStart?: number, - sourceEnd?: number): number + targetStart?: int, + targetEnd?: int, + sourceStart?: int, + sourceEnd?: int): int { const resolvedSource: ArrayBuffer = this.buffer; const resolvedTarget: ArrayBuffer = getArrayBufferFrom(target); - const targetSlice = resolvedTarget.slice(targetStart ?? 0, targetEnd ?? resolvedTarget.byteLength); - const sourceSlice = resolvedSource.slice(sourceStart ?? 0, sourceEnd ?? resolvedSource.byteLength); + + const resolvedTargetStart = targetStart ?? 0; + const resolveTargetEnd = targetEnd ?? resolvedTarget.byteLength.toInt(); + const resolvedSourceStart = sourceStart ?? 0; + const resolvedSourceEnd = sourceEnd ?? resolvedSource.byteLength.toInt(); + if (resolvedTargetStart < 0) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "targetStart" is out of range. ` + + `It must be >= 0 and <= 2147483647. Received value is: ${targetStart}`); + } + if (resolveTargetEnd < 0 || resolveTargetEnd > resolvedTarget.byteLength.toInt()) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "targetEnd" is out of range. ` + + `It must be >= 0 and <= ${resolvedTarget.byteLength.toInt()}. Received value is: ${targetEnd}`); + } + if (resolvedSourceStart < 0) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "sourceEnd" is out of range. ` + + `It must be >= 0 and <= 2147483647. Received value is: ${sourceStart}`); + } + if (resolvedSourceEnd < 0 || resolvedSourceEnd > this.length) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "sourceEnd" is out of range. ` + + `It must be >= 0 and <= ${this.length}. Received value is: ${sourceEnd}`); + } + const targetSlice = resolvedTarget.slice(resolvedTargetStart, resolveTargetEnd); + const sourceSlice = resolvedSource.slice(resolvedSourceStart, resolvedSourceEnd); return compare(new Buffer(sourceSlice), new Buffer(targetSlice)); } @@ -726,10 +909,10 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) * If sourceEnd is greater than the length of the target, the length of the target shall prevail, and the extra part will not be overwritten. * * @param { Buffer | Uint8Array } target - A Buffer or Uint8Array to copy into - * @param { number } [targetStart] - The offset within target at which to begin writing - * @param { number } [sourceStart] - The offset within buf from which to begin copying - * @param { number } [sourceEnd] - The offset within buf at which to stop copying (not inclusive) - * @returns { number } The number of bytes copied + * @param { int } [targetStart] - The offset within target at which to begin writing + * @param { int } [sourceStart] - The offset within buf from which to begin copying + * @param { int } [sourceEnd] - The offset within buf at which to stop copying (not inclusive) + * @returns { int } The number of bytes copied * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1. Mandatory parameters are left unspecified; * 2. Parameter verification failed. @@ -738,43 +921,51 @@ It must be >= 0 and <= uint32 max. Received value is: ${length}`) */ public copy( target: Buffer | Uint8Array, - targetStart?: number, - sourceStart?: number, - sourceEnd?: number): number + targetStart?: int, + sourceStart?: int, + sourceEnd?: int): int { - const resolvedTarget = getArrayBufferFrom(target); - const resolvedSource = this.buffer; + const resolvedTarget = target.buffer; + const resolvedSource: ArrayBuffer = this.buffer; const resolvedTargetStart = (targetStart ?? 0).toInt(); const resolvedSourceStart = (sourceStart ?? 0).toInt(); const resolvedSourceEnd = (sourceEnd ?? this.length).toInt(); if (resolvedTargetStart < 0) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "targetStart" is out of range. It must be >= 0. -Received value is: ${resolvedTargetStart}`) + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "targetStart" is out of range. ` + + `It must be >= 0. Received value is: ${targetStart}`); } if (resolvedSourceStart < 0) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "sourceStart" is out of range. It must be >= 0. -Received value is: ${resolvedSourceStart}`) + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "sourceStart" is out of range. ` + + `It must be >= 0. Received value is: ${sourceStart}`); } if (resolvedSourceEnd < 0) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "sourceEnd" is out of range. It must be >= 0. -Received value is: ${resolvedSourceEnd}`) + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "sourceEnd" is out of range. ` + + `It must be >= 0. Received value is: ${sourceEnd}`); } - - const sourceSlice = resolvedSource.slice(resolvedSourceStart, - resolvedSourceEnd); - for (let i = 0; i < sourceSlice.byteLength; i++) { - resolvedTarget.set(resolvedTargetStart + i, sourceSlice.at(i)); + if (resolvedTargetStart >= resolvedTarget.byteLength.toInt()) { + return 0; + } + if (resolvedSourceEnd <= resolvedSourceStart || resolvedSourceStart >= this.length) { + return 0; + } + const sourceSlice: ArrayBuffer = resolvedSource.slice(resolvedSourceStart, + resolvedSourceEnd); + let copyLen = 0; + for (; copyLen < sourceSlice.byteLength + && resolvedTargetStart + copyLen < resolvedTarget.byteLength; + copyLen++) { + resolvedTarget.set(resolvedTargetStart + copyLen, sourceSlice.at(copyLen)); } - return sourceSlice.byteLength; + return copyLen; } /** * Creates and returns an iterator of [index, byte] pairs from the contents of buf. * - * @returns { IterableIterator<[number, number]> } + * @returns { IterableIterator<[int, long]> } */ - public entries(): IterableIterator<[number, number]> { + public entries(): IterableIterator<[int, long]> { return new BufferEntriesIterator(this); } @@ -794,9 +985,9 @@ Received value is: ${resolvedSourceEnd}`) /** * Fills buf with the specified value. If the offset and end are not given, the entire buf will be filled. * - * @param { string | Buffer | Uint8Array | number } value - The value with which to fill buf - * @param { number } [offset] - Number of bytes to skip before starting to fill buf - * @param { number } [end] - Where to stop filling buf (not inclusive) + * @param { string | Buffer | Uint8Array | int | double | long } value - The value with which to fill buf + * @param { int } [offset] - Number of bytes to skip before starting to fill buf + * @param { int } [end] - Where to stop filling buf (not inclusive) * @param { BufferEncoding } [encoding] - The encoding for value if value is a string * @returns { Buffer } A reference to buf * @throws { BusinessError } 10200001 - The value of "[offset/end]" is out of range. It must be >= 0 and <= [right range]. Received value is: [offset/end] @@ -805,27 +996,34 @@ Received value is: ${resolvedSourceEnd}`) * 2. Parameter verification failed. */ public fill( - value: string | Buffer | Uint8Array | number, - offset: number = 0, - end?: number, + value: string | Buffer | Uint8Array | int | double | long, + offset: int = 0, + end?: int, encoding?: BufferEncoding ): Buffer { const resolvedEnd = end ?? this.length; if (offset < 0) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. It must be >= 0. -Received value is: ${offset}`) + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= 2147483647. Received value is: ${offset}`) } if (resolvedEnd < 0 || resolvedEnd > this.length) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "end" is out of range. It must be >= 0 and <= ${this.length}. -Received value is: ${resolvedEnd}`) + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "end" is out of range. ` + + `It must be >= 0 and <= ${this.length}. Received value is: ${resolvedEnd}`) } let klass: string = "string"; if (value instanceof string) { + if (value as string == "") { + return this.fill(0 as long, offset, end, encoding); + } klass = "string"; - } else if (value instanceof Number) { - klass = "number"; + } else if (value instanceof int) { + klass = "int"; + } else if (value instanceof long) { + klass = "long"; + } else if (value instanceof double) { + klass = "double"; } else { // NOTE (templin.konstantin): Can't completely use smart casts due internal issue #21021 // now is used temporarily solution with reflection @@ -844,6 +1042,9 @@ Received value is: ${resolvedEnd}`) if (resolvedEncoding == 'ascii') { strValue = sanitizeAscii(strValue); } else if (resolvedEncoding == 'utf8' || resolvedEncoding == 'utf-8') { + if (strValue.length == 0) { + return this; + } strValue = sanitizeUtf8(strValue); } else if (resolvedEncoding == 'latin1' || resolvedEncoding == 'binary') { strValue = sanitizeLatin1(strValue); @@ -855,21 +1056,51 @@ Received value is: ${resolvedEnd}`) } else if (resolvedEncoding == 'base64' || resolvedEncoding == 'base64url') { strValue = fillInPaddingBase64(strValue); } - let buff = ArrayBuffer.from(strValue, resolvedEncoding as string); + let buff: Uint8Array; + + if (resolvedEncoding === 'ucs2' || resolvedEncoding === 'ucs-2' || resolvedEncoding === 'utf16le') { + const u16 = new Uint16Array(strValue.length); + for (let i = 0; i < strValue.length; i++) { + u16[i] = strValue.charCodeAt(i); + } + buff = new Uint8Array(u16.buffer); + } else { + buff = new Uint8Array(ArrayBuffer.from(strValue, resolvedEncoding as string)); + } + + let pos = offsetInt; + while (buff.byteLength > 0 && pos + buff.byteLength <= resolvedEndInt) { + for (let j: int = 0; j < buff.byteLength; j++) { + this.buffer.set(pos++, buff[j].toByte()); + } + } + + for (let j: int = 0; pos < resolvedEndInt && buff.byteLength > 0; j++, pos++) { + this.buffer.set(pos, buff[j].toByte()); + } + return this; + case "int": + let asInt: int = value as int; for (let i: int = offsetInt; i < resolvedEndInt; i++) { - this.buffer.set(i, buff.at((i % buff.byteLength).toInt()).toByte()); + this.buffer.set(i, asInt.toByte()); } return this; - case "number": - let asNum = value as number; + case "long": + let asLong: long = value as long; for (let i: int = offsetInt; i < resolvedEndInt; i++) { - this.buffer.set(i, asNum.toByte()); + this.buffer.set(i, asLong.toByte()); + } + return this; + case "double": + let asDouble: double = value as double; + for (let i: int = offsetInt; i < resolvedEndInt; i++) { + this.buffer.set(i, asDouble.toByte()); } return this; case "Buffer": let asBuf = value as Buffer; for (let i: int = offsetInt; i < resolvedEndInt; i++) { - this.buffer.set(i, asBuf.at((i % asBuf.length).toInt())); + this.buffer.set(i, asBuf.at((i % asBuf.length)).toByte()); } return this; case "Uint8Array": @@ -879,7 +1110,7 @@ Received value is: ${resolvedEnd}`) } return this; default: - throw createBusinessError(TypeErrorCodeId, `Parameter error: value must be a string, Buffer, Uint8Array, or number. + throw createBusinessError(TypeErrorCodeId, `Parameter error: value must be a string, Buffer, Uint8Array, or int. \ Received value is: ${value}`) } } @@ -888,60 +1119,75 @@ Received value is: ${value}`) * Throws an error when a parameter value is outside allowed range bounds * * @param {string} param_name - Name of the parameter that caused the error - * @param {number} param_value - The value that is out of range + * @param {double} param_value - The value that is out of range * @param {int} left_bound - Minimum allowed value (inclusive) * @param {int} right_bound - Maximum allowed value (inclusive) * @returns {void} * @throws {Error} With a message indicating the parameter name, its value, and valid range * @private */ - private static throwOutOfRangeError(param_name: string, param_value: number, left_bound: int, right_bound: int) { + private static throwOutOfRangeError(param_name: string, param_value: double, left_bound: int, right_bound: int) { let message = `The value of "${param_name}" is out of range.`; message += `It must be >= ${left_bound} and <= ${right_bound}. Received value is: ${param_value}`; // NOTE (templin.konstantin): must be changed to BusinessError later - throw new Error(message); + throw createBusinessError(OutOfBoundsErrorCodeId, message); } /** * Checks if the byteLength parameter is within valid range * - * @param {number} byteLength - Number of bytes to read or write + * @param {int} byteLength - Number of bytes to read or write * @returns {void} * @throws {Error} If byteLength is less than 1 or greater than 6 * @private */ - private static checkByteLengthConstraint(byteLength: number) { + private static checkByteLengthConstraint(byteLength: int) { if (byteLength < 1 || byteLength > 6) { Buffer.throwOutOfRangeError("byteLength", byteLength, 1, 6); } } + /** + * Checks if the offset parameter is within valid range + * + * @param {int} offset - Number of bytes to skip before reading + * @param {int} lengthOffset - Maximum allowed value (inclusive) + * @returns {void} + * @throws {Error} If byteLength is less than 0 or greater than lengthOffset + * @private + */ + private static checkOffsetConstraint(offset: int, lengthOffset: int) { + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + } /** * Creates and returns an iterator of buf keys (indices). * - * @returns { IterableIterator } + * @returns { IterableIterator } */ - public keys(): IterableIterator { + public keys(): IterableIterator { return new BufferIteratorKeys(this); } /** * Creates and returns an iterator for buf values (bytes). * - * @returns { IterableIterator } + * @returns { IterableIterator } */ - public values(): IterableIterator { + public values(): IterableIterator { return new BufferIteratorValues(this); } /** * Returns a new Buffer that references the same memory as the original, but offset and cropped by the start and end indices. * - * @param { number } [start] - Where the new Buffer will start - * @param { number } [end] - Where the new Buffer will end (not inclusive) + * @param { int } [start] - Where the new Buffer will start + * @param { int } [end] - Where the new Buffer will end (not inclusive) * @returns { Buffer } Returns a new Buffer that references the same memory as the original */ - public subarray(start?: number, end?: number): Buffer { + public subarray(start?: int, end?: int): Buffer { if (start == undefined || isNaN(start!)) { start = 0 } @@ -1032,69 +1278,93 @@ Received value is: ${value}`) * @private */ private getDataView(): DataView { - return new DataView(this.buffer, 0, this.length); + return new DataView(this.buffer, 0, this.length.toInt()); } /** * Writes a signed integer to the buffer at the specified offset using big-endian format * - * @param {number} value - Value to write - * @param {number} offset - Number of bytes to skip before writing - * @param {number} byteLength - Number of bytes to write (maximum 6) - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} offset - Number of bytes to skip before writing + * @param {int} byteLength - Number of bytes to write (maximum 6) + * @returns {int} Offset plus the number of bytes written * @throws {Error} If byteLength is greater than 6 */ - public writeIntBE(value: number, offset: number, byteLength: number): number { + public writeIntBE(value: long, offset: int, byteLength: int): int { Buffer.checkByteLengthConstraint(byteLength); const view = this.getDataView(); let remaining = value; for (let i = byteLength - 1; i >= 0; i--) { - view.setUint8(offset + i, remaining & 0xff); + view.setUint8((offset + i).toInt(), remaining & 0xff); remaining >>= 8; } return offset + byteLength; } + private getMultiplier(i: int): long { + return Math.pow(2, i * 8).toLong() + } + + private toUByte(b: byte): int { + if (b < 0) { + return b + 256; + } + return b; + } + + private readBytes(view: DataView, offset: int, byteLength: int, LE: boolean = false): long { + let val: long = 0; + for (let i = 0; i < byteLength; i++) { + let idx = LE ? i : byteLength - i - 1 + const byt: int = this.toUByte(this.$_get(offset + idx)!.toByte()) + val += byt * this.getMultiplier(i); + } + return val + } + + private toSigned(val: long, byteLength: int) + { + const unsignedMax = this.getMultiplier(byteLength) + const half = unsignedMax / 2 + const max = half - 1 + + if (val > max) { + return val - unsignedMax + } + return val + } + /** * Reads a signed integer from the buffer at the specified offset using big-endian format * - * @param {number} offset - Number of bytes to skip before reading - * @param {number} byteLength - Number of bytes to read (maximum 6) - * @returns {number} The read value + * @param {int} offset - Number of bytes to skip before reading + * @param {int} byteLength - Number of bytes to read (maximum 6) + * @returns {int} The read value * @throws {Error} If byteLength is greater than 6 */ - public readIntBE(offset: number, byteLength: number): number { + public readIntBE(offset: int, byteLength: int): long { + let lengthOffset: int = this.length - byteLength; + Buffer.checkOffsetConstraint(offset, lengthOffset); Buffer.checkByteLengthConstraint(byteLength); - const view = this.getDataView(); - let val: number = 0; - let multiplier = 1 << (8 * (byteLength - 1)); - for (let i = 0; i < byteLength; i++) { - const byt = view.getUint8(offset + i); - val += byt * multiplier; - multiplier >>= 8; - } - const signBit = 1 << (8 * byteLength - 1); - if (val >= signBit) { - val -= (signBit * 2); - } - return val; + let val = this.readBytes(this.getDataView(), offset, byteLength) + return this.toSigned(val, byteLength) } /** * Writes a signed integer to the buffer at the specified offset using little-endian format * - * @param {number} value - Value to write - * @param {number} offset - Number of bytes to skip before writing - * @param {number} byteLength - Number of bytes to write (maximum 6) - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} offset - Number of bytes to skip before writing + * @param {int} byteLength - Number of bytes to write (maximum 6) + * @returns {int} Offset plus the number of bytes written * @throws {Error} If byteLength is greater than 6 */ - public writeIntLE(value: number, offset: number, byteLength: number): number { + public writeIntLE(value: long, offset: int, byteLength: int): int { Buffer.checkByteLengthConstraint(byteLength); const view = this.getDataView(); let remaining = value; for (let i = 0; i < byteLength; i++) { - view.setUint8(offset + i, remaining & 0xff); + view.setUint8((offset + i).toInt(), remaining & 0xff); remaining >>= 8; } return offset + byteLength; @@ -1103,42 +1373,34 @@ Received value is: ${value}`) /** * Reads a signed integer from the buffer at the specified offset using little-endian format * - * @param {number} offset - Number of bytes to skip before reading - * @param {number} byteLength - Number of bytes to read (maximum 6) - * @returns {number} The read value + * @param {int} offset - Number of bytes to skip before reading + * @param {int} byteLength - Number of bytes to read (maximum 6) + * @returns {long} The read value * @throws {Error} If byteLength is greater than 6 */ - public readIntLE(offset: number, byteLength: number): number { + public readIntLE(offset: int, byteLength: int): long { + let lengthOffset: int = this.length - byteLength; + Buffer.checkOffsetConstraint(offset, lengthOffset); Buffer.checkByteLengthConstraint(byteLength); - const view = this.getDataView(); - let val: number = 0; - let multiplier = 1; - for (let i = 0; i < byteLength; i++) { - const byt = view.getUint8(offset + i); - val += byt * multiplier; - multiplier <<= 8; - } - if (val >= (1 << (8 * byteLength - 1))) { - val -= (1 << (8 * byteLength)); - } - return val; + let val = this.readBytes(this.getDataView(), offset, byteLength, true) + return this.toSigned(val, byteLength) } /** * Writes an unsigned integer to the buffer at the specified offset using big-endian format * - * @param {number} value - Value to write - * @param {number} offset - Number of bytes to skip before writing - * @param {number} byteLength - Number of bytes to write (maximum 6) - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} offset - Number of bytes to skip before writing + * @param {int} byteLength - Number of bytes to write (maximum 6) + * @returns {int} Offset plus the number of bytes written * @throws {Error} If byteLength is greater than 6 */ - public writeUIntBE(value: number, offset: number, byteLength: number): number { + public writeUIntBE(value: long, offset: int, byteLength: int): int { Buffer.checkByteLengthConstraint(byteLength); const view = this.getDataView(); let remaining = value; for (let i = byteLength - 1; i >= 0; i--) { - view.setUint8(offset + i, remaining & 0xFF); + view.setUint8((offset + i).toInt(), remaining & 0xFF); remaining >>= 8; } return offset + byteLength; @@ -1147,36 +1409,33 @@ Received value is: ${value}`) /** * Reads an unsigned integer from the buffer at the specified offset using big-endian format * - * @param {number} offset - Number of bytes to skip before reading - * @param {number} byteLength - Number of bytes to read (maximum 6) - * @returns {number} The read value + * @param {int} offset - Number of bytes to skip before reading + * @param {int} byteLength - Number of bytes to read (maximum 6) + * @returns {long} The read value * @throws {Error} If byteLength is greater than 6 */ - public readUIntBE(offset: number, byteLength: number): number { + public readUIntBE(offset: int, byteLength: int): long { + let lengthOffset: int = this.length - byteLength; + Buffer.checkOffsetConstraint(offset, lengthOffset); Buffer.checkByteLengthConstraint(byteLength); - let val: number = 0; - const view = this.getDataView(); - for (let i = 0; i < byteLength; i++) { - val = (val * 256) + view.getUint8(offset + i); - } - return val; + return this.readBytes(this.getDataView(), offset, byteLength) } /** * Writes an unsigned integer to the buffer at the specified offset using little-endian format * - * @param {number} value - Value to write - * @param {number} offset - Number of bytes to skip before writing - * @param {number} byteLength - Number of bytes to write (maximum 6) - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} offset - Number of bytes to skip before writing + * @param {int} byteLength - Number of bytes to write (maximum 6) + * @returns {int} Offset plus the number of bytes written * @throws {Error} If byteLength is greater than 6 */ - public writeUIntLE(value: number, offset: number, byteLength: number): number { + public writeUIntLE(value: long, offset: int, byteLength: int): int { Buffer.checkByteLengthConstraint(byteLength); const view = this.getDataView(); let remaining = value; for (let i = 0; i < byteLength; i++) { - view.setUint8(offset + i, remaining & 0xFF); + view.setUint8((offset + i).toInt(), remaining & 0xFF); remaining >>= 8; } return offset + byteLength; @@ -1185,45 +1444,47 @@ Received value is: ${value}`) /** * Reads an unsigned integer from the buffer at the specified offset using little-endian format * - * @param {number} offset - Number of bytes to skip before reading - * @param {number} byteLength - Number of bytes to read (maximum 6) - * @returns {number} The read value + * @param {int} offset - Number of bytes to skip before reading + * @param {int} byteLength - Number of bytes to read (maximum 6) + * @returns {long} The read value * @throws {Error} If byteLength is greater than 6 */ - public readUIntLE(offset: number, byteLength: number): number { + public readUIntLE(offset: int, byteLength: int): long { + let lengthOffset: int = this.length - byteLength; + Buffer.checkOffsetConstraint(offset, lengthOffset); Buffer.checkByteLengthConstraint(byteLength); - const view = this.getDataView(); - let val: number = 0; - let multiplier = 1; - for (let i = 0; i < byteLength; i++) { - val += view.getUint8(offset + i) * multiplier; - multiplier <<= 8; - } - return val; + return this.readBytes(this.getDataView(), offset, byteLength, true) } /** * Writes a string to the buffer at the specified offset * * @param {string} str - String to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @param {number} [length] - Maximum number of bytes to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @param {int} [length] - Maximum number of bytes to write * @param {string} [encoding='utf8'] - Character encoding of the string - * @returns {number} Number of bytes written + * @returns {int} Number of bytes written * @throws { BusinessError } 10200001 - The value of "[offset/length]" is out of range. It mast be >= 0 and <= buf.length. Received value is: [offset/length] */ - public write(str: string, offset: number = 0, length?: number, encoding: string = 'utf8'): number { - if (offset < 0 || offset > this.length - 1) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. \ -It must be >= 0 and <= ${this.length}. Received value is: ${offset}`) + public write(str: string, offset: int = 0, length?: int, encoding: string = 'utf8'): int { + if (this.length === 0 ) { + throw createBusinessError(OutOfBoundsErrorCodeId, 'The buffer length is 0, and writing data is not allowed'); + } + if (offset != 0) { + if (offset < 0 || offset > this.length - 1) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${this.length - 1}. Received value is: ${offset}`); + } + } + if (!isEncoding(encoding)) { + throw createBusinessError(TypeErrorCodeId, 'Parameter error. The type of "encoding" must be BufferEncoding. the encoding invalid_encoding is unknown') } - const resolvedEncoding = encoding as buffer.BufferEncoding; const tmpLength = length ?? this.length - offset if (tmpLength < 0 || tmpLength > this.length) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "length" is out of range. \ -It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "length" is out of range. `+ + `It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) } const resolvedLength = Math.min(tmpLength, this.length - offset) let resolvedString = str @@ -1233,29 +1494,41 @@ It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) const byteLen = buffer.byteLength(resolvedString, resolvedEncoding); const strBuffer = buffer.from(resolvedString, resolvedEncoding); for (let i = 0; i < resolvedLength && i < byteLen; i++) { - this.buffer.set((offset + i).toInt(), strBuffer.at(i)); + this.buffer.set((offset + i).toInt(), strBuffer.at(i).toByte()); } - return Math.min(resolvedLength, byteLen); + return Math.min(resolvedLength, byteLen).toInt(); } /** * Reads a signed 64-bit integer from the buffer at the specified offset using big-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading + * @param {int} [offset=0] - Number of bytes to skip before reading * @returns {bigint} The read value + * @throws { BusinessError } 10200001 - The value of "[offset]" is out of range. It mast be >= 0 and + <= buf.length - 8. Received value is: [offset] */ - public readBigInt64BE(offset: number = 0): bigint { - return this.getDataView().getBigInt64(offset, false); + public readBigInt64BE(offset: int = 0): bigint { + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getBigInt64(offset.toInt(), false); } /** * Reads a signed 64-bit integer from the buffer at the specified offset using little-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading + * @param {int} [offset=0] - Number of bytes to skip before reading * @returns {bigint} The read value */ - public readBigInt64LE(offset: number = 0): bigint { - return this.getDataView().getBigInt64(offset, true); + public readBigInt64LE(offset: int = 0): bigint { + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getBigInt64(offset.toInt(), true); } /** @@ -1264,169 +1537,318 @@ It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) * @param {number} [offset=0] - Number of bytes to skip before reading * @returns {bigint} The read value */ - public readBigUInt64BE(offset: number = 0): bigint { - return this.getDataView().getBigUint64(offset, false); + public readBigUInt64BE(offset: int = 0): bigint { + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getBigUint64(offset.toInt(), false); } /** * Reads an unsigned 64-bit integer from the buffer at the specified offset using little-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading + * @param {int} [offset=0] - Number of bytes to skip before reading * @returns {bigint} The read value */ - public readBigUInt64LE(offset: number = 0): bigint { - return this.getDataView().getBigUint64(offset, true); + public readBigUInt64LE(offset: int = 0): bigint { + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getBigUint64(offset.toInt(), true); } /** * Reads a 64-bit double from the buffer at the specified offset using big-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {double} The read value */ - public readDoubleBE(offset: number = 0): number { - return this.getDataView().getFloat64(offset, false); + public readDoubleBE(offset: int = 0): double { + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getFloat64(offset.toInt(), false); } /** * Reads a 64-bit double from the buffer at the specified offset using little-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {double} The read value */ - public readDoubleLE(offset: number = 0): number { - return this.getDataView().getFloat64(offset, true); + public readDoubleLE(offset: int = 0): double { + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getFloat64(offset.toInt(), true); } /** * Reads a 32-bit float from the buffer at the specified offset using big-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {double} The read value */ - public readFloatBE(offset: number = 0): number { - return this.getDataView().getFloat32(offset, false); + public readFloatBE(offset: int = 0): double { + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getFloat32(offset.toInt(), false); } /** * Reads a 32-bit float from the buffer at the specified offset using little-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {double} The read value */ - public readFloatLE(offset: number = 0): number { - return this.getDataView().getFloat32(offset, true); + public readFloatLE(offset: int = 0): double { + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getFloat32(offset.toInt(), true); } /** * Reads a signed 8-bit integer from the buffer at the specified offset * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {long} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readInt8(offset: number = 0): number { - return this.getDataView().getInt8(offset); + public readInt8(offset: int = 0): long { + let lengthOffset: int = this.length - 1; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getInt8(offset.toInt()).toLong() } /** * Reads a signed 16-bit integer from the buffer at the specified offset using big-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readInt16BE(offset: number = 0): number { - return this.getDataView().getInt16(offset, false); + public readInt16BE(offset: int = 0): long { + let lengthOffset: int = this.length - 2; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getInt16(offset.toInt(), false).toLong() } /** * Reads a signed 16-bit integer from the buffer at the specified offset using little-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readInt16LE(offset: number = 0): number { - return this.getDataView().getInt16(offset, true); + public readInt16LE(offset: int = 0): long { + let lengthOffset: int = this.length - 2; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getInt16(offset.toInt(), true).toLong() } /** * Reads a signed 32-bit integer from the buffer at the specified offset using big-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readInt32BE(offset: number = 0): number { - return this.getDataView().getInt32(offset, false); + public readInt32BE(offset: int = 0): long { + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.read32Big(offset); + } + + private read32Big(byteOffset: int): int { + let len = this.length; + if (byteOffset < 0 || byteOffset + 4 > len) { + throw new RangeError("wrong index"); + } + let res: int = 0; + const startByte = byteOffset; + for (let i = 0; i < 4; i++) { + let byteVal: int = (this.bufferData.at(startByte + 3 - i)); + byteVal &= 0xff; + res = (res | byteVal << (8 * i)); + } + return res; } /** * Reads a signed 32-bit integer from the buffer at the specified offset using little-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readInt32LE(offset: number = 0): number { - return this.getDataView().getInt32(offset, true); + public readInt32LE(offset: int = 0): long { + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.read32Little(offset); + } + + private read32Little(byteOffset: int): int { + let len = this.length; + if (byteOffset < 0 || byteOffset + 4 > len) { + throw new RangeError("wrong index"); + } + let res: int = 0; + const startByte = byteOffset; + for (let i = 0; i < 4; i++) { + let byteVal: int = (this.buffer.at(startByte + i)); + byteVal &= 0xff; + res = (res | byteVal << (8 * i)); + } + return res; } /** * Reads an unsigned 8-bit integer from the buffer at the specified offset * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readUInt8(offset: number = 0): number { - return this.getDataView().getUint8(offset); + public readUInt8(offset: int = 0): long { + let lengthOffset: int = this.length - 1; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getUint8(offset.toInt()).toLong() } /** * Reads an unsigned 16-bit integer from the buffer at the specified offset using big-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readUInt16BE(offset: number = 0): number { - return this.getDataView().getUint16(offset, false); + public readUInt16BE(offset: int = 0): long { + let lengthOffset: int = this.length - 2; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getUint16(offset.toInt(), false).toLong() } /** * Reads an unsigned 16-bit integer from the buffer at the specified offset using little-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readUInt16LE(offset: number = 0): number { - return this.getDataView().getUint16(offset, true); + public readUInt16LE(offset: int = 0): long { + let lengthOffset: int = this.length - 2; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.getDataView().getUint16(offset.toInt(), true).toLong() } /** * Reads an unsigned 32-bit integer from the buffer at the specified offset using big-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readUInt32BE(offset: number = 0): number { - return this.getDataView().getUint32(offset, false); + public readUInt32BE(offset: int = 0): long { + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.readUInt32Big(offset).toLong(); + } + + private readUInt32Big(byteOffset: int): long { + let len = this.length; + if (byteOffset < 0 || byteOffset + 4 > len) { + throw new RangeError("wrong index"); + } + let res: long = 0; + const startByte = byteOffset; + for (let i = 0; i < 4; i++) { + let byteVal: long = (this.buffer.at(startByte + 3 - i)); + byteVal &= 0xff; + res = (res | byteVal << (8 * i)); + } + return res; } /** * Reads an unsigned 32-bit integer from the buffer at the specified offset using little-endian format * - * @param {number} [offset=0] - Number of bytes to skip before reading - * @returns {number} The read value + * @param {int} [offset=0] - Number of bytes to skip before reading + * @returns {long} The read value */ - public readUInt32LE(offset: number = 0): number { - return this.getDataView().getUint32(offset, true); + public readUInt32LE(offset: int = 0): long { + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + return this.readUInt32Little(offset); + } + + private readUInt32Little(offset: int): long { + let len = this.length; + if (offset < 0 || offset + 4 > len) { + throw new RangeError("wrong index"); + } + let res: long = 0; + const startByte = offset; + for (let i = 0; i < 4; i++) { + let byteVal: long = (this.bufferData.at(startByte + i)); + byteVal &= 0xff; + res = (res | byteVal << (8 * i)); + } + return res; } /** * Writes a signed 64-bit integer to the buffer at the specified offset using big-endian format * * @param {bigint} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeBigInt64BE(value: bigint, offset: number = 0): number { - this.getDataView().setBigInt64(offset, value, false); + public writeBigInt64BE(value: bigint, offset: int = 0): int { + if (value < Long.MIN_VALUE || value > Long.MAX_VALUE) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= -(2n ** 63n) and <= 2n ** 63n. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setBigInt64(offset.toInt(), value, false); return offset + 8; } @@ -1434,11 +1856,20 @@ It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) * Writes a signed 64-bit integer to the buffer at the specified offset using little-endian format * * @param {bigint} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeBigInt64LE(value: bigint, offset: number = 0): number { - this.getDataView().setBigInt64(offset, value, true); + public writeBigInt64LE(value: bigint, offset: int = 0): int { + if (value < Long.MIN_VALUE || value > Long.MAX_VALUE) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= -(2n ** 63n) and <= 2n ** 63n. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setBigInt64(offset.toInt(), value, true); return offset + 8; } @@ -1446,11 +1877,20 @@ It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) * Writes an unsigned 64-bit integer to the buffer at the specified offset using big-endian format * * @param {bigint} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeBigUInt64BE(value: bigint, offset: number = 0): number { - this.getDataView().setBigUint64(offset, value, false); + public writeBigUInt64BE(value: bigint, offset: int = 0): int { + if (value < 0 || value > ((1n << 64n) - 1n)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= 0 and <= 2n ** 64n - 1n. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setBigUint64(offset.toInt(), value, false); return offset + 8; } @@ -1458,214 +1898,363 @@ It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) * Writes an unsigned 64-bit integer to the buffer at the specified offset using little-endian format * * @param {bigint} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeBigUInt64LE(value: bigint, offset: number = 0): number { - this.getDataView().setBigUint64(offset, value, true); + public writeBigUInt64LE(value: bigint, offset: int = 0): int { + if (value < 0 || value > ((1n << 64n) - 1n)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= 0 and <= 2n ** 64n - 1n. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setBigUint64(offset.toInt(), value, true); return offset + 8; } /** * Writes a 64-bit double to the buffer at the specified offset using big-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {double} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeDoubleBE(value: number, offset: number = 0): number { - this.getDataView().setFloat64(offset, value, false); + public writeDoubleBE(value: double, offset: int = 0): int { + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setFloat64(offset.toInt(), value, false); return offset + 8; } /** * Writes a 64-bit double to the buffer at the specified offset using little-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {double} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeDoubleLE(value: number, offset: number = 0): number { - this.getDataView().setFloat64(offset, value, true); + public writeDoubleLE(value: double, offset: int = 0): int { + let lengthOffset: int = this.length - 8; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setFloat64(offset.toInt(), value, true); return offset + 8; } /** * Writes a 32-bit float to the buffer at the specified offset using big-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {double} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeFloatBE(value: number, offset: number = 0): number { - this.getDataView().setFloat32(offset, value, false); + public writeFloatBE(value: double, offset: int = 0): int { + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setFloat32(offset.toInt(), value, false); return offset + 4; } /** * Writes a 32-bit float to the buffer at the specified offset using little-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {double} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeFloatLE(value: number, offset: number = 0): number { - this.getDataView().setFloat32(offset, value, true); + public writeFloatLE(value: double, offset: int = 0): int { + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setFloat32(offset.toInt(), value, true); return offset + 4; } /** * Writes a signed 8-bit integer to the buffer at the specified offset * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeInt8(value: number, offset: number = 0): number { - this.getDataView().setInt8(offset, value); + public writeInt8(value: long, offset: int = 0): int { + if (value < (-Math.pow(2, 7)) || value > (Math.pow(2, 7) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= ${-Math.pow(2, 7)} and <= ${Math.pow(2, 7) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 1; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setInt8(offset.toInt(), value); return offset + 1; } /** * Writes a signed 16-bit integer to the buffer at the specified offset using big-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeInt16BE(value: number, offset: number = 0): number { - this.getDataView().setInt16(offset, value, false); + public writeInt16BE(value: long, offset: int = 0): int { + if (value < (-Math.pow(2, 15)) || value > (Math.pow(2, 15) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= ${-Math.pow(2, 15)} and <= ${Math.pow(2, 15) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 2; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setInt16(offset.toInt(), value, false); return offset + 2; } /** * Writes a signed 16-bit integer to the buffer at the specified offset using little-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeInt16LE(value: number, offset: number = 0): number { - this.getDataView().setInt16(offset, value, true); + public writeInt16LE(value: long, offset: int = 0): int { + if (value < (-Math.pow(2, 15)) || value > (Math.pow(2, 15) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= ${-Math.pow(2, 15)} and <= ${Math.pow(2, 15) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 2; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setInt16(offset.toInt(), value, true); return offset + 2; } /** * Writes a signed 32-bit integer to the buffer at the specified offset using big-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeInt32BE(value: number, offset: number = 0): number { - this.getDataView().setInt32(offset, value, false); + public writeInt32BE(value: long, offset: int = 0): int { + if (value < (-Math.pow(2, 31)) || value > (Math.pow(2, 31) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= ${-Math.pow(2, 31)} and <= ${Math.pow(2, 31) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setInt32(offset.toInt(), value, false); return offset + 4; } /** * Writes a signed 32-bit integer to the buffer at the specified offset using little-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeInt32LE(value: number, offset: number = 0): number { - this.getDataView().setInt32(offset, value, true); + public writeInt32LE(value: long, offset: int = 0): int { + if (value < -(Math.pow(2, 31)) || value > (Math.pow(2, 31) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= ${-Math.pow(2, 31)} and <= ${Math.pow(2, 31) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setInt32(offset.toInt(), value, true); return offset + 4; } /** * Writes an unsigned 8-bit integer to the buffer at the specified offset * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeUInt8(value: number, offset: number = 0): number { - this.getDataView().setUint8(offset, value); + public writeUInt8(value: long, offset: int = 0): int { + if (value < 0 || value > 255) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= 0 and <= 255. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 1; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setUint8(offset.toInt(), value); return offset + 1; } /** * Writes an unsigned 16-bit integer to the buffer at the specified offset using big-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeUInt16BE(value: number, offset: number = 0): number { - this.getDataView().setUint16(offset, value, false); + public writeUInt16BE(value: long, offset: int = 0): int { + if (value < 0 || value > (Math.pow(2, 16) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= 0 and <= ${Math.pow(2, 16) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 2; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setUint16(offset.toInt(), value, false); return offset + 2; } /** * Writes an unsigned 16-bit integer to the buffer at the specified offset using little-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeUInt16LE(value: number, offset: number = 0): number { - this.getDataView().setUint16(offset, value, true); + public writeUInt16LE(value: long, offset: int = 0): int { + if (value < 0 || value > (Math.pow(2, 16) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= 0 and <= ${Math.pow(2, 16) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 2; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setUint16(offset.toInt(), value, true); return offset + 2; } /** * Writes an unsigned 32-bit integer to the buffer at the specified offset using big-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeUInt32BE(value: number, offset: number = 0): number { - this.getDataView().setUint32(offset, value, false); + public writeUInt32BE(value: long, offset: int = 0): int { + if (value < 0 || value > (Math.pow(2, 32) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= 0 and <= ${Math.pow(2, 32) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.getDataView().setUint32(offset.toInt(), value, false); return offset + 4; } /** * Writes an unsigned 32-bit integer to the buffer at the specified offset using little-endian format * - * @param {number} value - Value to write - * @param {number} [offset=0] - Number of bytes to skip before writing - * @returns {number} Offset plus the number of bytes written + * @param {long} value - Value to write + * @param {int} [offset=0] - Number of bytes to skip before writing + * @returns {int} Offset plus the number of bytes written */ - public writeUInt32LE(value: number, offset: number = 0): number { - this.getDataView().setUint32(offset, value, true); + public writeUInt32LE(value: long, offset: int = 0): int { + if (value < 0 || value > (Math.pow(2, 32) - 1)) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "value" is out of range. ` + + `It must be >= 0 and <= ${Math.pow(2, 32) - 1}. Received value is: ${value}`) + } + let lengthOffset: int = this.length - 4; + if (offset < 0 || offset > lengthOffset) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "offset" is out of range. ` + + `It must be >= 0 and <= ${lengthOffset}. Received value is: ${offset}`); + } + this.set32Litter(offset, value); return offset + 4; } + private set32Litter(byteOffset: int, value: long): void { + let len = this.length; + if (byteOffset < 0 || byteOffset + 4.0 > len) { + throw new RangeError("wrong index"); + } + let bits = value; + if (bits == Long.MAX_VALUE || bits == Long.MIN_VALUE) { + bits = 0; + } + const startByte = byteOffset; + for (let i = 0; i < 4; i++) { + let byteVal = ((bits >>> (i * 8)) & 0xff).toByte(); + this.buffer.set(startByte + i, byteVal); + } + } + /** - * Returns the byte at the specified index. - * @param {number} index – byte index to read - * @returns {number} the byte value at `index` - */ - $_get(index: number): number | undefined { + * Returns the item at that index. + * + * @param { int } index - The zero-based index of the desired code unit. + * Throws error if index < 0 or index >= buffer.length. + * @returns { int } The element in the buffer matching the given index. + * @throws { BusinessError } 10200001 - The value of index is out of range. + */ + $_get(index: int): long { if (index < 0 || index >= this.length) { - return undefined; + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of "length" is out of range. \ +It must be >= 0 and <= ${this.length}. Received value is: ${index}`) } - return this.getDataView().getUint8(index); + return this.getDataView().getUint8(index.toInt()).toLong() } /** * Sets the byte at the specified index. * - * @param {number} index – byte index to write - * @param {number} value – byte value (0–255) + * @param {int} index – byte index to write + * @param {long} value – byte value (0–255) */ - $_set(index: number, value: number): void { + $_set(index: int, value: long): void { if (index < 0 || index >= this.length) { return; } - this.getDataView().setUint8(index, value); + this.getDataView().setUint8(index.toInt(), value); } - } - enum TypeParameters { - StringType = "string", - ArrayBufferType = "ArrayBuffer", - DataViewType = "DataView", - TypedArrayType = "TypedArray", - BlobType = "Blob" + /** + * Converts this Buffer instance into a JsonElement. + * + * @returns {JsonElement} A new JsonElement containing the Buffer + */ + public toJSON(): jsonx.JsonElement { + const arr = new Array; + for (let i: int = 0; i < this.length; ++i) { + const intElement = new jsonx.JsonElement(); + intElement.setInteger(this.at(i).toInt()); + arr.push(intElement); + } + const objectElem = new jsonx.JsonElement({} as Record); + objectElem.setElement("type", jsonx.JsonElement.createString("Buffer")); + objectElem.setElement("data", jsonx.JsonElement.createArray(arr)); + return objectElem; + } } export interface BlobOptions { @@ -1675,177 +2264,226 @@ It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) endings: 'transparent'|'native' }; - type SrcType = FixedArray | FixedArray | FixedArray | FixedArray | FixedArray; - type SrcItemType = string | ArrayBuffer | TypedArray | DataView | Blob; - type ArrSrcType = Array | Array | Array | Array | Array; - function toString(src: SrcItemType): string { - if (src instanceof string) { - return src - } else if (src instanceof ArrayBuffer) { - return buffer.from(src as ArrayBuffer).toString() - } else if (src instanceof TypedArray) { - return buffer.from(src.buffer as ArrayBuffer).toString() - } else if (src instanceof DataView) { - return buffer.from((src as DataView).buffer as ArrayBuffer).toString() - } - return "" - } + type FixedArrayUnionType = FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray + | FixedArray; + + type ArrayItemType = string + | ArrayBuffer + | Int8Array + | Uint8Array + | Uint8ClampedArray + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array + | BigInt64Array + | BigUint64Array + | DataView + | Blob; + + type ArrayUnionType = Array + | Array + | Array + | Array + | Array + | Array + | Array + | Array + | Array + | Array + | Array + | Array + | Array + | Array + | Array; + /** * A Blob encapsulates immutable, raw data that can be safely shared across multiple worker threads. */ export class Blob { + /** + * The content-size of the Blob. + */ + get size(): int { return this.blobSize } + + /** + * The content-type of the Blob. + */ + get type(): string { return this.blobType } + + /** + * The buffer of the Blob. + */ + private arrBuffer: ArrayBuffer + + /** + * Get the array of buffer. + */ + private arr: ArrayUnionType + + /** + * The size of the Blob. + */ + private blobSize: int; + + /** + * The type of the Blob. + */ + private blobType: string; /** * Creates a new Blob object containing a concatenation of the given sources. * , , , and sources are copied into the 'Blob' and can therefore be safely modified after the 'Blob' is created. * String sources are encoded as UTF-8 byte sequences and copied into the Blob. Unmatched surrogate pairs within each string part will be replaced by Unicode U+FFFD replacement characters. - * @param {FixedArray | FixedArray | FixedArray | FixedArray | FixedArray} [sources] - An array of string, , , , or objects, or any mix of such objects, that will be stored within the Blob. - * @param {BlobOptions} [options] - options + * @param { FixedArray | FixedArray | FixedArray | FixedArray | FixedArray } [sources] - An array of string, , , , or objects, or any mix of such objects, that will be stored within the Blob. + * @param { BlobOptions } [options] - options */ - constructor(sources: SrcType, options?: BlobOptions) { - this.checkParameters(sources) - this.createArray(sources) + constructor(sources: FixedArrayUnionType, options?: BlobOptions) { + this.handleParameters(sources) + this.arrBuffer = this.flattenData(this.arr) + this.blobSize = this.arrBuffer.byteLength; if (options != undefined) { - this.type = options!.type ? options!.type! : '' + this.blobType = options!.type ? options!.type! : '' } else { - this.type = '' + this.blobType = '' } - this.calcSize() } - constructor(sources: ArrSrcType, options?: BlobOptions) { - this.checkParameters(sources) - this.arr = sources + /** + * Creates a new Blob object containing a concatenation of the given sources. + * , , , and sources are copied into the 'Blob' and can therefore be safely modified after the 'Blob' is created. + * String sources are encoded as UTF-8 byte sequences and copied into the Blob. Unmatched surrogate pairs within each string part will be replaced by Unicode U+FFFD replacement characters. + * @param { Array | Array | Array | Array | Array } [sources] - An array of string, , , , or objects, or any mix of such objects, that will be stored within the Blob. + * @param { BlobOptions } [options] - options + */ + constructor(sources: ArrayUnionType, options?: BlobOptions) { + this.arrBuffer = this.flattenData(sources); + this.blobSize = this.arrBuffer.byteLength; if (options != undefined) { - this.type = options!.type ? options!.type! : '' + this.blobType = options!.type ? options!.type! : '' } else { - this.type = '' + this.blobType = '' } - this.calcSize() } - private constructor(sources: ArrSrcType, typeKey: string, type?: string) { - this.arr = sources - this.typeKey = typeKey - this.calcSize() + private constructor(sources: Array, typeKey: string, type?: string) { + this.arrBuffer = this.flattenData(sources) + this.blobSize = this.arrBuffer.byteLength; if (type != undefined) { - this.type = type + this.blobType = type } else { - this.type = '' - } - } - - private constructor(blob: Blob) { - this.arr = blob.arr - this.size = blob.size - this.type = blob.type - this.typeKey = blob.typeKey - } - - private checkParameters(sources: ArrSrcType | SrcType) { - for (const element of sources) { - if (element instanceof string) { - this.typeKey = TypeParameters.StringType - break; - } else if (element instanceof ArrayBuffer) { - this.typeKey = TypeParameters.ArrayBufferType - break; - } else if (element instanceof DataView) { - this.typeKey = TypeParameters.DataViewType - break; - } else if (element instanceof TypedArray) { - this.typeKey = TypeParameters.TypedArrayType - break; - } else if (element instanceof Blob) { - this.typeKey = TypeParameters.BlobType - break; - } + this.blobType = '' } } - private calcSize(): number { - this.size = 0 - switch (this.typeKey) { - case "string": { - for (const item of this.arr as Array) { - this.size += ArrayBuffer.bytesLength(item, "utf8") - } - } break - case "ArrayBuffer": { - for (const item of this.arr as Array) { - this.size += item.getByteLength() - } - } break - case "TypedArray": { - for (const item of this.arr as Array) { - this.size += item.byteLength - } - } break - case "DataView": { - for (const item of this.arr as Array) { - this.size += item.byteLength - } - } break - case "Blob": { - for (const item of this.arr as Array) { - this.size += item.calcSize() - } - } break + /** + * handle Parameters, and retrun ArrSrcType. + */ + private handleParameters(sources: FixedArrayUnionType) { + if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray) + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else if (sources instanceof FixedArray) { + this.arr = Array.from(sources as FixedArray); + } else { + this.arr = Array.from(sources as FixedArray); } - return this.size } - private createArray(sources: SrcType) { - switch (this.typeKey) { - case "string": { - this.arr = Array.from(sources as FixedArray) - } break - case "ArrayBuffer": { - this.arr = Array.from(sources as FixedArray) - } break - case "TypedArray": { - this.arr = Array.from(sources as FixedArray) - } break - case "DataView": { - this.arr = Array.from(sources as FixedArray) - } break - case "Blob": { - this.arr = Array.from(sources as FixedArray) - } break + private transformToArrayBuffer(value: ArrayItemType): ArrayBuffer { + if (value instanceof String) { + return ArrayBuffer.from(value, "utf-8"); + } else if (value instanceof ArrayBuffer) { + return value; + } else if (value instanceof DataView) { + return value.buffer; + } else if (value instanceof Blob) { + return value.getArrayBuffer() as ArrayBuffer; + } else { + return value.buffer as ArrayBuffer; + } + } + + private flattenData(value: ArrayUnionType): ArrayBuffer { + const resolvedList = new Array(); + for (const item of value) { + resolvedList.push(this.transformToArrayBuffer(item)); + } + const bufferLength = resolvedList.reduce( + (acc: number, item: ArrayBuffer): number => acc + item.byteLength, 0); + const listOfBytes = new Array(); + for (const item of resolvedList) { + for (let i = 0; i < item.byteLength; i++) { + listOfBytes.push(item.at(i)); + } + } + const buffer = new ArrayBuffer(bufferLength); + let offset = 0; + for (let i = 0; i < listOfBytes.length; i++) { + buffer.set(offset, listOfBytes[i]); + offset += 1; } + return buffer; + } + + /** + * Get flattened ArrayBuffer through sync interface. + */ + getArrayBuffer(): ArrayBuffer { + return ArrayBuffer.from(this.arrBuffer as ArrayBuffer); } /** * Creates and returns a new Blob containing a subset of this Blob objects data. The original Blob is not altered. * - * @param {number} [start=0] - The starting index. - * @param {number} [end=buffer.length] - The ending index. + * @param {int} [start=0] - The starting index. + * @param {int} [end=buffer.length] - The ending index. * @param {string} [type] - The content-type for the new Blob * @returns {Blob} */ - slice(start?: number, end?: number, type?: string): Blob { - switch (this.typeKey) { - case "string": { - let arr: Array = (this.arr as Array).slice(start, end) - return new Blob(arr, this.typeKey, type) - } - case "ArrayBuffer": { - let arr: Array = (this.arr as Array).slice(start, end) - return new Blob(arr, this.typeKey, type) - } - case "TypedArray": { - let arr: Array = (this.arr as Array).slice(start, end) - return new Blob(arr, this.typeKey, type) - } - case "DataView": { - let arr: Array = (this.arr as Array).slice(start, end) - return new Blob(arr, this.typeKey, type) - } - case "Blob": { - let arr: Array = (this.arr as Array).slice(start, end) - return new Blob(arr, this.typeKey, type) - } + slice(start?: int, end?: int, type?: string): Blob { + if (start == undefined) { + start = 0; } - return new Blob(this) + const slicedArrayBuffer = this.arrBuffer.slice(start, end) as ArrayBuffer; + return new Blob([slicedArrayBuffer], "ArrayBuffer", type) } /** @@ -1854,7 +2492,11 @@ It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) * @returns {Promise} */ text(): Promise { - return Promise.resolve(this.getString()) + let Encoding: string = "utf8"; + let resolvedStart: int = 0; + let resolvedEnd: int = this.arrBuffer.byteLength as int; + let decodeData: string = ArrayBuffer.stringify(this.arrBuffer, Encoding, resolvedStart, resolvedEnd); + return Promise.resolve(decodeData); } /** @@ -1863,45 +2505,7 @@ It must be >= 0 and <= ${this.length}. Received value is: ${tmpLength}`) * @returns {Promise} */ arrayBuffer(): Promise { - return Promise.resolve(this.toArrayBuffer()) - } - - size: number - - /** - * The content-type of the Blob. - */ - type: string - - private arr: ArrSrcType - private typeKey: string = 'string' - - private toArrayBuffer(): ArrayBuffer { - const str = this.getString() - return ArrayBuffer.from(str, "utf-8"); - } - - private getString(): string { - let str = '' - let b = new StringBuilder() - switch (this.typeKey) { - case "string": { - (this.arr as Array).forEach((value: string) => { b.append(toString(value as SrcItemType)) }) - } break - case "ArrayBuffer": { - (this.arr as Array).forEach((value: ArrayBuffer) => { b.append(toString(value)) }) - } break - case "TypedArray": { - (this.arr as Array).forEach((value: TypedArray) => { b.append(toString(value)) }) - } break - case "DataView": { - (this.arr as Array).forEach((value: DataView) => { b.append(toString(value)) }) - } break - case "Blob": { - (this.arr as Array).forEach((value: Blob) => { b.append(value.getString()) }) - } break - } - return b.toString() + return Promise.resolve(ArrayBuffer.from(this.arrBuffer as ArrayBuffer)) } } } diff --git a/static_core/plugins/ets/sdk/api/@ohos.uri.ets b/static_core/plugins/ets/sdk/api/@ohos.uri.ets index a4652afb361478de6ede5aa3bdc728611118fcc5..923845ffb5739e4e9f089cbd21da9f6290c5c673 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.uri.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.uri.ets @@ -15,186 +15,176 @@ import { BusinessError } from "@ohos.base"; -const SyntaxErrorCodeId: number = 10200002; -const TypeErrorCodeId: number = 401; - -function createBusinessError(code: number, message:string){ - let err = new BusinessError(); - err.code = code; - err.name='BusinessError'; - err.message = message; - return err; -} - +/** +* The uri module provides utilities for URI resolution and parsing. +* +* @namespace uri +*/ export namespace uri { - + /** + * URI Represents a Uniform Resource Identifier (URI) reference. + * + * @class URI + */ export class URI { uriEntry: UriEntry; - constructor(input: string) { - this.uriEntry = new UriEntry(input); - let errStr: string = this.uriEntry.getErrStr(); - if (errStr != "") { - throw createBusinessError(SyntaxErrorCodeId,`Syntax Error. Invalid Uri string: The ${errStr}`) - } + + /** + * URI constructor, which is used to instantiate a URI object. + * uri: Constructs a URI by parsing a given string. + * + * @param { string } uri - uri uri + * @throws { BusinessError } 10200002 - Invalid uri string. + */ + constructor(uri: string) { + this.uriEntry = new UriEntry(uri); } + /** + * Creates an opaque Uri from the given components. + * + * @param { string } scheme - of the URI. + * @param { string } ssp -scheme-specific-part, everything between the scheme separator (':') and the fragment + * separator ('#'), which will get encoded. + * @param { string } fragment - fragment, everything after the '#', null if undefined, will get encoded. + * @returns { URI } Return Uri consisting of a given scheme, SSP, and fragment. + */ static createFromParts(scheme: string, ssp: string, fragment: string): URI { let uriStr: string = scheme; uriStr += ':' + encodeURIComponent(ssp); if (fragment != "") { uriStr += '#' + encodeURIComponent(fragment); } - return createNewUri(uriStr); + return new URI(uriStr); } + /** + * Returns the serialized URI as a string. + * + * @returns { string } Returns the serialized URI as a string. + */ toString(): string { - return toAscllString(this.uriEntry.toString()); - } - - equals(other: URI): boolean { - return this.uriEntry.equals(other.uriEntry); + return toAsciiString(this.uriEntry.toString()); } + /** + * Check whether this URI is equivalent to other URI objects. + * + * @param { URI } other - other other URI object to be compared + * @returns { boolean } boolean Tests whether this URI is equivalent to other URI objects. + */ equalsTo(other: URI): boolean { - return this.uriEntry.equals(other.uriEntry); + return this.uriEntry.equalsTo(other.uriEntry); } + /** + * Indicates whether this URI is an absolute URI. + * + * @returns { boolean } boolean Indicates whether the URI is an absolute URI (whether the scheme component is defined). + */ checkIsAbsolute(): boolean { return this.uriEntry.isAbsolute(); } + /** + * Determine whether URI is Relative. + * + * @returns { boolean } Return true as Relative, otherwise return false. + */ checkRelative(): boolean { return this.uriEntry.isRelative(); } + /** + * Determine whether URI is Opaque. + * + * @returns { boolean } Return true as Opaque, otherwise return false. + */ checkOpaque(): boolean { return this.uriEntry.isOpaque(); } + /** + * Determine whether URI is hierarchical. + * + * @returns { boolean } Return true as Hierarchical, otherwise return false. + */ checkHierarchical(): boolean { return this.uriEntry.isHierarchical(); } + /** + * Encodes the key and value and then appends the result to the query string. + * + * @param { string } [key] - The key it will be encoded with. + * @param { string } [value] - The value it will be encoded with. + * @returns { URI } Return URI object. + */ addQueryValue(key: string, value: string): URI { let uriStr = this.uriEntry.addQueryValue(encodeURIComponent(key), encodeURIComponent(value)); - return createNewUri(uriStr); + return new URI(uriStr); } + /** + * Creates a new Uri by appending an already-encoded path segment to a base Uri. + * + * @param { string } encodedPathSegment - Encoded path segment to be added. + * @returns { URI } After adding, return the URI object. + */ addEncodedSegment(encodedPathSegment: string): URI { let uriStr = this.uriEntry.addSegment(encodedPathSegment); - return createNewUri(uriStr); + return new URI(uriStr); } + /** + * Encodes the given path segment and appends it to the path. + * + * @param { string } [pathSegment] - path segment to be added. + * @returns { URI } After adding, return the URI object. + */ addSegment(pathSegment: string): URI { let uriStr = this.uriEntry.addSegment(encodeURIComponent(pathSegment)); - return createNewUri(uriStr); + return new URI(uriStr); } + /** + * Searches the query string for the first value with the given key. + * + * @param { string } key - Given the first value of the key. + * @returns { string | null } Return decoded value. + */ getQueryValue(key: string): string | null { - let query = this.uriEntry.getQuery(); - if (query === '') { - return null; - } - const queryStrs = query.split('&'); - for (const item of queryStrs) { - const eqPos = item.indexOf('='); - let currentKey = item; - let currentValue = ''; - if (eqPos !== -1) { - currentKey = item.substring(0, eqPos); - currentValue = item.substring(eqPos + 1); - } - - const decodedKey = this.decodeSafelyInner(currentKey); - if (decodedKey === key) { - const decodedValue = this.decodeSafelyInner(currentValue.replaceAll('+', ' ')); - return decodedValue || ''; - } - } - return null; + return this.uriEntry.getQueryValue(key); } - private decodeSafelyOut(input: string): string { - let decodedString = ""; - let decodedTemp = ""; - let index: number = 0; - while (index < input.length) { - let isHexDigit: boolean = Char.isHexDigit(input[index + 1]) && Char.isHexDigit(input[index + 2]); - if (input[index] == c'%' && isHexDigit) { - const encodedChar = input.slice(index, index + 3); - try { - decodedString += decodeURIComponent(decodedTemp + encodedChar); - decodedTemp = ""; - } catch (e) { - decodedTemp += encodedChar; - } - index += 3; - continue; - } - decodedString += decodedTemp + input[index]; - decodedTemp = ""; - index++; - } - return decodedString + decodedTemp; - } - - private decodeSafelyInner(input: string): string { - if (input == "") { - return input; - } - try { - return decodeURIComponent(input); - } catch (e) { - return this.decodeSafelyOut(input); - } - } - - getQueryNames(): Array { - let query = this.uriEntry.getQuery(); - if (query == "") { - return new Array(); - } - let names = new Set(); - let start: number = 0; - while (start < query.length) { - let next: number = query.indexOf('&', start); - let end: number = (next == -1) ? query.length : next; - let separator: number = query.indexOf('=', start); - if (separator > end || separator == -1) { - separator = end; - } - let name = query.substring(start, separator); - names.add(this.decodeSafelyInner(name)); - start = end + 1; - } - return Array.from(names); + /** + * Obtains all non-repeated keys in the query component of this URI. + * + * @returns { string[] } Return a set of decoded names. + */ + getQueryNames(): string[] { + return this.uriEntry.getQueryNames(); } + /** + * Searches the query string for parameter values with the given key. + * + * @param { string } key - The key it will be encoded with. + * @returns { Array } Return a set of decoded values. + */ getQueryValues(key: string): Array { - let query = this.uriEntry.getQuery(); - if (query == "") { - return new Array(); - } - let values = new Array(); - let queryStrs: string[] = query.split('&'); - let isKeyEmpty = key == ""; - for (let item of queryStrs) { - if (isKeyEmpty && item == "") { - values.push(item); - } - let strArr: string[] = item.split('='); - let isKeyEqualStr0 = this.decodeSafelyInner(strArr[0]) == key; - if (strArr.length == 1 && isKeyEqualStr0) { - values.push(""); - } else if (isKeyEqualStr0) { - values.push(this.decodeSafelyInner(item.substring(strArr[0].length + 1))); - } - } - return values; + return this.uriEntry.getQueryValues(key); } + /** + * Searches the query string for the first value with the given key and interprets it as a boolean value. + * + * @param { string } key - Indicates the key value to be queried. + * @param { boolean } defaultValue - The default value returned when the key has no query parameters. + * @returns { boolean } Query with key value returns true, otherwise returns false. + */ getBooleanQueryValue(key: string, defaultValue: boolean): boolean { - let flag = this.getQueryValue(key); + let flag = this.uriEntry.getQueryValue(key); if (flag == null) { return defaultValue; } @@ -202,298 +192,480 @@ export namespace uri { return 'false' != flag && '0' != flag; } + /** + * Gets the decoded last path segment. + * + * @returns { string } Returns the last decoded segment, or null if the path is empty. + */ getLastSegment(): string { - let segments = this.uriEntry.getSegment(); - if (segments.length == 0) { - return ""; - } - return this.decodeSafelyInner(segments[segments.length - 1]); + return this.uriEntry.getLastSegment(); } - getSegment(): Array { - let array = new Array(); - let segments = this.uriEntry.getSegment(); - segments.forEach( - (element: string, i: number) => { - array.push(this.decodeSafelyInner(element)); - }); - return array; + /** + * Gets the decoded path segments. + * + * @returns { string[] } Return decoded path segments, each without a leading or trailing "/". + */ + getSegment(): string[] { + return this.uriEntry.getSegments(); } + /** + * Clears the previously set query. + * + * @returns { URI } After clearing, return the URI object. + */ clearQuery(): URI { let uriStr: string = this.uriEntry.clearQuery(); - return createNewUri(uriStr); + return new URI(uriStr); } + /** + * Normalize the path of this URI, It is not safe to call the normalize interface with URI. + * + * @returns { URI } URI Used to normalize the path of this URI and return a URI object whose path has been normalized. + */ normalize(): URI { let uriStr: string = this.uriEntry.normalize(); - return createNewUri(uriStr); + return new URI(uriStr); } + /** + * Gets the protocol part of the URI. + * + * @returns { string | null } + */ get scheme(): string | null { - if (this.uriEntry.getScheme() == '') { - return null; - } - return this.uriEntry.getScheme(); + let s: string = this.uriEntry.getScheme(); + return s == "" ? null : s; } + /** + * Gets the authority part of the URI. + * + * @returns { string | null } + */ get authority(): string | null { - let thisAuthority: string = this.uriEntry.getAuthority(); - if (thisAuthority == '') { - return null; - } - return this.dealDecode(thisAuthority, true); + let s: string = this.uriEntry.getAuthority(); + return s == "" ? null : this.dealDecodeInput(s); } + /** + * Gets the decoding scheme-specific part of the URI. + * + * @returns { string } + */ get ssp(): string { - return this.dealDecode(this.uriEntry.getSsp(), true) + return this.dealDecodeInput(this.uriEntry.getSsp()) } - private dealDecode(input: string, decode?: boolean): string { + private dealDecodeInput(input: string): string { let index1 = input.indexOf('['); - let result = ""; - if (index1 != -1) { - let index2 = input.indexOf(']', index1); - let split1 = input.substring(0, index1); - let split2 = input.substring(index1 + 1, index2); - let split3 = input.substring(index2 + 1); - if (decode) { - result = this.decodeSafelyInner(split1) + '[' + split2 + ']' + this.decodeSafelyInner(split3); - } else { - result = split1 + '[' + split2 + ']' + split3; - } - } else { - result = decode ? this.decodeSafelyInner(input) : input; + if (index1 == -1) { + return decodeURIComponent(input); } - return result; + let index2 = input.indexOf(']', index1); + let split1 = input.substring(0, index1); + let split2 = input.substring(index1 + 1, index2); + let split3 = input.substring(index2 + 1); + return decodeURIComponent(split1) + '[' + split2 + ']' + decodeURIComponent(split3); } + /** + * Gets Obtains the user information part of the URI. + * + * @returns { string | null } + */ get userInfo(): string | null { - if (this.uriEntry.getUserinfo() == '') { - return null; - } - return this.decodeSafelyInner(this.uriEntry.getUserinfo()); + let s: string = this.uriEntry.getUserinfo(); + return s == '' ? null : decodeURIComponent(s); } + /** + * Gets the hostname portion of the URI without a port. + * + * @returns { string | null } + */ get host(): string | null { - if (this.uriEntry.getHost() == '') { - return null; - } - return this.uriEntry.getHost(); + let s: string = this.uriEntry.getHost(); + return s == '' ? null : s; } + /** + * Gets the port portion of the URI. + * + * @returns { string } + */ get port(): string { return Number.toString(this.uriEntry.getPort()); } + /** + * Gets the path portion of the URI. + * + * @returns { string | null } + */ get path(): string | null { - if (this.uriEntry.getPath() == '') { - return null; - } - return this.decodeSafelyInner(this.uriEntry.getPath()); + let s: string = this.uriEntry.getPath(); + return s == '' ? null : decodeURIComponent(s); } + /** + * Gets the query portion of the URI + * + * @returns { string | null } + */ get query(): string | null { - if (this.uriEntry.getQuery() == '') { - return null; - } - return this.decodeSafelyInner(this.uriEntry.getQuery()); + let s: string = this.uriEntry.getQuery(); + return s == '' ? null : decodeURIComponent(s); } + /** + * Gets the fragment portion of the URI + * + * @returns { string | null } + */ get fragment(): string | null { - if (this.uriEntry.getFragment() == '') { - return null; - } - return this.decodeSafelyInner(this.uriEntry.getFragment()); + let s: string = this.uriEntry.getFragment(); + return s == '' ? null : decodeURIComponent(s); } + /** + * Gets Obtains the encoded user information part of the URI. + * + * @returns { string | null } + */ get encodedUserInfo(): string | null { - if (this.uriEntry.getUserinfo() == '') { - return null; - } - return this.uriEntry.getUserinfo(); + let s: string = this.uriEntry.getUserinfo(); + return s == '' ? null : s; } + /** + * Gets the encoded path portion of the URI . + * + * @returns { string | null } + */ get encodedPath(): string | null { - if (this.uriEntry.getPath() == '') { - return null; - } - return this.uriEntry.getPath(); + let s: string = this.uriEntry.getPath(); + return s == '' ? null : s; } + /** + * Gets the encoded query component from this URI. + * + * @returns { string | null } + */ get encodedQuery(): string | null { - if (this.uriEntry.getQuery() == '') { - return null; - } - return this.uriEntry.getQuery(); + let s: string = this.uriEntry.getQuery(); + return s == '' ? null : s; } + /** + * Gets the encoded fragment part of this URI, everything after the '#'. + * + * @returns { string | null } + */ get encodedFragment(): string | null { - if (this.uriEntry.getFragment() == '') { - return null; - } - return this.uriEntry.getFragment(); + let s: string = this.uriEntry.getFragment(); + return s == '' ? null : s; } + /** + * Gets the encoded authority part of this URI. + * + * @returns { string | null } + */ get encodedAuthority(): string | null { - let thisAuthority: string = this.uriEntry.getAuthority(); - if (thisAuthority == '') { - return null; - } - return this.dealDecode(thisAuthority); + let s: string = this.uriEntry.getAuthority(); + return s == '' ? null : s; } + /** + * Gets the scheme-specific part of this URI, i.e. everything between the scheme separator ':' and + * the fragment separator '#'. + * + * @returns { string } + */ get encodedSSP(): string { - let thisSsp: string = this.uriEntry.getSsp(); - return this.dealDecode(thisSsp); + return this.uriEntry.getSsp(); } - set scheme(input: string | null) { - if (input === null || input.length === 0) { + /** + * Sets the protocol part of the URI. + * + * @param { string } input + */ + set scheme(input: string) { + if (input.length === 0) { return; } this.uriEntry.setScheme(input); } - set path(input: string | null) { - if (input === null) { - return; - } + /** + * Sets the path portion of the URI. + * @param { string } input + */ + set path(input: string) { this.uriEntry.setPath(encodeURI(input)); } /** + * Sets the decoding scheme-specific part of the URI. + * @param { string } input * NOTE(zhangziye):#25267 Unable to use different getter/setter signature */ - set ssp(input: string | null) { - if (input === null) { - return; - } + set ssp(input: string) { this.uriEntry.setSsp(encodeURI(input)) } - set authority(input: string | null) { - if (input === null) { - return; - } + /** + * Sets the decoding permission component part of this URI. + * @param { string } input + */ + set authority(input: string) { this.uriEntry.setAuthority(encodeURI(input)) } - set userInfo(input: string | null) { - if (input === null) { - return; - } + /** + * Sets Obtains the user information part of the URI. + * @param { string } input + */ + set userInfo(input: string) { this.uriEntry.setUserInfo(encodeURIComponent(input)); } - set query(input: string | null) { - if (input === null) { - return; - } + /** + * Sets the query portion of the URI + * @param { string } input + */ + set query(input: string) { this.uriEntry.setQuery(encodeURIComponent(input)); } - set fragment(input: string | null) { - if (input === null) { - return; - } + /** + * Sets the fragment portion of the URI + * @param { string } input + */ + set fragment(input: string) { this.uriEntry.setFragment(encodeURIComponent(input)); } - set encodedUserInfo(input: string | null) { - if (input === null) { - return; - } + /** + * Sets Obtains the encoded user information part of the URI. + * @param { string } input + */ + set encodedUserInfo(input: string) { this.uriEntry.setUserInfo(input); } - set encodedPath(input: string | null) { - if (input === null) { - return; - } + /** + * Sets the encoded path portion of the URI. + * @param { string } input + */ + set encodedPath(input: string) { this.uriEntry.setPath(input); } - set encodedQuery(input: string | null) { - if (input === null) { - return; - } + /** + * Sets the encoded query component from this URI. + * @param { string } input + */ + set encodedQuery(input: string) { this.uriEntry.setQuery(input); } - set encodedFragment(input: string | null) { - if (input === null) { - return; - } + /** + * Sets the encoded fragment component from this URI. + * @param { string } input + */ + set encodedFragment(input: string) { this.uriEntry.setFragment(input); } - set encodedAuthority(input: string | null) { - if (input === null) { - return; - } + /** + * Sets the encoded authority component from this URI. + * @param { string } input + */ + set encodedAuthority(input: string) { this.uriEntry.setAuthority(input); } /** * NOTE(zhangziye):#25267 Unable to use different getter/setter signature */ - set encodedSSP(input: string | null) { - if (input === null) { - return; - } + set encodedSSP(input: string) { this.uriEntry.setSsp(input); } } - function toAscllString(uriStr: string): string { + function toAsciiString(uriStr: string): string { return encodeURI(uriStr) .replaceAll("%5B", '[') .replaceAll("%5D", ']') .replaceAll("%25", '%'); } - function createNewUri(uriStr: string): URI { - return new URI(uriStr); + interface Rules { + g_ruleAlphaLookup: Uint8Array; + g_ruleSchemeLookup: Uint8Array; + g_ruleUrlcLookup: Uint8Array; + g_ruleUserInfoLookup: Uint8Array; + g_rulePortLookup: Uint8Array; + g_ruleDigitLookup: Uint8Array; + g_rulePathLookup: Uint8Array; } - const MAX_BIT_SIZE: number = 128; - - class UriData { - port: number = -1; - scheme: string = ""; - userInfo: string = ""; - host: string = ""; - query: string = ""; - fragment: string = ""; - path: string = ""; - authority: string = ""; - SchemeSpecificPart: string = ""; - }; - - export class UriEntry { - uriData: UriData = new UriData(); - data: string = ""; - inputUri: string = ""; - errStr: string = ""; + function initializeUriRules(): Rules { + + const digitAggregate = "0123456789"; + const alphasAggregate = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const schemeAggregate = "+-.| _-~!$&=,;'(){}*"; + const uricAggregate = "/?:@[]%\""; + const pathAggregate = "/:@%"; + const userInfoAggregate = ":%"; + const portAggregate = ".:@-;&=+$,-_!~*'()"; + + let g_ruleAlphaLookup = new Uint8Array(128); + let g_ruleSchemeLookup = new Uint8Array(128); + let g_ruleUrlcLookup = new Uint8Array(128); + let g_ruleUserInfoLookup = new Uint8Array(128); + let g_rulePortLookup = new Uint8Array(128); + let g_ruleDigitLookup = new Uint8Array(128); + let g_rulePathLookup = new Uint8Array(128); + + for (let i = 0; i < digitAggregate.length; i++) { + const code = digitAggregate.charCodeAt(i).toInt(); + if (code < 128) { + g_ruleSchemeLookup[code] = 1; + g_ruleUrlcLookup[code] = 1; + g_ruleUserInfoLookup[code] = 1; + g_ruleDigitLookup[code] = 1; + g_rulePortLookup[code] = 1; + g_rulePathLookup[code] = 1; + } + } + + for (let i = 0; i < alphasAggregate.length; i++) { + const code = alphasAggregate.charCodeAt(i).toInt(); + if (code < 128) { + g_ruleSchemeLookup[code] = 1; + g_ruleUrlcLookup[code] = 1; + g_ruleUserInfoLookup[code] = 1; + g_ruleAlphaLookup[code] = 1; + g_rulePortLookup[code] = 1; + g_rulePathLookup[code] = 1; + } + } + + for (let i = 0; i < schemeAggregate.length; i++) { + const code = schemeAggregate.charCodeAt(i).toInt(); + if (code < 128) { + g_ruleSchemeLookup[code] = 1; + g_ruleUrlcLookup[code] = 1; + g_ruleUserInfoLookup[code] = 1; + g_rulePathLookup[code] = 1; + } + } + + for (let i = 0; i < uricAggregate.length; i++) { + const code = uricAggregate.charCodeAt(i).toInt(); + if (code < 128) { + g_ruleUrlcLookup[code] = 1; + } + } + + for (let i = 0; i < pathAggregate.length; i++) { + const code = pathAggregate.charCodeAt(i).toInt(); + if (code < 128) { + g_rulePathLookup[code] = 1; + } + } + + for (let i = 0; i < userInfoAggregate.length; i++) { + const code = userInfoAggregate.charCodeAt(i).toInt(); + if (code < 128) { + g_ruleUserInfoLookup[code] = 1; + } + } + + for (let i = 0; i < portAggregate.length; i++) { + const code = portAggregate.charCodeAt(i).toInt(); + if (code < 128) { + g_rulePortLookup[code] = 1; + } + } + return { + g_ruleAlphaLookup, + g_ruleSchemeLookup, + g_ruleUrlcLookup, + g_ruleUserInfoLookup, + g_rulePortLookup, + g_ruleDigitLookup, + g_rulePathLookup, + } + } + /** + * Handling specific URI logic + */ + class UriEntry { + private static uriRules: Rules = initializeUriRules(); + private errStr: string = ""; + private data: string = ""; + private inputUri: string = ""; + + private port: number = -1; + private scheme: string = ""; + private userInfo: string = ""; + private host: string = ""; + private query: string = ""; + private fragment: string = ""; + private path: string = ""; + private authority: string = ""; + private schemeSpecificPart: string = ""; constructor(input: string) { - this.errStr = ""; if (input == "") { this.errStr = "Uri is empty."; - return; + this.checkErrAndThrow(); } this.inputUri = input; - try { - this.analysisUri(); - } catch (e) { } + this.analysisUri(); + } + + getQueryValue(key: string): string | null { + let queryList = this.getQueryList(); + for (let i = 0; i < queryList.length; i += 2) { + if (decodeURIComponent(queryList[i]) == key) { + return decodeURIComponent(queryList[i + 1].replaceAll("+", ' ')); + } + } + return null; } + getQueryValues(key: string): Array { + let queryList = this.getQueryList(); + let values = new Array(); + for (let i = 0; i < queryList.length; i += 2) { + if (decodeURIComponent(queryList[i]) == key) { + values.push(decodeURIComponent(queryList[i + 1])); + } + } + return values; + } + getQueryNames(): Array { + let queryList = this.getQueryList(); + let names = new Set(); + for (let i = 0; i < queryList.length; i += 2) { + names.add(decodeURIComponent(queryList[i])); + } + return Array.from(names); + } private assignSchemeSpecificPart() { - this.uriData.SchemeSpecificPart += this.data; - if (this.uriData.query != "") { - this.uriData.SchemeSpecificPart += "?"; - this.uriData.SchemeSpecificPart += this.uriData.query; + this.schemeSpecificPart += this.data; + if (this.query != "") { + this.schemeSpecificPart += "?"; + this.schemeSpecificPart += this.query; } } @@ -525,38 +697,55 @@ export namespace uri { this.data = this.data.substring(2); // 2:Intercept the string from the second subscript this.analysisHostAndPath(); this.checkErrAndThrow(); - } else if (this.data.length != 0 && this.data[0] == c'/') { - this.uriData.path = this.data; + } else if (this.data.length != 0 && this.data.charAt(0) == c'/') { + this.path = this.data; this.assignSchemeSpecificPart(); this.data = ""; } else { this.assignSchemeSpecificPart(); - this.uriData.query = ""; + this.query = ""; this.data = ""; } } - private checkCharacter(data: string, rule: boolean[], flag: boolean): boolean { - let dataLen = data.length; - for (let i = 0; i < dataLen; i++) { - let charCode = data.charCodeAt(i); - if (charCode >= 0 && charCode < MAX_BIT_SIZE) { - if (!rule[charCode]) { + private checkCharacter(data: string, rule: Uint8Array, ignoreNonAscll: boolean): boolean { + const len = data.length; + + for (let i = 0; i < len; i++) { + const code = data.charCodeAt(i).toInt(); + + if (code < 128) { + if (rule[code] === 0) { return false; } - } else if (!flag) { + } else if (!ignoreNonAscll) { return false; } } return true; } + private getQueryList(): Array { + let queryList = new Array(); + if (this.query == "" || this.query == null) { + return queryList; + } + for (let str of this.query.split('&')) { + let pare = str.split('='); + let key = pare.length > 0 ? pare[0] : ''; + let value = pare.length > 1 ? pare[1] : ''; + queryList.push(key); + queryList.push(value); + } + return queryList; + } + private specialPath() { - if (!this.checkCharacter(this.data, UriRule.rulePath, true)) { + if (!this.checkCharacter(this.data, UriEntry.uriRules.g_rulePathLookup, true)) { this.errStr = "SpecialPath does not conform to the rule."; return; } - this.uriData.path = this.data; + this.path = this.data; this.data = ""; } @@ -566,21 +755,21 @@ export namespace uri { return; } let fragment = this.data.substring(pos + 1); - if (!this.checkCharacter(fragment, UriRule.ruleUrlc, true)) { + if (!this.checkCharacter(fragment, UriEntry.uriRules.g_ruleUrlcLookup, true)) { this.errStr = "Fragment does not conform to the rule."; return; } - this.uriData.fragment = fragment; + this.fragment = fragment; this.data = this.data.substring(0, pos); } private analysisQuery(pos: number) { let query = this.data.substring(pos + 1); - if (!this.checkCharacter(query, UriRule.ruleUrlc, true)) { + if (!this.checkCharacter(query, UriEntry.uriRules.g_ruleUrlcLookup, true)) { this.errStr = "Query does not conform to the rule."; return; } - this.uriData.query = query; + this.query = query; this.data = this.data.substring(0, pos); } @@ -588,31 +777,29 @@ export namespace uri { let slashPos = this.data.indexOf('/'); if (slashPos != -1 && slashPos < pos) { this.specialPath(); - this.uriData.SchemeSpecificPart += (this.uriData.path); - this.uriData.SchemeSpecificPart += ("?"); - this.uriData.SchemeSpecificPart += (this.uriData.query); + this.schemeSpecificPart += (this.path); + this.schemeSpecificPart += ("?"); + this.schemeSpecificPart += (this.query); this.data = ""; } else { - let code = this.data.charCodeAt(0); - if (code >= 0 && code < MAX_BIT_SIZE && - !UriRule.ruleAlpha[code]) { + if (!this.checkCharacter(this.data[0], UriEntry.uriRules.g_ruleAlphaLookup, true)) { this.errStr = "Scheme the first character must be a letter."; return; } let scheme = this.data.substring(0, pos); - if (!this.checkCharacter(scheme, UriRule.ruleScheme, false)) { + if (!this.checkCharacter(scheme, UriEntry.uriRules.g_ruleSchemeLookup, false)) { this.errStr = "Scheme does not conform to the rule."; return; } - this.uriData.scheme = scheme; + this.scheme = scheme; this.data = this.data.substring(pos + 1); } } private analysisHost(isLawfulPort: boolean) { // find ipv4 or ipv6 or host - if (this.data.length > 0 && this.data[0] == c'[') { - if (this.data[this.data.length - 1] == c']') { + if (this.data.length > 0 && this.data.charAt(0) == c'[') { + if (this.data.charAt(this.data.length.toInt() - 1) == c']') { // IPV6 if (!isLawfulPort) { this.errStr = "Port does not conform to the rule."; @@ -630,9 +817,9 @@ export namespace uri { } // ipv4 if (!isLawfulPort || !this.analysisIPV4()) { - this.uriData.port = -1; - this.uriData.host = ""; - this.uriData.userInfo = ""; + this.port = -1; + this.host = ""; + this.userInfo = ""; } } } @@ -647,7 +834,7 @@ export namespace uri { this.analysisPath(pos); this.checkErrAndThrow(); } - this.uriData.authority = this.data; + this.authority = this.data; // find UserInfo pos = this.data.indexOf('@'); if (pos != -1) { @@ -669,61 +856,121 @@ export namespace uri { private analysisPath(pos: number) { let path = this.data.substring(pos); - if (!this.checkCharacter(path, UriRule.rulePath, true)) { + if (!this.checkCharacter(path, UriEntry.uriRules.g_rulePathLookup, true)) { this.errStr = "Path does not conform to the rule."; return; } - this.uriData.path = path; + this.path = path; this.data = this.data.substring(0, pos); } private analysisUserInfo(pos: number) { let userInfo = this.data.substring(0, pos); - if (!this.checkCharacter(userInfo, UriRule.ruleUserInfo, true)) { + if (!this.checkCharacter(userInfo, UriEntry.uriRules.g_ruleUserInfoLookup, true)) { this.errStr = "UserInfo does not conform to the rule."; return; } - this.uriData.userInfo = userInfo; + this.userInfo = userInfo; this.data = this.data.substring(pos + 1); } private analysisPort(pos: number) { - let port = this.data.substring(pos + 1); - if (!this.checkCharacter(port, UriRule.rulePort, true)) { + let portStr = this.data.substring(pos + 1); + if (!this.checkCharacter(portStr, UriEntry.uriRules.g_rulePortLookup, true)) { this.errStr = "Port does not conform to the rule."; return false; - } else if (this.checkCharacter(port, UriRule.ruleDigit, false)) { - // 10:The maximum number of bits for int value - if (port.length == 0 || port.length > 10) { + } + + if (portStr == '') { + return false; + } + + let port = new Number(portStr); + if (isNaN(port)) { + this.data = this.data.substring(0, pos); + return false; + } + if (port < 0 || port > Int.MAX_VALUE) { + return false; + } + this.port = port; + this.data = this.data.substring(0, pos); + return true; + } + + private isValidIPv4(ip: string) { + const segments = ip.split('.'); + if (segments.length !== 4) return false; + + for (const segment of segments) { + if (segment === "") return false; + + if (segment.length > 1 && segment[0] === '0') return false; + + const num = Number(segment); + if (isNaN(num) || num < 0 || num > 255) return false; + } + return true; + } + + private isAlphanumeric(code: string) { + return (code >= 'a' && code <= 'z') || + (code >= 'A' && code <= 'Z') || + (code >= '0' && code <= '9'); + } + + private isLetter(code: string) { + return (code >= 'a' && code <= 'z') || (code >= 'A' && code <= 'Z'); + } + + private isValidChar(code: string) { + const allowedChars = `-~_|+{}!$&=,;:'()* `; + return this.isAlphanumeric(code) || allowedChars.includes(code); + } + + private isValidHostname(hostname: string) { + if (hostname.length === 0 || hostname.length > 253) return false; + + if (!hostname.includes('.')) { + if (!this.isAlphanumeric(hostname[0])) return false; + if (hostname.length > 1 && !this.isValidChar(hostname[hostname.length - 1])) return false; + + for (let i = 1; i < hostname.length - 1; i++) { + if (!this.isValidChar(hostname[i])) return false; + } + return true; + } + + const labels: Array = hostname.split('.'); + if (labels.length < 2) return false; + + for (let i = 0; i < labels.length; i++) { + const label = labels[i]; + if (label.length === 0 || label.length > 63) return false; + + if (i === labels.length - 1) { + if (!this.isLetter(label[0])) return false; + } else { + if (!this.isAlphanumeric(label[0])) return false; } - let tempPort = new Number(port); - if (tempPort < 0 || tempPort > Int.MAX_VALUE) { + + if (!this.isValidChar(label[label.length - 1])) return false; + + for (let j = 1; j < label.length - 1; j++) { + if (!this.isValidChar(label[j])) return false; } - this.uriData.port = tempPort; - this.data = this.data.substring(0, pos); - return true; - } else { - this.data = this.data.substring(0, pos); - return false; } + + return true; } private analysisIPV4() { - /** - * 正则图解析 https://www.jyshare.com/front-end/7625 - regex_match默认使用的是完全匹配模式 js需要要加上^$ - */ - let ipv4 = new RegExp("^((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)$"); - let hostname = new RegExp("^(([a-zA-Z0-9]([a-zA-Z0-9\\-~_|\\+{}!$&=,;:'()\\*\\s]*[a-zA-Z0-9])?\\.)+([a-zA-Z]" - + "([a-zA-Z0-9\\-~_|\\+{}!$&=,;:'()\\*\\s]*[a-zA-Z0-9\\-~_|\\+{}!$&=,;:'()\\*\\s])?))$|^([a-zA-Z0-9]([a-zA-Z0-9\\-~_|\\+{}!$&=,;:'()\\*\\s]*[a-zA-Z0-9])?)$"); - let isIpv4 = ipv4.test(this.data); - let isHosName = hostname.test(this.data); - if (!isIpv4 && !isHosName) { + if (!this.isValidIPv4(this.data) && !this.isValidHostname(this.data)) { return false; } else { - this.uriData.host = this.data; + this.host = this.data; this.data = ""; return true; } @@ -745,39 +992,16 @@ export namespace uri { this.errStr = "Ipv6 does not conform to the rule."; return; } - this.uriData.host = this.data; + this.host = this.data; this.data = ""; } - equals(other: UriEntry): boolean { - if (this.uriData.port != other.uriData.port) { - return false; - } - if (this.uriData.scheme != other.uriData.scheme) { - return false; - } - if (this.uriData.userInfo != other.uriData.userInfo) { - return false; - } - if (this.uriData.host != other.uriData.host) { - return false; - } - if (this.uriData.query != other.uriData.query) { - return false; - } - if (this.uriData.fragment != other.uriData.fragment) { - return false; - } - if (this.uriData.path != other.uriData.path) { - return false; - } - if (this.uriData.authority != other.uriData.authority) { - return false; - } - if (this.uriData.SchemeSpecificPart != other.uriData.SchemeSpecificPart) { - return false; - } - return true; + equalsTo(other: UriEntry): boolean { + return (this.port == other.port) && (this.scheme == other.scheme) + && (this.userInfo == other.userInfo) && (this.host == other.host) + && (this.query == other.query) && (this.fragment == other.fragment) + && (this.path == other.path) && (this.authority == other.authority) + && (this.schemeSpecificPart == other.schemeSpecificPart); } toString(): string { @@ -785,11 +1009,11 @@ export namespace uri { } isAbsolute(): boolean { - return this.uriData.scheme != ""; + return this.scheme != ""; } isRelative(): boolean { - return this.uriData.scheme == ""; + return this.scheme == ""; } isOpaque(): boolean { @@ -797,14 +1021,14 @@ export namespace uri { } isHierarchical(): boolean { - let index = this.inputUri.indexOf(':'); + let index = this.inputUri.indexOf(':').toInt(); if (index == -1) { return true; } if (this.inputUri.length == index + 1) { return false; } - return this.inputUri[index + 1] == c'/'; + return this.inputUri.charAt(index.toInt() + 1) == c'/'; } addQueryValue(key: string, value: string) { @@ -817,17 +1041,17 @@ export namespace uri { private buildUriString(str: string, param: string) { let result = ""; - if (this.uriData.scheme != "") { - result += this.uriData.scheme + ":"; + if (this.scheme != "") { + result += this.scheme + ":"; } - if (this.uriData.authority != "") { - result += "//" + this.uriData.authority; + if (this.authority != "") { + result += "//" + this.authority; } - if (this.uriData.path != "") { - result += this.uriData.path; + if (this.path != "") { + result += this.path; } if (str == "segment") { - let lastChar = result[result.length - 1]; + let lastChar = result.charAt(result.length.toInt() - 1); if (lastChar == c'/') { result += param; } else { @@ -835,68 +1059,80 @@ export namespace uri { } } if (str != "clearquery") { - if (this.uriData.query == "") { + if (this.query == "") { if (str == "query") { result += "?" + param; } } else { - result += "?" + this.uriData.query; + result += "?" + this.query; if (str == "query") { result += "&" + param; } } } - if (this.uriData.fragment != "") { - result += "#" + this.uriData.fragment; + if (this.fragment != "") { + result += "#" + this.fragment; } return result; } - getSegment(): Array { - let segments = new Array(); - if (this.uriData.path == "") { - return segments; - } - let previous = 0; - let current = 0; - for (current = this.uriData.path.indexOf('/', previous); current != -1; - current = this.uriData.path.indexOf('/', previous)) { - if (previous < current) { - let segment = this.uriData.path.substring(previous, current); - segments.push(segment); + getLastSegment(): string { + let input = ""; + let end = this.path.length - 1; + while (end >= 0 && this.path[end] == '/') { + end--; + } + while (end >= 0) { + let start = end; + let tmp = ""; + while (start >= 0 && this.path[start] != '/') { + start --; + } + for (let i = start + 1; i <= end; i++) { + tmp += this.path[i]; + } + tmp = tmp.trim(); + if (tmp != "") { + input = tmp; + break; + } else { + end = start - 1; } - previous = current + 1; - } - if (previous < this.uriData.path.length) { - segments.push(this.uriData.path.substring(previous)); } - return segments; + return decodeURIComponent(input); } - getErrStr(): string { - return this.errStr; + getSegments(): Array { + let segmentArray = new Array(); + for (let segment of this.path.split('/')) { + if (segment.trim() != '') { + segmentArray.push(decodeURIComponent(segment.trim())) + } + } + return segmentArray; } private checkErrAndThrow() { if (this.errStr != "") { - throw new Error(); + const SyntaxErrorCodeId: int = 10200002; + throw new BusinessError(SyntaxErrorCodeId, new Error('BusinessError', `Syntax Error. Invalid Uri string: The ${this.errStr}`, undefined)); } } normalize(): string { let temp = new Array; - let pathLen = this.uriData.path.length; + let pathLen = this.path.length; if (pathLen == 0) { return this.inputUri; } let pos = 0; let left = 0; - while ((pos = this.uriData.path.indexOf('/', left)) != -1) { - temp.push(this.uriData.path.substring(left, pos)); + while ((pos = this.path.indexOf('/', left)) != -1) { + temp.push(this.path.substring(left, pos)); left = pos + 1; } if (left != pathLen) { - temp.push(this.uriData.path.substring(left)); + temp.push(this.path.substring(left)); } const STR_DOTDOT = '..'; let tempLen = temp.length; @@ -927,69 +1163,69 @@ export namespace uri { private split(path: string): string { let normalizeUri = ""; - if (this.uriData.scheme != "") { - normalizeUri += this.uriData.scheme + ":"; + if (this.scheme != "") { + normalizeUri += this.scheme + ":"; } - if (this.uriData.path == "") { - normalizeUri += this.uriData.SchemeSpecificPart; + if (this.path == "") { + normalizeUri += this.schemeSpecificPart; } else { - if (this.uriData.host != "") { + if (this.host != "") { normalizeUri += "//"; - if (this.uriData.userInfo != "") { - normalizeUri += this.uriData.userInfo + "@"; + if (this.userInfo != "") { + normalizeUri += this.userInfo + "@"; } - normalizeUri += this.uriData.host; - if (this.uriData.port != -1) { - normalizeUri += ":" + Number.toString(this.uriData.port); + normalizeUri += this.host; + if (this.port != -1) { + normalizeUri += ":" + Number.toString(this.port); } - } else if (this.uriData.authority != "") { - normalizeUri += "//" + this.uriData.authority; + } else if (this.authority != "") { + normalizeUri += "//" + this.authority; } normalizeUri += path; } - if (this.uriData.query != "") { - normalizeUri += "?" + this.uriData.query; + if (this.query != "") { + normalizeUri += "?" + this.query; } - if (this.uriData.fragment != "") { - normalizeUri += '#' + this.uriData.fragment; + if (this.fragment != "") { + normalizeUri += '#' + this.fragment; } return normalizeUri; } getScheme() { - return this.uriData.scheme; + return this.scheme; } getAuthority() { - return this.uriData.authority; + return this.authority; } getSsp() { - return this.uriData.SchemeSpecificPart; + return this.schemeSpecificPart; } getUserinfo() { - return this.uriData.userInfo; + return this.userInfo; } getHost() { - return this.uriData.host; + return this.host; } getPort() { - return this.uriData.port; + return this.port; } getPath() { - return this.uriData.path; + return this.path; } getQuery() { - return this.uriData.query; + return this.query; } getFragment() { - return this.uriData.fragment; + return this.fragment; } clearQuery() { @@ -997,9 +1233,7 @@ export namespace uri { } setScheme(scheme: string): void { - let code = scheme.charCodeAt(0); - if (code >= 0 && code < MAX_BIT_SIZE && - !UriRule.ruleAlpha[code]) { + if (!this.checkCharacter(scheme[0], UriEntry.uriRules.g_ruleAlphaLookup, true)) { this.errStr = "Scheme the first character must be a letter."; return; } @@ -1011,20 +1245,20 @@ export namespace uri { temp = scheme; } - if (!this.checkCharacter(temp, UriRule.ruleScheme, false)) { + if (!this.checkCharacter(temp, UriEntry.uriRules.g_ruleSchemeLookup, false)) { this.errStr = "Scheme does not conform to the rule."; return; } - this.uriData.scheme = temp; + this.scheme = temp; this.inputUri = this.updateToString(); } setAuthority(authorityStr: string): void { // Reset values - this.uriData.port = -1; - this.uriData.host = ""; - this.uriData.userInfo = ""; - this.uriData.authority = authorityStr; + this.port = -1; + this.host = ""; + this.userInfo = ""; + this.authority = authorityStr; this.data = authorityStr; // Find UserInfo @@ -1033,11 +1267,11 @@ export namespace uri { const userStr = this.data.substring(0, atPos); this.data = this.data.substring(atPos + 1); - if (!this.checkCharacter(userStr, UriRule.ruleUserInfo, true)) { + if (!this.checkCharacter(userStr, UriEntry.uriRules.g_ruleUserInfoLookup, true)) { this.errStr = "userInfo does not conform to the rule"; return; } - this.uriData.userInfo = userStr; + this.userInfo = userStr; } let isLawfulPort = true; @@ -1059,13 +1293,13 @@ export namespace uri { setSsp(sspStr: string): void { // Reset all relevant fields - this.uriData.authority = ""; - this.uriData.port = -1; - this.uriData.host = ""; - this.uriData.userInfo = ""; - this.uriData.query = ""; - this.uriData.path = ""; - this.uriData.SchemeSpecificPart = ""; + this.authority = ""; + this.port = -1; + this.host = ""; + this.userInfo = ""; + this.query = ""; + this.path = ""; + this.schemeSpecificPart = ""; this.data = sspStr; // Handle query part @@ -1080,165 +1314,125 @@ export namespace uri { this.assignSchemeSpecificPart(); this.data = this.data.substring(2); // Skip the '//' this.analysisHostAndPath(); - } else if (this.data[0] === c'/') { - this.uriData.path = this.data; + } else if (this.data.charAt(0) === c'/') { + this.path = this.data; this.assignSchemeSpecificPart(); this.data = ""; } else { this.assignSchemeSpecificPart(); - this.uriData.path = ""; - this.uriData.query = ""; + this.path = ""; + this.query = ""; this.data = ""; } this.inputUri = this.updateToString(); } setUserInfo(userInfo: string): void { - if (!this.uriData.host || !this.checkCharacter(userInfo, UriRule.ruleUserInfo, true)) { + if (!this.host || !this.checkCharacter(userInfo, UriEntry.uriRules.g_ruleUserInfoLookup, true)) { this.errStr = "userInfo does not conform to the rule"; return; } - this.uriData.userInfo = userInfo; + this.userInfo = userInfo; this.updateAuthority(); this.updateSsp(); this.inputUri = this.updateToString(); } setPath(pathStr: string): void { - if (!this.checkCharacter(pathStr, UriRule.rulePath, true)) { + if (!this.checkCharacter(pathStr, UriEntry.uriRules.g_rulePathLookup, true)) { this.errStr = "pathStr does not conform to the rule"; return; } - this.uriData.path = pathStr; + this.path = pathStr; this.updateSsp(); this.inputUri = this.updateToString(); } setQuery(queryStr: string): void { - if (!this.checkCharacter(queryStr, UriRule.ruleUrlc, true)) { + if (!this.checkCharacter(queryStr, UriEntry.uriRules.g_ruleUrlcLookup, true)) { this.errStr = "QueryStr does not conform to the rule"; return; } - this.uriData.query = queryStr; + this.query = queryStr; this.updateSsp(); this.inputUri = this.updateToString(); } setFragment(fragmentStr: string): void { - if (!this.checkCharacter(fragmentStr, UriRule.ruleUrlc, true)) { + if (!this.checkCharacter(fragmentStr, UriEntry.uriRules.g_ruleUrlcLookup, true)) { this.errStr = "Fragment does not conform to the rule"; return; } - this.uriData.fragment = fragmentStr; + this.fragment = fragmentStr; this.inputUri = this.updateToString(); } private updateAuthority(): void { - let temp = this.uriData.userInfo; - if (this.uriData.userInfo) { + let temp = this.userInfo; + if (this.userInfo) { temp += "@"; } - temp += this.uriData.host; - if (this.uriData.port !== -1) { - temp += `:${this.uriData.port}`; + temp += this.host; + if (this.port !== -1) { + temp += `:${this.port}`; } - this.uriData.authority = temp; + this.authority = temp; } private updateSsp(): void { let temp = ""; - if (this.uriData.authority) { - temp += `//${this.uriData.authority}`; + if (this.authority) { + temp += `//${this.authority}`; } - if (this.uriData.path) { - temp += this.uriData.path.startsWith('/') - ? this.uriData.path - : `/${this.uriData.path}`; + if (this.path) { + temp += this.path.startsWith('/') + ? this.path + : `/${this.path}`; } - if (this.uriData.query) { - temp += `?${this.uriData.query}`; + if (this.query) { + temp += `?${this.query}`; } - this.uriData.SchemeSpecificPart = temp; + this.schemeSpecificPart = temp; } private updateToString(): string { let uriStr = ""; let addQuery = false; - if (this.uriData.scheme) { - uriStr += this.uriData.scheme + ":"; + if (this.scheme) { + uriStr += this.scheme + ":"; } - if (!this.uriData.path) { - uriStr += this.uriData.SchemeSpecificPart; + if (!this.path) { + uriStr += this.schemeSpecificPart; addQuery = true; } else { - if (this.uriData.host) { + if (this.host) { uriStr += "//"; - if (this.uriData.userInfo) { - uriStr += this.uriData.userInfo + "@"; + if (this.userInfo) { + uriStr += this.userInfo + "@"; } - uriStr += this.uriData.host; - if (this.uriData.port !== -1) { - uriStr += ":" + this.uriData.port; + uriStr += this.host; + if (this.port !== -1) { + uriStr += ":" + this.port; } - } else if (this.uriData.authority) { - uriStr += "//" + this.uriData.authority; + } else if (this.authority) { + uriStr += "//" + this.authority; } - if (this.uriData.path) { - uriStr += this.uriData.path.startsWith('/') - ? this.uriData.path - : "/" + this.uriData.path; + if (this.path) { + uriStr += this.path.startsWith('/') + ? this.path + : "/" + this.path; } } - if (this.uriData.query && !addQuery) { - uriStr += "?" + this.uriData.query; + if (this.query && !addQuery) { + uriStr += "?" + this.query; } - if (this.uriData.fragment) { - uriStr += "#" + this.uriData.fragment; + if (this.fragment) { + uriStr += "#" + this.fragment; } return uriStr; } } - class UriRule { - static ruleAlpha = new boolean[MAX_BIT_SIZE]; - static ruleScheme = new boolean[MAX_BIT_SIZE]; - static ruleUrlc = new boolean[MAX_BIT_SIZE]; - static rulePath = new boolean[MAX_BIT_SIZE]; - static ruleUserInfo = new boolean[MAX_BIT_SIZE]; - static ruleDigit = new boolean[MAX_BIT_SIZE]; - static rulePort = new boolean[MAX_BIT_SIZE]; - static { - let digitAggregate = "0123456789"; - UriRule.setBit(UriRule.ruleDigit, digitAggregate); - - let alphasAggregate = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - UriRule.setBit(UriRule.ruleAlpha, alphasAggregate); - - let schemeAggregate = digitAggregate + alphasAggregate + "+-.| _-~!$&=,;'(){}*"; - UriRule.setBit(UriRule.ruleScheme, schemeAggregate); - - let uricAggregate = schemeAggregate + ";/?:@&=$,[]_!~*'()%\""; - UriRule.setBit(UriRule.ruleUrlc, uricAggregate); - let pathAggregate = schemeAggregate + ";/:@&=$,_!~*'()%"; - UriRule.setBit(UriRule.rulePath, pathAggregate); - - let userInfoAggregate = schemeAggregate + ";:&=$,_!~*'()%"; - UriRule.setBit(UriRule.ruleUserInfo, userInfoAggregate); - - let portAggregate = digitAggregate + alphasAggregate + ".:@-;&=+$,-_!~*'()"; - UriRule.setBit(UriRule.rulePort, portAggregate); - } - - private static setBit(arr: boolean[], str: string) { - for (let i = 0; i < arr.length; i++) { - arr[i] = false; - } - for (let i = 0; i < str.length; i++) { - let code = str.charCodeAt(i); - arr[code] = true; - } - } - } } diff --git a/static_core/plugins/ets/sdk/api/@ohos.url.ets b/static_core/plugins/ets/sdk/api/@ohos.url.ets index c28c95208c3d8012fa7a69807b169ae0f28bb4a8..7889230a2e98f36710962a58e2e04d59b45652f1 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.url.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.url.ets @@ -15,30 +15,41 @@ import { BusinessError } from "@ohos.base"; -const TypeErrorCodeId: number = 401; -const SyntaxErrorCodeId: number = 10200002; +const SyntaxErrorCodeId: int = 10200002; -function createBusinessError(code: number, message:string){ +function createBusinessError(code: int, message: string) { let err = new BusinessError(); err.code = code; - err.name='BusinessError'; + err.name = 'BusinessError'; err.message = message; return err; } +/** +* The url module provides utilities for URL resolution and parsing. +* +* @namespace url +*/ export namespace url { + export type UrlCbFn = (value: string, key: string, searchParams: URLParams) => void; + + /** + * Store some constants and arrays + * @class Helper + */ class Helper { static ASCII_32: number = 32; - static ASCII_127: number = 127; + static ASCII_127: int = 127; static specialCharForBit: Array = new Array(128); static head: Map = new Map(); static doubleSegment = new Array("..", ".%2e", ".%2E", "%2e.", "%2E.", "%2e%2e", "%2E%2E", "%2e%2E", "%2E%2e"); - static singlesegment = new Array(".", "%2e", "%2E"); + static singlesegment = new Array('.', "%2e", "%2E"); - static spesicalSymbols = new Array("@", "%40", "#", "%23", "=", "%3D", ":", "%3A", "/", "%2F", ";", "%3B", "?", "%3F"); + static spesicalSymbols: Record = + { "@": "%40", "#": "%23", "=": "%3D", ":": "%3A", "/": "%2F", ";": "%3B", "?": "%3F" }; static specialcharacter = new Array('\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'); @@ -51,7 +62,8 @@ export namespace url { Helper.specialCharForBit[i] = 1; } for (let i = 0; i < specialSymbolsTmp.length; i++) { - Helper.specialCharForBit[specialSymbolsTmp[i].charCodeAt(0)] = 1; + let specialChar = specialSymbolsTmp[i].charCodeAt(0).toInt(); + Helper.specialCharForBit[specialChar] = 1; } Helper.specialCharForBit[Helper.ASCII_127] = 1; @@ -165,7 +177,7 @@ export namespace url { function checkCharacter(input: string) { let inputLen = input.length; for (let i = 0; i < inputLen; i++) { - let charPos: number = input.charCodeAt(i); + let charPos: int = input.charCodeAt(i).toInt(); if (charPos >= 0 && charPos < 128) { if (Helper.specialCharForBit[charPos]) { return false; @@ -176,49 +188,30 @@ export namespace url { } function deleteC0OrSpace(input: string): string { - if (input == "") { - return ""; - } - let i: number = 0; - let strLen = input.length; - let charCodeOfC0 = '\0'.charCodeAt(0); - let charCodeOfSpace = ' '.charCodeAt(0); - while (i < strLen) { - let inputCharCode = input.charCodeAt(i); - if (inputCharCode >= charCodeOfC0 && inputCharCode <= charCodeOfSpace) { - i++; - continue; - } - break; - } - let trimHead = input.substring(i); - strLen = trimHead.length; - if (strLen == 0) { - return ""; - } - for (i = strLen - 1; i >= 0; i--) { - let inputCharCode = trimHead.charCodeAt(i); - if (inputCharCode >= charCodeOfC0 && inputCharCode <= charCodeOfSpace) { - continue; - } - break; - } - if (i < 0) { - return ""; - } - return trimHead.substring(0, i + 1); - } - - function isAlpha(ch: string): boolean { - return ch.length == 1 && ((ch[0] >= c'a' && ch[0] <= c'z') || (ch[0] >= c'A' && ch[0] <= c'Z')); + const codeC0 = '\0'.charCodeAt(0); + const codeSpace = ' '.charCodeAt(0); + let left = 0; + let right = input.length - 1; + while (left <= right && + input.charCodeAt(left) <= codeSpace && + input.charCodeAt(left) >= codeC0) { + left++; + } + while (right >= left && + input.charCodeAt(right) <= codeSpace && + input.charCodeAt(right) >= codeC0) { + right--; + } + return left > right ? "" : input.slice(left, right + 1); } - function isNum(ch: string): boolean { - return ch.length == 1 && (ch[0] >= c'0' && ch[0] <= c'9'); + function deleteTabOrNewline(input: string): string { + let reg = new RegExp('\\t|\\r|\\n', 'g'); + return input.replaceAll(reg, ''); } - function isAlphaNum(ch: string): boolean { - return isAlpha(ch) || isNum(ch); + function isAlphaNum(ch: char): boolean { + return Char.isLetter(ch) || Char.isDecDigit(ch); } function isSpecialScheme(scheme: string) { @@ -226,16 +219,7 @@ export namespace url { } function isFileNotHost(input: string) { - let ch = new Char(input[0]); - if (isAlpha(ch.toString()) && (input[1] == c':' || input[1] == c'|')) { - return true; - } - return false; - } - - function isHexDigit(ch: string): boolean { - let charCode = ch.charCodeAt(0); - return isNum(ch) || (charCode >= c'A' && charCode <= c'F') || (charCode >= c'a' && charCode <= c'f'); + return Char.isLetter(input.charAt(0)) && (input.charAt(1) == c':' || input.charAt(1) == c'|'); } function DecodeSpecialChars(input: string): string { @@ -244,9 +228,9 @@ export namespace url { if (input.length == 0) { return temp; } - let pos: number = temp.indexOf("%"); + let pos: int = temp.indexOf("%").toInt(); while ((pos != -1) && (pos < len - 2)) { - if (isHexDigit(temp.charAt(pos + 1)) && isHexDigit(temp.charAt(pos + 2))) { + if (Char.isHexDigit(temp.charAt(pos.toInt() + 1)) && Char.isHexDigit(temp.charAt(pos.toInt() + 2))) { let subStr = temp.substring(pos + 1, pos + 3); let octNum: number = parseInt(subStr, 16); if (isNaN(octNum)) { @@ -262,57 +246,27 @@ export namespace url { return temp; } - function toHex(num: number): string { - const hexDigits = "0123456789abcdef"; - let hexStr = ""; - while (num > 0) { - const remainder = num % 16; - hexStr = hexDigits[remainder] + hexStr; - num = Math.floor(num / 16); - } - - return hexStr || "0"; - } - function dealIPv4(input: string): string { - let temp = new Array(); - let pos: number = input.lastIndexOf(':'); - let index: number = pos; - let left = pos + 1; - let hexVal = ""; - let val = ""; - while (true) { - pos = input.indexOf(".", left); - if (pos == -1) { - break; - } + const hexSegments = new Array(); + const lastColonPos = input.lastIndexOf(':'); + let currentPosition = lastColonPos + 1; + while (currentPosition < input.length) { + const dotPos = input.indexOf('.', currentPosition); + const segmentEnd = dotPos === -1 ? input.length : dotPos; - val = input.substring(left, pos); - let num = parseInt(val, 10); - if (isNaN(num) && num > 255) { - return val; - } + const segmentValue = input.substring(currentPosition, segmentEnd); + const numericValue = parseInt(segmentValue, 10); - hexVal = toHex(num); - if (hexVal.length == 1) { - hexVal = c'0' + hexVal; + if (isNaN(numericValue) || numericValue > 255 || numericValue < 0) { + return segmentValue; } - temp.push(hexVal); - left = pos + 1; - } - val = input.substring(left); - let num = parseInt(val, 10); - if (isNaN(num) && num > 255) { - return val; - } - hexVal = toHex(num); - if (hexVal.length == 1) { - hexVal = c'0' + hexVal; + + const hexValue = numericValue.toString(16).padStart(2, '0'); + hexSegments.push(hexValue); + currentPosition = segmentEnd + 1; } - temp.push(hexVal); - let res = input.substring(0, index); - res = res + ":" + temp[0] + temp[1] + ":" + temp[2] + temp[3]; - return res; + const prefix = input.substring(0, lastColonPos); + return `${prefix}:${hexSegments[0]}${hexSegments[1]}:${hexSegments[2]}${hexSegments[3]}`; } function formatIPv6(input: string): string { @@ -339,26 +293,6 @@ export namespace url { return input; } - function removeLeadingZeros(inputs: Array) { - let inputSize = inputs.length; - for (let i = 0; i < inputSize; i++) { - let strLen: number = inputs[i].length; - let count: number = 0; - let j = 0; - for (j = 0; j < strLen; j++) { - if (inputs[i][j] != c'0') { - break; - } - count++; - } - if (count == strLen) { - inputs[i] = "0"; - } else if (count != 0) { - inputs[i] = inputs[i].substring(j); - } - } - } - function zeroCompression(inputs: Array): string { let maxIndex: number = 0; let maxSize: number = 0; @@ -397,27 +331,18 @@ export namespace url { } function compress(input: string): string { - let temp = new Array(); - let pos: number = 0; - let left: number = 0; - while (true) { - pos = input.indexOf(":", left); - if (pos == -1) { - break; - } - temp.push(input.substring(left, pos)); - left = pos + 1; - } - temp.push(input.substring(left)); - removeLeadingZeros(temp); - let res: string = zeroCompression(temp); + let array = Array.from( + input.split(':'), + (it) => it.trimLeft([c'0']) + ) + let res: string = zeroCompression(array); return res.toLowerCase(); } function isRadix(input: string, radix: string): boolean { let len = input.length; for (let i = 0; i < len; i++) { - if (radix.indexOf(input[i]) == -1) { + if (radix.indexOf(input.charAt(i)) == -1) { return false; } } @@ -426,14 +351,14 @@ export namespace url { function isNumber(num: string, convension: number[]): boolean { let len: number = num.length; - if (len >= 2 && num[0] == c'0' && (num[1] == c'x' || num[1] == c'X')) { + if (len >= 2 && num.charAt(0) == c'0' && (num.charAt(1) == c'x' || num.charAt(1) == c'X')) { convension[0] = 16; let subStr = num.substring(2); if (subStr.length == 0) { return true; } return isRadix(subStr, "0123456789abcdefABCDEF"); - } else if (len >= 1 && num[0] == c'0') { + } else if (len >= 1 && num.charAt(0) == c'0') { convension[0] = 8; let subStr = num.substring(1); if (subStr.length == 0) { @@ -471,7 +396,7 @@ export namespace url { return num; } - function isFormatIPv4(nums: Array): number { + function getFormatIPv4Index(nums: Array): int { let len = nums.length; for (let i = 0; i < len; i++) { if (nums[i].length > 8) { @@ -505,20 +430,26 @@ export namespace url { return res.slice(0, res.length - 1); } - class UrlInfo { - cScheme: string = ""; - cFragment: string = ""; - cQuery: string = ""; - cPath = new Array(); - cHost: string = ""; - cIsSpecialPath: boolean = false; - cUsername: string = ""; - cPassword: string = ""; - cPort: number = -1; - } - + const INDEX_INVALID = 0; + const INDEX_SPECIAL = 1; + const INDEX_USERNAME = 2; + const INDEX_PASSWORD = 3; + const INDEX_HOST = 4; + const INDEX_PORT = 5; + const INDEX_PATH = 6; + const INDEX_QUERY = 7; + const INDEX_FRAGMENT = 8; + const INDEX_INFOPATH = 9; + const INDEX_IPV6 = 10; + const INDEX_DOUBLE_SLASH = 11; + + + /** + * Handle the specific URL logic + * @class InnerURL + */ class InnerURL { - flags: boolean[] = [false, false, false, false, false, false, false, false, false, false, false, false]; + flags: boolean[] = new boolean[12]; cScheme: string = ""; cFragment: string = ""; cQuery: string = ""; @@ -532,34 +463,32 @@ export namespace url { private ipv4Array: Array = new Array(); analysizeScheme(input: string): boolean { - let ch = new Char(input[0]); - if (!isAlpha(ch.toString())) { - this.flags[0] = true; + if (!Char.isLetter(input.charAt(0))) { + this.flags[INDEX_INVALID] = true; return false; } let sizeLen: number = input.length; for (let i = 1; i < sizeLen - 1; i++) { - let ch1 = new Char(input[i]); - if (!isAlphaNum(ch1.toString()) && input[i] != c'+' && input[i] != c'-' && input[i] != c'.') { - this.flags[0] = true; + if (!isAlphaNum(input.charAt(i)) && input.charAt(i) != c'+' && input.charAt(i) != c'-' && input.charAt(i) != c'.') { + this.flags[INDEX_INVALID] = true; return false; } } this.cScheme = input.toLowerCase(); if (isSpecialScheme(this.cScheme)) { - this.flags[1] = true; + this.flags[INDEX_SPECIAL] = true; } return true; } private analysizeFragment(fragment: string) { this.cFragment = fragment; - this.flags[8] = true; + this.flags[INDEX_FRAGMENT] = true; } private analysizeQuery(query: string) { this.cQuery = query; - this.flags[7] = true; + this.flags[INDEX_QUERY] = true; } private analysizeFilePath(input: string) { @@ -591,15 +520,14 @@ export namespace url { continue; } this.cPath.push(temp[i]); - this.flags[6] = true; + this.flags[INDEX_PATH] = true; } let it = this.cPath[0]; - let ch = new Char(it[0]); - if (isAlpha(ch.toString()) && (it[1] == c':' || it[1] == c'|')) { + if (Char.isLetter(it.charAt(0)) && (it.charAt(1) == c':' || it.charAt(1) == c'|')) { if (it.length == 2) { this.cPath[0] = it[0] + ':'; - this.flags[4] = false; + this.flags[INDEX_HOST] = false; this.cHost = ""; } } @@ -612,7 +540,7 @@ export namespace url { let isFile = isFileNotHost(strHost); if (!isFile) { this.analysizeHost(strHost, special); - } else if (!isFile && this.flags[0]) { + } else if (!isFile && this.flags[INDEX_INVALID]) { return; } if (!isFile) { @@ -625,7 +553,7 @@ export namespace url { private analysizeIPv6Host(input: string) { let regex: RegExp = new RegExp("(::|(:((:[0-9A-Fa-f]{1,4}){1,7}))|(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|:))|(((:(:[0-9A-Fa-f]{1,4}){0,5}:)|(([0-9A-Fa-f]{1,4}:){1}(:[0-9A-Fa-f]{1,4}){0,4}:)|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}:)|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}:)|(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4})?:)|(([0-9A-Fa-f]{1,4}:){5}:)|(([0-9A-Fa-f]{1,4}:){6}))((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)))(%[a-zA-Z0-9._]+)?") if (!regex.test(input)) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } let pos: number = input.indexOf('.'); @@ -635,15 +563,13 @@ export namespace url { input = formatIPv6(input); input = compress(input); this.cHost = "[" + input + "]"; - this.flags[4] = true; - this.flags[10] = true + this.flags[INDEX_HOST] = true; + this.flags[INDEX_IPV6] = true } private removalIPv4(inputStr: string): boolean { - // this.ipv4Array = new Array(); let pos: number = 0; let left: number = 0; - // let inputs = new Array() pos = inputStr.indexOf(".", left); while (pos != -1) { this.ipv4Array.push(inputStr.substring(left, pos)); @@ -670,10 +596,10 @@ export namespace url { for (let i = 0; i < inputSize; i++) { if (res[i] == '') { isIPv4 = false; - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; if (i == inputSize - 1) { res.push(''); - this.flags[0] = false; + this.flags[INDEX_INVALID] = false; } } } @@ -681,8 +607,8 @@ export namespace url { } private formatIPv4(nums: Array) { - let len: number = nums.length; - let index: number = isFormatIPv4(nums); + let len: int = nums.length; + let index: int = getFormatIPv4Index(nums); let res: string = ""; if (index == -1) { for (let i = 0; i < len - 1; i++) { @@ -693,7 +619,7 @@ export namespace url { } res += nums[len - 1]; this.cHost = res; - this.flags[4] = true; + this.flags[INDEX_HOST] = true; } else if (index == (len - 1)) { for (let i = 0; i < (len - 1); i++) { res += nums[i] + "."; @@ -701,16 +627,16 @@ export namespace url { let storeNumber: number[] = [0]; let temp: string = splitNum(nums[index], storeNumber); if (storeNumber[0] + (len - 1) > 4) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } for (let i = 0; i < (4 - (len - 1 + storeNumber[0])); i++) { temp = "0." + temp; } this.cHost = res + temp; - this.flags[4] = true; + this.flags[INDEX_HOST] = true; } else { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; } } @@ -718,10 +644,10 @@ export namespace url { let isIPv4: boolean = false; isIPv4 = this.removalIPv4(input); let temp = this.ipv4Array; - let tempLen: number = temp.length; + let tempLen: int = temp.length; let lastSize: number = temp[tempLen - 1].length; if (isIPv4 && lastSize > 8) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } let res: string = ""; @@ -735,13 +661,13 @@ export namespace url { if (tempLen > 4) { res = res.toLowerCase(); this.cHost = res; - this.flags[4] = true; + this.flags[INDEX_HOST] = true; } else if (tempLen == 4) { - if (isFormatIPv4(temp) == -1) { + if (getFormatIPv4Index(temp) == -1) { this.cHost = res; - this.flags[4] = true; + this.flags[INDEX_HOST] = true; } else { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; } } else { this.formatIPv4(temp); @@ -749,37 +675,37 @@ export namespace url { } else { res = res.toLowerCase(); this.cHost = res; - this.flags[4] = true; + this.flags[INDEX_HOST] = true; } } private analysizeOpaqueHost(input: string) { let inputSize: number = input.length; for (let i = 0; i < inputSize; i++) { - let ch = new Char(input[i]); + let ch = new Char(input.charAt(i)); let result = Helper.specialcharacter.indexOf(ch.toString()); if ((ch != c'%') && (result != -1)) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } } this.cHost = input; - this.flags[4] = true; + this.flags[INDEX_HOST] = true; } analysizeHost(input: string, special: boolean) { if (input.length == 0) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } let inputLength = input.length; - if (input[0] == c'[') { - if (input[inputLength - 1] == c']') { + if (input.charAt(0) == c'[') { + if (input.charAt(inputLength.toInt() - 1) == c']') { input = input.substring(1, inputLength - 1); this.analysizeIPv6Host(input); return; } - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } if (!special) { @@ -788,7 +714,7 @@ export namespace url { } let decodedInput = DecodeSpecialChars(input); if (!checkCharacter(decodedInput)) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } this.analysizeIPv4(decodedInput); @@ -796,7 +722,9 @@ export namespace url { private analysizeFile(input: string) { let special: boolean = true; - if ((input[0] == c'/' || input[0] == c'\\') && (input[1] == c'/' || input[1] == c'\\')) { + if ((input.charAt(0) == c'/' || input.charAt(0) == c'\\') && + (input.charAt(1) == c'/' || input.charAt(1) == c'\\')) + { let temp: string = input.substring(2); let pos: number = 0; if (((pos = temp.indexOf('/')) != -1 || (pos = temp.indexOf('\\')) != -1) && pos == 0) { @@ -805,15 +733,15 @@ export namespace url { } else if (((pos = temp.indexOf('/')) != -1 || (pos = temp.indexOf('\\')) != -1) && pos != 0) { this.analysizeSpecialFile(temp, pos); } else { - if (temp.length != 0 && this.flags[0]) { + if (temp.length != 0 && this.flags[INDEX_INVALID]) { this.analysizeHost(temp, special); - } else if (temp.length != 0 && !this.flags[0]) { + } else if (temp.length != 0 && !this.flags[INDEX_INVALID]) { this.analysizeHost(temp, special); return; } } } else { - if (input[0] == c'/' || input[0] == c'\\') { + if (input.charAt(0) == c'/' || input.charAt(0) == c'\\') { input = input.substring(1); } this.analysizeFilePath(input); @@ -821,9 +749,9 @@ export namespace url { } private analysizeUsernameAndPasswd(strHost: string): string { - let pos: number = strHost.length - 1; + let pos: int = strHost.length - 1; for (; pos >= 0; pos--) { - if (strHost[pos] == c'@') { + if (strHost.charAt(pos.toInt()) == c'@') { break; } } @@ -848,15 +776,15 @@ export namespace url { let keyWord: string = userAndPasswd.substring(position + 1); if (user.length != 0) { this.cUsername = user; - this.flags[2] = true; + this.flags[INDEX_USERNAME] = true; } if (keyWord.length != 0) { this.cPassword = keyWord; - this.flags[3] = true; + this.flags[INDEX_PASSWORD] = true; } } else { this.cUsername = userAndPasswd; - this.flags[2] = true; + this.flags[INDEX_USERNAME] = true; } return strHost; } @@ -866,28 +794,27 @@ export namespace url { return; } for (let i = 0; i < port.length; i++) { - let ch = new Char(port[i]); - if (!isNum(ch.toString())) { - this.flags[0] = true; + if (!Char.isDecDigit(port.charAt(i))) { + this.flags[INDEX_INVALID] = true; return; } } if (port.length >= 6) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } let it: number = parseInt(port); const maxPort: number = 65535; if (it > maxPort) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } - this.flags[5] = true; + this.flags[INDEX_PORT] = true; for (let key of Helper.head.keys()) { let value = Helper.head.get(key); if (key == this.cScheme && value == it) { this.cPort = -1; - this.flags[5] = false; + this.flags[INDEX_PORT] = false; return; } } @@ -908,7 +835,7 @@ export namespace url { if (result != -1) { if (this.cPath.length == 0 && it == (tempLen - 1)) { this.cPath.push(""); - this.flags[6] = true; + this.flags[INDEX_PATH] = true; } if (this.cPath.length == 0) { continue; @@ -916,7 +843,7 @@ export namespace url { this.cPath.pop(); if (it == (tempLen - 1)) { this.cPath.push(""); - this.flags[6] = true; + this.flags[INDEX_PATH] = true; } continue; } @@ -924,21 +851,21 @@ export namespace url { result = Helper.singlesegment.indexOf(temp[it]); if (result != -1 && it == (tempLen - 1)) { this.cPath.push(""); - this.flags[6] = true; + this.flags[INDEX_PATH] = true; continue; } if (result == -1) { this.cPath.push(temp[it]); - this.flags[6] = true; + this.flags[INDEX_PATH] = true; } } } private parsingHostAndPath(input: string, pos: number) { let special: boolean = true; - let inputLen: number = input.length; + let inputLen: int = input.length; for (pos = 0; pos < inputLen; pos++) { - if (input[pos] == c'/' || input[pos] == c'\\') { + if (input.charAt(pos.toInt()) == c'/' || input.charAt(pos.toInt()) == c'\\') { break; } } @@ -948,10 +875,10 @@ export namespace url { strHost = this.analysizeUsernameAndPasswd(strHost); } if (strHost.length == 0) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } - if (strHost[strHost.length - 1] != c']' && (strHost.lastIndexOf(':') != -1)) { + if (strHost.charAt(strHost.length.toInt() - 1) != c']' && (strHost.lastIndexOf(':') != -1)) { pos = strHost.lastIndexOf(':'); if (pos != -1) { let port: string = strHost.substring(pos + 1); @@ -959,7 +886,9 @@ export namespace url { this.analysizePort(port); } } - if (strHost[strHost.length - 1] != c']' && strHost.lastIndexOf(':') != -1 && this.flags[0]) { + if (strHost.charAt(strHost.length.toInt() - 1) != c']' && strHost.lastIndexOf(':') != -1 && + this.flags[INDEX_INVALID]) + { return; } this.analysizeHost(strHost, special); @@ -972,16 +901,16 @@ export namespace url { strHost = this.analysizeUsernameAndPasswd(strHost); } if (strHost.length == 0) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } - if (strHost[strHost.length - 1] != c']') { + if (strHost.charAt(strHost.length.toInt() - 1) != c']') { if ((pos = strHost.lastIndexOf(':')) != -1) { let port: string = strHost.substring(pos + 1); strHost = strHost.substring(0, pos); this.analysizePort(port); } - if ((pos = strHost.lastIndexOf(':')) != -1 && this.flags[0]) { + if ((pos = strHost.lastIndexOf(':')) != -1 && this.flags[INDEX_INVALID]) { return; } } @@ -991,7 +920,7 @@ export namespace url { private analysizeFilescheme(input: string) { let strPath: string = this.cScheme + input; this.cScheme = "file:"; - this.flags[1] = true; + this.flags[INDEX_SPECIAL] = true; this.analysizeFilePath(strPath); } @@ -1000,18 +929,18 @@ export namespace url { input = this.analysizeUsernameAndPasswd(input); } if (input.length == 0) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; } return input; } private analysizeHostPath(input: string): string { let pos: number = 0; - if (input[input.length - 1] != c']' && (pos = input.lastIndexOf(':')) != -1) { + if (input.charAt(input.length.toInt() - 1) != c']' && (pos = input.lastIndexOf(':')) != -1) { let port: string = input.substring(pos + 1); input = input.substring(0, pos); this.analysizePort(port); - if (this.flags[0]) { + if (this.flags[INDEX_INVALID]) { return input; } } @@ -1019,12 +948,12 @@ export namespace url { } private analysizeInfoPath(input: string) { - this.flags[9] = true; + this.flags[INDEX_INFOPATH] = true; if (this.cPath.length == 0) { this.cPath.push(""); } this.cPath[0] = input; - this.flags[6] = true; + this.flags[INDEX_PATH] = true; return; } @@ -1033,7 +962,7 @@ export namespace url { this.analysizeFilescheme(input); return; } - if (input[0] == c'/' && input[1] == c'/' && input[2] != c'/') { + if (input.charAt(0) == c'/' && input.charAt(1) == c'/' && input.charAt(2) != c'/') { let hostAndPath: string = input.substring(2); if (hostAndPath.length == 0) { return; @@ -1049,16 +978,18 @@ export namespace url { strHost = this.analysizeUsernameAndPasswd(strHost); } if (strHost.length == 0) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } let pos: number = 0; - if (strHost[strHost.length - 1] != c']' && (pos = strHost.lastIndexOf(':')) != -1) { + if (strHost.charAt(strHost.length.toInt() - 1) != c']' && (pos = strHost.lastIndexOf(':')) != -1) { let port: string = strHost.substring(pos + 1); strHost = strHost.substring(0, pos); this.analysizePort(port); } - if (strHost[strHost.length - 1] != c']' && (pos = strHost.lastIndexOf(':')) != -1 && this.flags[0]) { + if (strHost.charAt(strHost.length.toInt() - 1) != c']' && (pos = strHost.lastIndexOf(':')) != -1 && + this.flags[INDEX_INVALID]) + { return; } this.analysizeHost(strHost, special); @@ -1069,7 +1000,7 @@ export namespace url { strHost = this.analysizeHostPath(strHost); this.analysizeHost(strHost, special); } - } else if (input[0] == c'/' && input[1] == c'/') { + } else if (input.charAt(0) == c'/' && input.charAt(1) == c'/') { let strOfPath = input.substring(1); this.analysizePath(strOfPath, false); } else { @@ -1078,11 +1009,11 @@ export namespace url { } private analysizeHostAndPath(input: string) { - if (this.flags[1]) { - let pos: number = 0; - let inputLen: number = input.length; + if (this.flags[INDEX_SPECIAL]) { + let pos: int = 0; + let inputLen: int = input.length; while (pos < inputLen) { - if (input[pos] == c'/' || input[pos] == c'\\') { + if (input.charAt(pos.toInt()) == c'/' || input.charAt(pos.toInt()) == c'\\') { pos++; continue; } @@ -1090,7 +1021,7 @@ export namespace url { } input = input.substring(pos); if (input.length == 0) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return; } else if (input.indexOf('/') != -1 || input.indexOf('\\') != -1) { this.parsingHostAndPath(input, pos); @@ -1100,9 +1031,9 @@ export namespace url { } else { let inputLen: number = input.length; if (inputLen > 0) { - this.cIsSpecialPath = input[0] != c'/' ? true : false; + this.cIsSpecialPath = input.charAt(0) != c'/'; } - if (this.flags[11] && inputLen == 2) { + if (this.flags[INDEX_DOUBLE_SLASH] && inputLen == 2) { return; } this.analysizeNoDefaultProtocol(input); @@ -1111,14 +1042,14 @@ export namespace url { initOnlyInput(input: string): string { if (input.length == 0) { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; return input; } let tempInput = input; if (tempInput.indexOf(':') != -1) { - let pos = tempInput.indexOf(':'); + let pos = tempInput.indexOf(':').toInt(); pos++; - this.flags[11] = (input[pos] == c'/' && input[pos + 1] == c'/'); + this.flags[INDEX_DOUBLE_SLASH] = (input.charAt(pos.toInt()) == c'/' && input.charAt(pos.toInt() + 1) == c'/'); let scheme: string = tempInput.substring(0, pos); if (!this.analysizeScheme(scheme)) { return tempInput; @@ -1142,7 +1073,7 @@ export namespace url { this.analysizeHostAndPath(restInput); } } else { - this.flags[0] = true; + this.flags[INDEX_INVALID] = true; } return tempInput; @@ -1162,33 +1093,33 @@ export namespace url { this.analysizeQuery(query); input = input.substring(0, pos); } - let special: boolean = this.flags[1] ? true : false; - // let pathStr: string = input; + let special: boolean = this.flags[INDEX_SPECIAL]; this.analysizePath(input, special); return input; } toolHasBase(input: string, strInput: string): string { - if (input.length != 0 && input[0] == c'/') { + if (input.length != 0 && input.charAt(0) == c'/') { strInput = input.substring(1); return this.analysizeInput(strInput); - } else if (input.length != 0 && input[0] != c'/') { + } else if (input.length != 0 && input.charAt(0) != c'/') { return this.analysizeInput(strInput); } return strInput; } parseSingleUrl(inputUrl: string): string { - let trimedStr = deleteC0OrSpace(inputUrl); - return this.initOnlyInput(trimedStr); + inputUrl = deleteC0OrSpace(inputUrl); + inputUrl = deleteTabOrNewline(inputUrl); + return this.initOnlyInput(inputUrl); } - constructor() { } + constructor() {} getHostName(): string { let temp = ""; - if (this.flags[4]) { - if (!this.flags[10]) { + if (this.flags[INDEX_HOST]) { + if (!this.flags[INDEX_IPV6]) { temp = encodePercentEncoding(this.cHost, Helper.URL_ENCODED_PERCENT_SIGN_CHARS); } else { temp = this.cHost; @@ -1198,13 +1129,16 @@ export namespace url { } setHostName(input: string) { - if (this.flags[9]) { + if (this.flags[INDEX_INFOPATH]) { return; } let strHost: string = input; let len: number = strHost.length; for (let pos = 0; pos < len; pos++) { - if ((strHost[pos] == c':') || (strHost[pos] == c'?') || (strHost[pos] == c'#') || (strHost[pos] == c'/' || strHost[pos] == c'\\')) { + if ((strHost.charAt(pos.toInt()) == c':') || (strHost.charAt(pos.toInt()) == c'?') || + (strHost.charAt(pos.toInt()) == c'#') || + (strHost.charAt(pos.toInt()) == c'/' || strHost.charAt(pos.toInt()) == c'\\')) + { strHost = strHost.substring(0, pos); break; } @@ -1215,43 +1149,40 @@ export namespace url { let special: boolean = Helper.head.has(this.cScheme); let tempUrl: InnerURL = new InnerURL(); tempUrl.analysizeHost(strHost, special); - if (tempUrl.flags[4]) { + if (tempUrl.flags[INDEX_HOST]) { if (this.cScheme == "file:" && (tempUrl.cHost == "localhost")) { tempUrl.cHost = ""; } this.cHost = tempUrl.cHost; - this.flags[4] = true; + this.flags[INDEX_HOST] = true; } } setUserName(input: string) { if (input.length == 0) { this.cUsername = ""; - this.flags[2] = false; + this.flags[INDEX_USERNAME] = false; } else { let username: string = encodePercentEncoding(input, Helper.USERINFO_PERCENT_SIGN_CHARS); - let len: number = Helper.spesicalSymbols.length - 2; - for (let i = 0; i <= len; i += 2) { - // ReplaceSpecialSymbols - username = username.replaceAll(Helper.spesicalSymbols[i], Helper.spesicalSymbols[i + 1]); - } + Helper.spesicalSymbols.forEach((value: string, key: string) => { + username = username.replaceAll(key, value); + }); this.cUsername = username; - this.flags[2] = true; + this.flags[INDEX_USERNAME] = true; } } setPassword(input: string) { if (input.length == 0) { this.cPassword = ""; - this.flags[3] = false; + this.flags[INDEX_PASSWORD] = false; } else { let keyWord: string = encodePercentEncoding(input, Helper.USERINFO_PERCENT_SIGN_CHARS); - let len: number = Helper.spesicalSymbols.length - 2; - for (let i = 0; i <= len; i += 2) { - keyWord = keyWord.replaceAll(Helper.spesicalSymbols[i], Helper.spesicalSymbols[i + 1]); - } + Helper.spesicalSymbols.forEach((value: string, key: string) => { + keyWord = keyWord.replaceAll(key, value); + }); this.cPassword = keyWord; - this.flags[3] = true; + this.flags[INDEX_PASSWORD] = true; } } @@ -1259,13 +1190,13 @@ export namespace url { let strInput: string = input; let special: boolean = Helper.head.has(this.cScheme); let inputIsSpecial: boolean = Helper.head.has(input); - if ((special != inputIsSpecial) || ((input == "file") && (this.flags[2] || this.flags[3] || this.flags[5]))) { + if ((special != inputIsSpecial) || ((input == "file") && (this.flags[INDEX_USERNAME] || this.flags[INDEX_PASSWORD] || this.flags[INDEX_PORT]))) { return; } let tempUrl: InnerURL = new InnerURL(); if (tempUrl.analysizeScheme(strInput)) { - if (tempUrl.flags[1]) { - this.flags[1] = true; + if (tempUrl.flags[INDEX_SPECIAL]) { + this.flags[INDEX_SPECIAL] = true; } this.cScheme = tempUrl.cScheme; } @@ -1275,9 +1206,9 @@ export namespace url { let temp: string = ""; if (input.length == 0) { this.cFragment = ""; - this.flags[8] = false; + this.flags[INDEX_FRAGMENT] = false; } else { - if (input[0] != c'#') { + if (input.charAt(0) != c'#') { temp = '#'; temp += encodePercentEncoding(input, Helper.FRAGMENT_PERCENT_SIGN_CHARS); } else { @@ -1291,9 +1222,9 @@ export namespace url { let temp: string = ""; if (input.length == 0) { this.cQuery = ""; - this.flags[7] = false; + this.flags[INDEX_QUERY] = false; } else { - if (input[0] != c'?') { + if (input.charAt(0) != c'?') { temp = "?"; temp += input; } else { @@ -1311,7 +1242,7 @@ export namespace url { } setHost(input: string) { - if (input.length == 0 || this.flags[9]) { + if (input.length == 0 || this.flags[INDEX_INFOPATH]) { return; } let strHost: string = input; @@ -1319,9 +1250,11 @@ export namespace url { // splitString let strLen: number = input.length; for (let pos = 0; pos < strLen; pos++) { - if ((input[pos] == c':') || (input[pos] == c'?') || (input[pos] == c'#') || (input[pos] == c'/') || (input[pos] == c'\\')) { + if ((input.charAt(pos.toInt()) == c':') || (input.charAt(pos.toInt()) == c'?') || + (input.charAt(pos.toInt()) == c'#') || (input.charAt(pos.toInt()) == c'/') || (input.charAt(pos.toInt()) == c'\\')) + { strHost = input.substring(0, pos); - if (input[pos] == c':') { + if (input.charAt(pos.toInt()) == c':') { pos++; port = input.substring(pos); } @@ -1335,19 +1268,21 @@ export namespace url { let special: boolean = Helper.head.has(this.cScheme); let tempUrl: InnerURL = new InnerURL(); tempUrl.analysizeHost(strHost, special); - if (tempUrl.flags[4]) { + if (tempUrl.flags[INDEX_HOST]) { if (this.cScheme == "file:" && tempUrl.cHost == "localhost") { tempUrl.cHost = ""; } this.cHost = tempUrl.cHost; - this.flags[4] = true; + this.flags[INDEX_HOST] = true; } else { return; } if (port.length > 0) { let portLen: number = port.length; for (let pos = 0; pos < portLen; pos++) { - if (port[pos] == c'?' || port[pos] == c'#' || port[pos] == c'/' || port[pos] == c'\\') { + if (port.charAt(pos.toInt()) == c'?' || port.charAt(pos.toInt()) == c'#' || + port.charAt(pos.toInt()) == c'/' || port.charAt(pos.toInt()) == c'\\') + { port = port.substring(0, pos); break; } @@ -1355,8 +1290,8 @@ export namespace url { if (port.length > 0) { let tempPortUrl: InnerURL = new InnerURL(); tempPortUrl.analysizePort(port); - if (tempPortUrl.flags[5]) { - this.flags[5] = true; + if (tempPortUrl.flags[INDEX_PORT]) { + this.flags[INDEX_PORT] = true; this.cPort = tempPortUrl.cPort; } } @@ -1367,7 +1302,9 @@ export namespace url { let port: string = input; let portLen: number = port.length; for (let pos = 0; pos < portLen; pos++) { - if ((port[pos] == c'?') || (port[pos] == c'#') || (port[pos] == c'/') || (port[pos] == c'\\')) { + if ((port.charAt(pos.toInt()) == c'?') || (port.charAt(pos.toInt()) == c'#') || + (port.charAt(pos.toInt()) == c'/') || (port.charAt(pos.toInt()) == c'\\')) + { port = port.substring(0, pos); break; } @@ -1375,8 +1312,8 @@ export namespace url { if (port.length > 0) { let tempUrl: InnerURL = new InnerURL(); tempUrl.analysizePort(port); - if (tempUrl.flags[5]) { - this.flags[5] = true; + if (tempUrl.flags[INDEX_PORT]) { + this.flags[INDEX_PORT] = true; this.cPort = tempUrl.cPort; } } @@ -1388,7 +1325,7 @@ export namespace url { let newUrl: InnerURL = new InnerURL(); newUrl.initOnlyInput(str); this.flags = newUrl.flags; - if (!newUrl.flags[0]) { + if (!newUrl.flags[INDEX_INVALID]) { this.flags = newUrl.flags; this.cFragment = newUrl.cFragment; this.cScheme = newUrl.cScheme; @@ -1404,7 +1341,7 @@ export namespace url { setPath(input: string) { let strPath: string = encodePercentEncoding(input, Helper.PATH_PERCENT_SIGN_CHARS); - if (this.flags[9] || strPath.length == 0) { + if (this.flags[INDEX_INFOPATH] || strPath.length == 0) { return; } let oldStr: string = "%3A"; @@ -1413,57 +1350,57 @@ export namespace url { let special: boolean = Helper.head.has(this.cScheme); if (this.cScheme == "file:") { let tempUrl: InnerURL = new InnerURL(); - if ((strPath[0] == c'/') || (strPath[0] == c'\\' && this.flags[1])) { + if ((strPath.charAt(0) == c'/') || (strPath.charAt(0) == c'\\' && this.flags[INDEX_SPECIAL])) { strPath = strPath.substring(1); } tempUrl.analysizeFilePath(strPath); - if (tempUrl.flags[6]) { + if (tempUrl.flags[INDEX_PATH]) { this.cPath = tempUrl.cPath; - this.flags[6] = true; + this.flags[INDEX_PATH] = true; } } else { let tempUrl: InnerURL = new InnerURL(); - if ((strPath[0] == c'/') || (strPath[0] == c'\\' && this.flags[1])) { + if ((strPath.charAt(0) == c'/') || (strPath.charAt(0) == c'\\' && this.flags[INDEX_SPECIAL])) { strPath = strPath.substring(1); } tempUrl.analysizePath(strPath, special); - if (tempUrl.flags[6]) { + if (tempUrl.flags[INDEX_PATH]) { this.cPath = tempUrl.cPath; - this.flags[6] = true; + this.flags[INDEX_PATH] = true; } } } getSearch(): string { - if (this.flags[7] && (this.cQuery.length != 1)) { + if (this.flags[INDEX_QUERY] && (this.cQuery.length != 1)) { return this.cQuery; } return ""; } getEncodeSearch(): string { - if (this.flags[7] && (this.cQuery.length != 1)) { + if (this.flags[INDEX_QUERY] && (this.cQuery.length != 1)) { return encodePercentEncoding(this.cQuery, Helper.QUERY_PERCENT_SIGN_CHARS); } return ""; } getUsername(): string { - if (this.flags[2]) { + if (this.flags[INDEX_USERNAME]) { return encodePercentEncoding(this.cUsername, Helper.USERINFO_PERCENT_SIGN_CHARS); } return ""; } getPassword(): string { - if (this.flags[3]) { + if (this.flags[INDEX_PASSWORD]) { return encodePercentEncoding(this.cPassword, Helper.USERINFO_PERCENT_SIGN_CHARS); } return ""; } getFragment(): string { - if (this.flags[8] && (this.cFragment.length != 1)) { + if (this.flags[INDEX_FRAGMENT] && (this.cFragment.length != 1)) { return encodePercentEncoding(this.cFragment, Helper.FRAGMENT_PERCENT_SIGN_CHARS); } return ""; @@ -1481,7 +1418,7 @@ export namespace url { if (this.cIsSpecialPath) { temp = ""; } - if (this.flags[6]) { + if (this.flags[INDEX_PATH]) { let len: number = this.cPath.length; for (let i = 0; i < len; i++) { if (i < len - 1) { @@ -1501,7 +1438,7 @@ export namespace url { } getPort(): string { - if (this.flags[5]) { + if (this.flags[INDEX_PORT]) { return Number.toString(this.cPort); } return ""; @@ -1509,53 +1446,54 @@ export namespace url { getHost(): string { let temp: string = this.cHost; - if (this.flags[5]) { + if (this.flags[INDEX_PORT]) { temp += ":"; temp += Number.toString(this.cPort); } - if (!this.flags[10]) { + if (!this.flags[INDEX_IPV6]) { temp = encodePercentEncoding(temp, Helper.URL_ENCODED_PERCENT_SIGN_CHARS); } return temp; } getIsIpv6(): boolean { - return this.flags[10] ? true : false; + return this.flags[INDEX_IPV6]; } hasDoubleSlash(): boolean { - return this.flags[11]; + return this.flags[INDEX_DOUBLE_SLASH]; } + } function baseIntoUrl(base: InnerURL, res: InnerURL, inputIsEmpty: boolean) { res.cScheme = base.cScheme; - res.flags[1] = base.flags[1]; + res.flags[INDEX_SPECIAL] = base.flags[INDEX_SPECIAL]; res.cHost = base.cHost; - res.flags[4] = true + res.flags[INDEX_HOST] = true res.cUsername = base.cUsername; - res.flags[2] = base.flags[2]; + res.flags[INDEX_USERNAME] = base.flags[INDEX_USERNAME]; res.cPassword = base.cPassword; - res.flags[3] = base.flags[3]; + res.flags[INDEX_PASSWORD] = base.flags[INDEX_PASSWORD]; res.cPort = base.cPort; - res.flags[5] = base.flags[5]; + res.flags[INDEX_PORT] = base.flags[INDEX_PORT]; if (inputIsEmpty) { res.cPath = base.cPath; - res.flags[6] = base.flags[6]; + res.flags[INDEX_PATH] = base.flags[INDEX_PATH]; res.cQuery = base.cQuery; - res.flags[7] = base.flags[7]; + res.flags[INDEX_QUERY] = base.flags[INDEX_QUERY]; res.cFragment = base.cFragment; - res.flags[8] = base.flags[8]; + res.flags[INDEX_FRAGMENT] = base.flags[INDEX_FRAGMENT]; } - res.flags[9] = base.flags[9]; - res.flags[10] = base.flags[10]; + res.flags[INDEX_INFOPATH] = base.flags[INDEX_INFOPATH]; + res.flags[INDEX_IPV6] = base.flags[INDEX_IPV6]; } function shorteningPath(resUrl: InnerURL, baseUrl: InnerURL, isFile: boolean) { @@ -1567,8 +1505,9 @@ export namespace url { return; } - let ch = new Char(baseUrl.cPath[0].charAt(0)); - if ((baseUrl.cPath.length == 1) && isFile && isAlpha(ch.toString()) && (baseUrl.cPath[0].charAt(1) == c':')) { + let ch: char = baseUrl.cPath[0].charAt(0); + let ch1: char = baseUrl.cPath[0].charAt(1); + if ((baseUrl.cPath.length == 1) && isFile && Char.isLetter(ch) && ch1 == c':') { return; } baseUrl.cPath.pop(); @@ -1605,9 +1544,8 @@ export namespace url { } function containIllegalCode(str: string): Boolean { - const unpairedSurrogateRe = new RegExp("(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])"); - const regex = new RegExp(unpairedSurrogateRe); - return regex.test(str) + const regex = new RegExp("(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])"); + return regex.test(str); } function fixIllegalString(str: string): string { @@ -1618,6 +1556,11 @@ export namespace url { } } + /** + * The interface of URL is used to parse, construct, normalize, and encode URLs. + * + * @class URL + */ export class URL { urlInner: InnerURL = new InnerURL(); @@ -1635,37 +1578,64 @@ export namespace url { cPort: string = ''; cHasDoubleSlash: boolean = false; cParams: URLParams = new URLParams(); - - constructor() { } - + /** + * URL constructor, which is used to instantiate a URL object. + * + */ + constructor() {} + + /** + * Replaces the original constructor to process arguments and return a url object. + * + * @param { string } inputUrl - Absolute or relative input URL to resolve. + * Base is required if input is relative. + * If input is an absolute value, base ignores the value. + * @param { string | URL } [base] - Base URL to parse if input is not absolute. + * @returns { URL } + * @throws { BusinessError } 10200002 - Invalid url string. + */ static parseURL(inputUrl: string, inputBase?: string | URL): URL { let nativeUrl: URL; - - if (!inputBase) { + if (inputBase == undefined) { nativeUrl = URL.parseSingleInput(fixIllegalString(inputUrl)); } else { - if (typeof inputBase == 'string') { - let inputBaseStr = inputBase as string; - nativeUrl = URL.parseBaseInput(fixIllegalString(inputUrl), fixIllegalString(inputBaseStr)); - } else { - nativeUrl = URL.parseBaseUrl(fixIllegalString(inputUrl), inputBase as URL); + if (inputBase instanceof string) { + nativeUrl = URL.parseBaseInput(fixIllegalString(inputUrl), fixIllegalString(inputBase)); + } + if (inputBase instanceof URL) { + nativeUrl = URL.parseBaseUrl(fixIllegalString(inputUrl), inputBase); } } - if (nativeUrl.urlInner.flags[0]) { - throw createBusinessError(SyntaxErrorCodeId,'Syntax Error. Invalid Url string.'); + if (nativeUrl!.urlInner.flags[INDEX_INVALID]) { + throw createBusinessError(SyntaxErrorCodeId, 'Syntax Error. Invalid Url string.'); } - setEncodedData(nativeUrl, nativeUrl.urlInner); - return nativeUrl; + setEncodedData(nativeUrl!, nativeUrl!.urlInner); + return nativeUrl!; } + /** + * Returns the serialized URL as a string. + * + * @returns { string } Returns the serialized URL as a string. + */ toString(): string { return this.cHref; } + /** + * Gets the protocol portion of the URL. + * + * @return {string} Returns the protocol portion of the URL. + */ get protocol(): string { return this.cProtocol; } + /** + * Sets the protocol portion of the URL. + * + * @param { string } scheme - protocol portion of the URL. + */ set protocol(scheme: string) { if (scheme.length == 0) { return; @@ -1680,6 +1650,11 @@ export namespace url { this.setHref(); } + /** + * Gets the origin portion of the URL. + * + * @return {string} Returns the origin portion of the URL. + */ get origin(): string { let kOpaqueOrigin: string = 'null'; switch (this.cProtocol) { @@ -1694,10 +1669,21 @@ export namespace url { return kOpaqueOrigin; } + /** + * Gets the username portion of the URL. + * + * @return {string} Returns the username portion of the URL. + * @type { string } + */ get username(): string { return this.cUsername; } + /** + * Sets the username portion of the URL. + * + * @param { string } input - The username portion of the URL. + */ set username(input: string) { if (this.cHost == null || this.cHost == '' || this.cProtocol == 'file:') { return; @@ -1707,10 +1693,20 @@ export namespace url { this.setHref(); } + /** + * Gets the password portion of the URL. + * + * @return {string} Returns the password portion of the URL. + */ get password(): string { return this.cPassword; } + /** + * Sets the password portion of the URL. + * + * @param { string } input - The password portion of the URL. + */ set password(input: string) { if (this.cHost == null || this.cHost == '' || this.cProtocol == 'file:') { return; @@ -1720,20 +1716,40 @@ export namespace url { this.setHref(); } + /** + * Gets the serialized URL as a string. + * + * @return {string} Returns the serialized URL as a string. + */ get hash(): string { return this.cHash; } + /** + * Sets the hash portion of the URL. + * + * @param { string } fragment - The hash portion of the URL. + */ set hash(fragment: string) { this.urlInner.setFragment(fixIllegalString(fragment)); this.cHash = this.urlInner.getFragment(); this.setHref(); } + /** + * Gets the search portion of the URL. + * + * @return {string} Returns the search portion of the URL. + */ get search(): string { return this.cSearch; } + /** + * Sets the search portion of the URL. + * + * @param { string } query - The search portion of the URL. + */ set search(query: string) { this.urlInner.setEncodeSearch(fixIllegalString(query)); this.cSearch = this.urlInner.getEncodeSearch(); @@ -1741,10 +1757,20 @@ export namespace url { this.setHref(); } + /** + * Gets the hostname portion of the URL. + * + * @return {string} Returns the hostname portion of the URL. + */ get hostname(): string { return this.cHostname; } + /** + * Sets the hostname portion of the URL. + * + * @param { string } hostname - The hostname portion of the URL. + */ set hostname(hostname: string) { this.urlInner.setHostName(hostname); this.cHostname = this.urlInner.getHostName(); @@ -1752,10 +1778,20 @@ export namespace url { this.setHref(); } + /** + * Gets the host portion of the URL. + * + * @return {string} Returns the host portion of the URL. + */ get host(): string { return this.cHost; } + /** + * Sets the host portion of the URL. + * + * @param { string } host - The host portion of the URL. + */ set host(host: string) { this.urlInner.setHost(host); this.cHost = this.urlInner.getHost(); @@ -1764,10 +1800,20 @@ export namespace url { this.setHref(); } + /** + * Gets the port portion of the URL. + * + * @return {string} Returns the port portion of the URL. + */ get port(): string { return this.cPort; } + /** + * Sets the port portion of the URL. + * + * @param { string } port - The port portion of the URL. + */ set port(port: string) { if (this.cHost == '' || this.cProtocol == 'file:' || port == '') { return; @@ -1778,34 +1824,64 @@ export namespace url { this.setHref(); } + /** + * Gets the pathname portion of the URL. + * + * @return {string} Returns the pathname portion of the URL. + */ get pathname(): string { return this.cPathname; } + /** + * Sets the pathname portion of the URL. + * + * @param { string } path - The pathname portion of the URL. + */ set pathname(path: string) { this.urlInner.setPath(fixIllegalString(path)); this.cPathname = this.urlInner.getPath(); this.setHref(); } + /** + * Gets the params portion of the URL. + * + * @return {URLParams} Returns the params portion of the URL. + */ get params(): URLParams { return this.cParams; } + /** + * Returns the serialized URL as a string. + * + * @returns { string } Returns the serialized URL as a string. + */ toJSON(): string { return this.cHref; } + /** + * Gets the href portion of the URL. + * + * @return {string} Returns the href portion of the URL. + */ get href(): string { return this.cHref; } + /** + * Sets the href portion of the URL. + * + * @param { string } href - The href portion of the URL. + */ set href(href: string) { this.urlInner.setHref(href); - if (!this.urlInner.flags[0]) { + if (!this.urlInner.flags[INDEX_INVALID]) { setEncodedData(this, this.urlInner); } else { - throw createBusinessError(SyntaxErrorCodeId,'Syntax Error. Invalid Url string.'); + throw createBusinessError(SyntaxErrorCodeId, 'Syntax Error. Invalid Url string.'); } } @@ -1851,53 +1927,52 @@ export namespace url { } static parseBaseInput(input: string, base: string): URL { + if (base.length == 0) { + throw createBusinessError(SyntaxErrorCodeId, 'Parameter error. The type of must be string') + } let result = new URL(); let possibleBaseURL = new InnerURL(); let resUrl = new InnerURL(); let strInput = input; - if (base.length == 0) { - possibleBaseURL.flags[0] = true - } possibleBaseURL.parseSingleUrl(base); - if (possibleBaseURL.flags[0]) { - resUrl.flags[0] = true + if (possibleBaseURL.flags[INDEX_INVALID]) { + resUrl.flags[INDEX_INVALID] = true result.urlInner = resUrl; return result; - } else if (!possibleBaseURL.flags[0]) { - strInput = resUrl.parseSingleUrl(strInput); - if (!resUrl.flags[0]) { - result.urlInner = resUrl; - return result; - } - if ((input[0] == c'/') && (input[1] == c'/' || (input[1] == c'\\' && possibleBaseURL.flags[1]))) { - let newInput: string = possibleBaseURL.cScheme + input; - resUrl.flags[0] = false - resUrl.parseSingleUrl(newInput); - result.urlInner = resUrl; - return result; - } - if (!possibleBaseURL.flags[9]) { - resUrl.flags[0] = false - baseIntoUrl(possibleBaseURL, resUrl, input.length == 0); - strInput = resUrl.toolHasBase(input, strInput); - if (input.length != 0 && input[0] != c'/' && resUrl.cPath.length == 0) { - resUrl.cPath = possibleBaseURL.cPath; - resUrl.flags[6] = possibleBaseURL.flags[6]; - } - if (input.length != 0 && input[0] != c'/' && resUrl.cPath.length != 0) { - let isFile: boolean = ((resUrl.cScheme == "file:") ? true : false); - shorteningPath(resUrl, possibleBaseURL, isFile); - let basePathStr: string = basePathToString(possibleBaseURL); - basePathStr == "" ? basePathStr = strInput : basePathStr += "/" + strInput; - resUrl.cPath = new Array(); - resUrl.analysizeInput(basePathStr); - resUrl.flags[6] = true - } - } else if (possibleBaseURL.flags[9]) { - resUrl.flags[0] = true - result.urlInner = resUrl; - return result; + } + strInput = resUrl.parseSingleUrl(strInput); + if (!resUrl.flags[INDEX_INVALID]) { + result.urlInner = resUrl; + return result; + } + if ((input.charAt(0) == c'/') && (input.charAt(1) == c'/' || (input.charAt(1) == c'\\' && possibleBaseURL.flags[INDEX_SPECIAL]))) { + let newInput: string = possibleBaseURL.cScheme + input; + resUrl.flags[INDEX_INVALID] = false + resUrl.parseSingleUrl(newInput); + result.urlInner = resUrl; + return result; + } + if (!possibleBaseURL.flags[INDEX_INFOPATH]) { + resUrl.flags[INDEX_INVALID] = false + baseIntoUrl(possibleBaseURL, resUrl, input.length == 0); + strInput = resUrl.toolHasBase(input, strInput); + if (input.length != 0 && input.charAt(0) != c'/' && resUrl.cPath.length == 0) { + resUrl.cPath = possibleBaseURL.cPath; + resUrl.flags[INDEX_PATH] = possibleBaseURL.flags[INDEX_PATH]; + } + if (input.length != 0 && input.charAt(0) != c'/' && resUrl.cPath.length != 0) { + let isFile: boolean = resUrl.cScheme == "file:"; + shorteningPath(resUrl, possibleBaseURL, isFile); + let basePathStr: string = basePathToString(possibleBaseURL); + basePathStr == "" ? basePathStr = strInput : basePathStr += '/' + strInput; + resUrl.cPath = new Array(); + resUrl.analysizeInput(basePathStr); + resUrl.flags[INDEX_PATH] = true } + } else if (possibleBaseURL.flags[INDEX_INFOPATH]) { + resUrl.flags[INDEX_INVALID] = true + result.urlInner = resUrl; + return result; } result.urlInner = resUrl; return result; @@ -1909,38 +1984,38 @@ export namespace url { let resUrl: InnerURL = new InnerURL(); let strInput: string = input; resUrl.parseSingleUrl(strInput); - if (!resUrl.flags[0]) { + if (!resUrl.flags[INDEX_INVALID]) { result.urlInner = resUrl; return result; } if (input.length != 0) { - if ((input[0] == c'/') && (input[1] == c'/' || (input[1] == c'\\' && base.flags[1]))) { + if ((input.charAt(0) == c'/') && (input.charAt(1) == c'/' || (input.charAt(1) == c'\\' && base.flags[INDEX_SPECIAL]))) { let newInput: string = base.cScheme + input; - resUrl.flags[0] = false + resUrl.flags[INDEX_INVALID] = false resUrl.initOnlyInput(newInput); result.urlInner = resUrl; return result; } } - if (!base.flags[9]) { - resUrl.flags[0] = false + if (!base.flags[INDEX_INFOPATH]) { + resUrl.flags[INDEX_INVALID] = false baseIntoUrl(base, resUrl, input.length == 0); strInput = resUrl.toolHasBase(input, strInput); - if (input.length != 0 && input[0] != c'/' && resUrl.cPath.length == 0) { + if (input.length != 0 && input.charAt(0) != c'/' && resUrl.cPath.length == 0) { resUrl.cPath = base.cPath; - resUrl.flags[6] = base.flags[6]; + resUrl.flags[INDEX_PATH] = base.flags[INDEX_PATH]; } - if (input.length != 0 && input[0] != c'/' && resUrl.cPath.length != 0) { - let isFile: boolean = ((resUrl.cScheme == "file:") ? true : false); + if (input.length != 0 && input.charAt(0) != c'/' && resUrl.cPath.length != 0) { + let isFile: boolean = resUrl.cScheme == "file:"; shorteningPath(resUrl, base, isFile); let basePathStr: string = basePathToString(base); basePathStr == "" ? basePathStr = strInput : basePathStr += "/" + strInput; resUrl.cPath = new Array(); resUrl.analysizeInput(basePathStr); - resUrl.flags[6] = true + resUrl.flags[INDEX_PATH] = true } - } else if (base.flags[9]) { - resUrl.flags[0] = true + } else if (base.flags[INDEX_INFOPATH]) { + resUrl.flags[INDEX_INVALID] = true result.urlInner = resUrl; return result; } @@ -1958,15 +2033,38 @@ export namespace url { return result; } + /** + * The URLParams interface defines some practical methods to process URL query strings. + * + * @class URLParams + */ export class URLParams { urlClass: NativeURLSearchParams; parentUrl: URL | null = null; + + /** + * A parameterized constructor used to create an URLParams instance. + * As the input parameter of the constructor function, init supports four types. + * The input parameter is a character string two-dimensional array. + * The input parameter is the object list. + * The input parameter is a character string. + * The input parameter is the URLParams object. + * + * @param { string[][] | Record | string | URLParams } + * @throws { BusinessError } 10200002 - Parameter error. Parameter verification failed. + */ constructor(input?: [string, string][] | Record | string | URLParams) { let arr = parameterProcess(input); this.urlClass = new NativeURLSearchParams(); this.urlClass.setArray(arr); } + /** + * Appends a specified key/value pair as a new search parameter. + * + * @param { string } name - name name Key name of the search parameter to be inserted. + * @param { string } value - value value Values of search parameters to be inserted. + */ append(params1: string, params2: string): void { params1 = fixUSVstring(params1); params2 = fixUSVstring(params2); @@ -1977,6 +2075,16 @@ export namespace url { this.parentUrl!.setHref(); } } + + /** + * Sets the value associated with a given search parameter to the + * given value. If there were several matching values, this method + * deletes the others. If the search parameter doesn't exist, this + * method creates it. + * + * @param { string } name - name name Key name of the parameter to be set. + * @param { string } value - value value Indicates the parameter value to be set. + */ set(setName: string, setValues: string): void { setName = fixUSVstring(setName); setValues = fixUSVstring(setValues); @@ -1987,6 +2095,11 @@ export namespace url { this.parentUrl!.setHref(); } } + + /** + * Sort all key/value pairs contained in this object in place and return undefined. + * + */ sort(): void { this.urlClass.sort(); if (this.parentUrl != null) { @@ -1995,35 +2108,83 @@ export namespace url { this.parentUrl!.setHref(); } } + + /** + * Returns a Boolean that indicates whether a parameter with the specified name exists. + * + * @param { string } name - name name Specifies the name of a key-value pair. + * @returns { boolean } Returns a Boolean value that indicates whether a found + */ has(hasname: string): boolean { hasname = fixUSVstring(hasname); return this.urlClass.isHas(hasname); } + + /** + * Returns a query string suitable for use in a URL. + * + * @returns { string } Returns a search parameter serialized as a string, percent-encoded if necessary. + */ toString(): string { return this.urlClass.toString(); } + + /** + * Returns an iterator allowing to go through all keys contained in this object. + * + * @returns { IterableIterator } Returns an ES6 Iterator over the names of each name-value pair. + */ keys(): IterableIterator { return this.urlClass.iterByKeys().$_iterator(); } + /** + * Returns an iterator allowing to go through all keys contained in this object. + * + * @returns { IterableIterator } Returns an ES6 Iterator over the names of each name-value pair. + */ values(): IterableIterator { return this.urlClass.iterByValues().$_iterator(); } - getAll(getAllname: string): Array { + /** + * Returns all key-value pairs associated with a given search parameter as an array. + * + * @param { string } name - Specifies the name of a key value. + * @returns { string[] } string[] Returns all key-value pairs with the specified name. + */ + getAll(getAllname: string): string[] { getAllname = fixUSVstring(getAllname); return this.urlClass.getAll(getAllname); } + /** + * Returns the first value associated to the given search parameter. + * + * @param { string } name - Specifies the name of a key-value pair. + * @returns { string | undefined } Returns the first value found by name. If no value is found, undefined is returned. + * If no value is found, undefined is returned. + */ get(getname: string): string | undefined { getname = fixUSVstring(getname); return this.urlClass.get(getname); } - entries(): IterableIterator<[string,string]> { + /** + * Returns an ES6 iterator. Each item of the iterator is a JavaScript Array. + * The first item of Array is name, and the second item of Array is value. + * + * @returns { IterableIterator<[string,string]> } Returns an iterator over all values. + */ + entries(): IterableIterator<[string, string]> { return this.urlClass.entries().$_iterator(); } + /** + * Deletes the given search parameter and its associated value,from the list of all search parameters. + * + * @param { string } name - name name Name of the key-value pair to be deleted. + */ delete(deleteName: string): void { deleteName = fixUSVstring(deleteName); this.urlClass.delete(deleteName); @@ -2035,9 +2196,13 @@ export namespace url { } } - forEach( - objfun: (value: string, key: string, param: URLParams) => void - ) { + /** + * Callback functions are used to traverse key-value pairs on the URLParams instance object. + * + * @param { function } callbackFn - callbackFn value Current traversal key value, + * key Indicates the name of the key that is traversed. + */ + forEach(objfun: UrlCbFn) { let array = this.urlClass.getArray(); if (array.length == 0) { return; @@ -2050,20 +2215,37 @@ export namespace url { } } + /** + * Returns an iterator allowing to go through all key/value + * pairs contained in this object. + * + * @returns { IterableIterator<[string, string]> } Returns an ES6 iterator. + * Each item of the iterator is a JavaScript Array. + * The first item of Array is name, and the second item of Array is value. + */ $_iterator(): IterableIterator<[string, string]> { return this.urlClass.entries().$_iterator(); } + /** + * Update an URLParams instance by a string input. + * + * @param { string } input - input string. + */ updateParams(input: string): void { let arr = parameterProcess(input); this.urlClass.setArray(arr); } } + /** + * Handle the specific URLParams logic + * @class NativeURLParams + */ class NativeURLSearchParams { private searchParams = new Array(); - constructor() { } + constructor() {} get(key: string): string | undefined { let len = this.searchParams.length; @@ -2099,7 +2281,7 @@ export namespace url { } } - entries(): Array<[string,string]> { + entries(): Array<[string, string]> { let result = new Array<[string, string]>(); for (let i = 0; i < this.searchParams.length; i += 2) { let arr: [string, string] = [this.searchParams[i], this.searchParams[i + 1]]; @@ -2196,8 +2378,8 @@ export namespace url { let arrayLen: number = array.length; let key: string = ''; let value: string = ''; - const regStr='%20'; - for (let pos: number = 0; pos < arrayLen; pos += 2) { // 2:Even subscripts exist as key values + const regStr = '%20'; + for (let pos: int = 0; pos < arrayLen; pos += 2) { // 2:Even subscripts exist as key values key = encodePercentEncoding(array[pos], Helper.PARAMS_TOSTRING_SIGN_CHARS).replaceAll(regStr, '+'); value = encodePercentEncoding(array[pos + 1], Helper.PARAMS_TOSTRING_SIGN_CHARS).replaceAll(regStr, '+'); resultArray.push(`${pos > 0 ? '&' : ''}${key}=${value}`); @@ -2221,7 +2403,7 @@ export namespace url { } else if (input instanceof Record) { return parseParamFromRecord(input as Record) } else { - throw createBusinessError(SyntaxErrorCodeId,'Syntax Error. Invalid input type.'); + throw createBusinessError(SyntaxErrorCodeId, 'Syntax Error. Invalid input type.'); } } @@ -2230,7 +2412,7 @@ export namespace url { if (input.length == 0) { return result; } else if (input[0][0] == undefined || input[0][1] == undefined) { - throw createBusinessError(SyntaxErrorCodeId,'Parameter error. The type of input must be string[][].'); + throw createBusinessError(SyntaxErrorCodeId, 'Parameter error. The type of input must be string[][].'); } for (let it of input) { @@ -2250,7 +2432,7 @@ export namespace url { } function parseParamFromString(input: string): Array { - if (input[0] == c'?') { + if (input.charAt(0) == c'?') { input = input.slice(1); } let strVal = input.replaceAll('+', ' '); @@ -2262,9 +2444,6 @@ export namespace url { let result: string = ''; let i = 0; const UNICODE_0x80 = 0x80; - const UNICODE_0x800 = 0x800; - const UNICODE_0xD800 = 0xD800; - const UNICODE_0xE000 = 0xE000; const UNICODE_0xC0 = 0xC0; const UNICODE_0x3F = 0x3F; const UNICODE_0xF0 = 0xF0; @@ -2325,86 +2504,106 @@ export namespace url { } function unescapeBuffer(s: string): Uint8Array { - const outBuffer: Uint8Array = new Uint8Array(s.length); - let index = 0; + const outBuffer = new Uint8Array(s.length * 4); let outIndex = 0; - let currentChar: number = 0; - let nextChar = 0; - let hexHigh: number = 0; - let hexLow: number = 0; - const maxLength = s.length - 2; - // Flag to know if some hex chars have been decoded - let hasHex = false; - let code_plus = 43; - let code_percent = 37; - let code_space = 32; - while (index < s.length) { - currentChar = s.charCodeAt(index); - if (currentChar == code_plus) { - outBuffer[outIndex++] = code_space; - index++; - continue; + let srcIndex = 0; + + // UTF-8 encoding function + const encodeUtf8 = (codePoint: number) => { + if (codePoint <= 0x7F) { + outBuffer[outIndex++] = codePoint; + } else if (codePoint <= 0x7FF) { + outBuffer[outIndex++] = 0xC0 | (codePoint >> 6); + outBuffer[outIndex++] = 0x80 | (codePoint & 0x3F); + } else if (codePoint <= 0xFFFF) { + outBuffer[outIndex++] = 0xE0 | (codePoint >> 12); + outBuffer[outIndex++] = 0x80 | ((codePoint >> 6) & 0x3F); + outBuffer[outIndex++] = 0x80 | (codePoint & 0x3F); + } else if (codePoint <= 0x10FFFF) { + outBuffer[outIndex++] = 0xF0 | (codePoint >> 18); + outBuffer[outIndex++] = 0x80 | ((codePoint >> 12) & 0x3F); + outBuffer[outIndex++] = 0x80 | ((codePoint >> 6) & 0x3F); + outBuffer[outIndex++] = 0x80 | (codePoint & 0x3F); + } else { + // Invalid code point + outBuffer[outIndex++] = 0xEF; + outBuffer[outIndex++] = 0xBF; + outBuffer[outIndex++] = 0xBD; // U+FFFD + } + }; + + // Process '%' encoding + const handlePercentEncoding = () => { + if (srcIndex + 2 < s.length) { + const hex1 = Helper.unhexTable[s.charCodeAt(srcIndex + 1).toInt()]; + const hex2 = Helper.unhexTable[s.charCodeAt(srcIndex + 2).toInt()]; + + if (hex1 !== -1 && hex2 !== -1) { + outBuffer[outIndex++] = hex1 * 16 + hex2; + // skip %XX + srcIndex += 3; + return true; + } } - if (currentChar == code_percent && index < maxLength) { - currentChar = s.charCodeAt(++index); - hexHigh = Helper.unhexTable[currentChar]; - if (hexHigh < 0) { - outBuffer[outIndex++] = code_percent; + return false; + }; + + while (srcIndex < s.length) { + const code = s.charCodeAt(srcIndex); + + // Process '%' encoding + if (code === 0x25) { // '%' + if (handlePercentEncoding()) { + //Successfully processed '%' encoding, proceed to next char continue; - } else { - nextChar = s.charCodeAt(++index); - hexLow = Helper.unhexTable[nextChar]; - if (!(hexLow >= 0)) { - outBuffer[outIndex++] = code_percent; - index--; - } else { - hasHex = true; - currentChar = hexHigh * 16 + hexLow; + } + } + + // Handling bit agents + if (isHighSurrogate(code)) { + if (srcIndex + 1 < s.length) { + const nextCode = s.charCodeAt(srcIndex + 1); + if (isLowSurrogate(nextCode)) { + const codePoint = combineSurrogates(code, nextCode); + encodeUtf8(codePoint); + //Skip high-bit agents and low-bit agents + srcIndex += 2; + continue; } } + // No matching low-bit agent + encodeUtf8(0xFFFD); + } else if (isLowSurrogate(code)) { + // Isolated low-bit agents + encodeUtf8(0xFFFD); + } else { + encodeUtf8(code); } - outBuffer[outIndex++] = currentChar; - index++; + srcIndex++; } - return hasHex ? outBuffer.slice(0, outIndex) : outBuffer; + return outBuffer.slice(0, outIndex); + } + + function isHighSurrogate(code: number): boolean { + return code >= 0xD800 && code <= 0xDBFF; + } + + function isLowSurrogate(code: number): boolean { + return code >= 0xDC00 && code <= 0xDFFF; + } + + function combineSurrogates(high: number, low: number): number { + return (high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000; } function decodeStringParmas(input: string): string { - let strVal = ''; + let result = ''; try { - strVal = decodeURIComponent(input); + result = decodeURIComponent(input); } catch (e) { - strVal = utf8Decode(unescapeBuffer(input)); - } - return strVal; - } - - function decodeSafelyOut(input: string): string { - let decodedString: string = ''; - let decodedTemp: string = ''; - let index: number = 0; - while (index < input.length) { - if (input[index] == c'%' && new RegExp('[0-9A-Fa-f]{2}').test(input.slice(index + 1, index + 3))) { - const encodedChar = input.slice(index, index + 3); - try { - decodedString += decodeURIComponent(decodedTemp + encodedChar); - decodedTemp = ''; - } catch (e) { - decodedTemp += encodedChar; - } - index += 3; - continue; - } - if (decodedTemp == '') { - decodedString += input[index]; - } else { - decodedString += decodedTemp; - decodedString += input[index]; - decodedTemp = ''; - } - index++; + result = utf8Decode(unescapeBuffer(input)); } - return decodedTemp == '' ? decodedString : decodedString += decodedTemp; + return result; } function stringParmas(stringParm: string): Array { @@ -2415,17 +2614,16 @@ export namespace url { let buf = ""; let i = 0; for (; i < stringParm.length; i++) { - let code = stringParm[i]; + let code = stringParm.charAt(i); switch (code) { case c'&': { if (strStartPos == i) { strLastPos = i + 1; strStartPos = i + 1; - continue; } if (strLastPos < i) { - buf += stringParm.substr(strLastPos, i - strLastPos); + buf += stringParm.substring(strLastPos, i); } seachParasVec.push(buf); if (!isHasSpace) { @@ -2444,7 +2642,7 @@ export namespace url { break; } if (strLastPos < i) { - buf += stringParm.substr(strLastPos, i - strLastPos); + buf += stringParm.substring(strLastPos, i); } seachParasVec.push(buf); buf = ""; @@ -2459,7 +2657,7 @@ export namespace url { return seachParasVec; } if (strLastPos < i) { - buf += stringParm.substr(strLastPos, i - strLastPos); + buf += stringParm.substring(strLastPos, i); } seachParasVec.push(buf); if (!isHasSpace) { @@ -2469,24 +2667,10 @@ export namespace url { return seachParasVec; } - const unpairedSurrogateRe: RegExp = - new RegExp("(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])"); - function fixUSVstring(str: string) { return str.toWellFormed(); } - function join(arr: Array, s: string): string { - let result = ''; - for (let i = 0; i < arr.length; i++) { - result += arr[i]; - if (i != arr.length - 1) { - result += s; - } - } - return result; - } - function encodePercentEncoding(str: string, noEscapeTable: Int8Array) { const len = str.length; if (len == 0) @@ -2507,7 +2691,7 @@ export namespace url { const UNICODE_0x10000 = 0x10000; outer: for (; i < len; i++) { - let c = str.charCodeAt(i as number); + let c = str.charCodeAt(i).toInt(); // ASCII while (c < UNICODE_0x80) { @@ -2521,7 +2705,7 @@ export namespace url { if (++i == len) break outer; - c = str.charCodeAt(i); + c = str.charCodeAt(i).toInt(); } if (lastPos < i) @@ -2544,7 +2728,7 @@ export namespace url { // Surrogate pair ++i; - const c2 = str.charCodeAt(i) & UNICODE_0x3FF; + const c2 = str.charCodeAt(i).toInt() & UNICODE_0x3FF; lastPos = i + 1; c = UNICODE_0x10000 + (((c & UNICODE_0x3FF) << 10) | c2); diff --git a/static_core/plugins/ets/sdk/api/@ohos.util.ArrayList.ets b/static_core/plugins/ets/sdk/api/@ohos.util.ArrayList.ets index af05a63a1308bc7f9e3b7c564f286912836c1132..ca1834247bcc9d65db51e2acdf792437aa134116 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.util.ArrayList.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.util.ArrayList.ets @@ -15,17 +15,33 @@ import { BusinessError } from "@ohos.base"; -const TypeErrorCodeId: number = 401; -const OutOfBoundsErrorCodeId: number = 10200001; -const IncorrectBufferSizeId: number = 10200009; -const CapacityErrorCodeId: number = 10200062; -const INT_MAX_VALUE_OFFSET: number = 4294967296; +const TypeErrorCodeId: int = 401; +const OutOfBoundsErrorCodeId: int = 10200001; +const IncorrectBufferSizeId: int = 10200009; +const CapacityErrorCodeId: int = 10200062; +const INT_MAX_VALUE_OFFSET: int = 2147483647; + +export type ArrayListComparatorFn = (firstValue: T, secondValue: T) => double; +export type ArrayListForEachCb = (value: T, index: int, arrlist: ArrayList) => void; +export type ArrayListReplaceCb = (value: T, index: int, arrlist: ArrayList) => T; + +function createBusinessError(code: int, message: string) { + let err = new BusinessError(); + err.code = code; + err.name = 'BusinessError'; + err.message = message; + return err; +} export class ArrayList implements Iterable, JsonReplacer { private readonly DEFAULT_CAPACITY = 10; private capacity: int = this.DEFAULT_CAPACITY; private buffer: Array; + public toString(): string { + return "[object ArrayList]"; + } + public jsonReplacer(): Record { const buf = this.buffer; const len = buf.length; @@ -39,8 +55,8 @@ export class ArrayList implements Iterable, JsonReplacer { /** * property: The number of elements in the ArrayList. */ - get length(): number { - return this.buffer.length; + get length(): int { + return this.buffer.length.toInt(); } /** @@ -66,7 +82,7 @@ export class ArrayList implements Iterable, JsonReplacer { */ public add(element: T): boolean { if (this.length >= Int.MAX_VALUE) { - throw new BusinessError(CapacityErrorCodeId, new Error(`The current capacity has reached the maximum limit.`)); + throw createBusinessError(CapacityErrorCodeId, `The current capacity has reached the maximum limit.`); } if (this.length == this.capacity) { @@ -83,8 +99,7 @@ export class ArrayList implements Iterable, JsonReplacer { * * @param index - the index. */ - public insert(element: T, index: number): void { - this.checkIndexType(index); + public insert(element: T, index: int): void { this.checkIndex(index, this.length); if (this.length + 1 <= this.capacity) { @@ -128,10 +143,10 @@ export class ArrayList implements Iterable, JsonReplacer { * * @returns the index if the specified element is included, otherwise, -1. */ - public getIndexOf(element: T): number { + public getIndexOf(element: T): int { return this.buffer.findIndex((item: T): boolean => { return item == element; - }); + }).toInt(); } /** @@ -141,10 +156,10 @@ export class ArrayList implements Iterable, JsonReplacer { * * @returns the index if the specified element is included, otherwise, -1. */ - public getLastIndexOf(element: T): number { + public getLastIndexOf(element: T): int { return this.buffer.findLastIndex((item: T): boolean => { return item == element; - }); + }).toInt(); } /** @@ -154,8 +169,7 @@ export class ArrayList implements Iterable, JsonReplacer { * * @returns the deleted element. */ - public removeByIndex(index: number): T { - this.checkIndexType(index); + public removeByIndex(index: int): T { this.checkEmptyContainer(); this.checkIndex(index, this.length - 1); @@ -177,7 +191,7 @@ export class ArrayList implements Iterable, JsonReplacer { * @returns true if the specified element is included, otherwise, false. */ public remove(element: T): boolean { - let index: number = this.getIndexOf(element); + let index: int = this.getIndexOf(element); if (index != -1) { this.removeByIndex(index); return true; @@ -192,7 +206,7 @@ export class ArrayList implements Iterable, JsonReplacer { * * @param toIndex - the end index. */ - public removeByRange(fromIndex: number, toIndex: number): void { + public removeByRange(fromIndex: int, toIndex: int): void { this.checkEmptyContainer(); this.checkRange(fromIndex, toIndex); @@ -209,9 +223,9 @@ export class ArrayList implements Iterable, JsonReplacer { * @param callbackfn - The callback function taking the value, index, and the ArrayList as arguments. * */ - public replaceAllElements(callbackfn: (value: T, index: number, arrList: ArrayList) => T): void { + public replaceAllElements(callbackfn: ArrayListReplaceCb): void { for (let i: int = 0; i < this.length; i++) { - this.buffer[i] = callbackfn(this.buffer[i], i.toDouble(), this); + this.buffer[i] = callbackfn(this.buffer[i], i, this); } } @@ -221,9 +235,9 @@ export class ArrayList implements Iterable, JsonReplacer { * @param callbackfn - The function to execute on each element, taking the value, index, and ArrayList as arguments. * */ - public forEach(callbackfn: (value: T, index: number, arrList: ArrayList) => void): void { + public forEach(callbackfn: ArrayListForEachCb): void { for (let i: int = 0; i < this.length; i++) { - callbackfn(this.buffer[i], i.toDouble(), this); + callbackfn(this.buffer[i], i, this); } } @@ -233,7 +247,7 @@ export class ArrayList implements Iterable, JsonReplacer { * @param comparator - Optional. A function that defines the sort order. */ - public sort(comparator?: (firstValue: T, secondValue: T) => number): void { + public sort(comparator?: ArrayListComparatorFn): void { this.buffer.sort(comparator); } @@ -246,7 +260,7 @@ export class ArrayList implements Iterable, JsonReplacer { * * @returns A new ArrayList containing the specified portion of the original ArrayList. */ - public subArrayList(fromIndex: number, toIndex: number): ArrayList { + public subArrayList(fromIndex: int, toIndex: int): ArrayList { this.checkEmptyContainer(); this.checkRange(fromIndex, toIndex); @@ -287,7 +301,7 @@ export class ArrayList implements Iterable, JsonReplacer { * * @returns The capacity of the ArrayList. */ - public getCapacity(): number { + public getCapacity(): int { return this.capacity; } @@ -316,10 +330,7 @@ export class ArrayList implements Iterable, JsonReplacer { * * @returns The element at the specified index. */ - public $_get(index: number): T { - if (index > Int.MAX_VALUE) { - throw new BusinessError(TypeErrorCodeId, new Error(`The type of \"index\" must be small integer.`)); - } + public $_get(index: int): T { this.checkEmptyContainer(); this.checkIndex(index, this.length - 1); @@ -333,11 +344,8 @@ export class ArrayList implements Iterable, JsonReplacer { * * @param val - The value to set at the specified index. */ - public $_set(index: number, val: T): void { + public $_set(index: int, val: T): void { this.checkEmptyContainer(); - if (index < 0 || index > Int.MAX_VALUE) { - return; - } this.checkIndex(index, this.length - 1); this.buffer[index] = val; @@ -348,12 +356,12 @@ export class ArrayList implements Iterable, JsonReplacer { * * @param newCapacity - The minimum new capacity for the ArrayList. */ - public increaseCapacityTo(newCapacity: number): void { + public increaseCapacityTo(newCapacity: int): void { if (newCapacity > Int.MAX_VALUE) { - throw new BusinessError(CapacityErrorCodeId, new Error(`The current capacity has reached the maximum limit.`)); + throw createBusinessError(CapacityErrorCodeId, `The current capacity has reached the maximum limit.`); } - if (newCapacity > this.capacity) { + if (newCapacity > this.length) { this.increaseCap(newCapacity.toInt()); } } @@ -394,38 +402,38 @@ export class ArrayList implements Iterable, JsonReplacer { private checkEmptyContainer(): void { if (this.isEmpty()) { - throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`Container is empty`)); + throw createBusinessError(OutOfBoundsErrorCodeId, `Container is empty`); } } - private checkIndexType(index: number): void { + private checkIndex(index: int, length: int): void { if (index > Int.MAX_VALUE) { - throw new BusinessError(TypeErrorCodeId, new Error(`The type of "index" must be small integer. Received value is: ${index}`)); + throw createBusinessError(TypeErrorCodeId, `The type of "index" must be small integer. Received value is: ${index}`); } - } - - private checkIndex(index: number, length: number): void { if (index > length || index < 0) { - this.createBusinessError('index', length, index); + this.createRangeBusinessError('index', length, index); + } + if (!isInteger(index)) { + throw createBusinessError(TypeErrorCodeId, `Index must be an integer. Received value is: ${index}`); } } - private checkRange(fromIndex: number, toIndex: number): void { + private checkRange(fromIndex: int, toIndex: int): void { if (fromIndex > Int.MAX_VALUE) { - this.createBusinessError('fromIndex', min(this.length - 1, toIndex - 1), fromIndex - INT_MAX_VALUE_OFFSET); + this.createRangeBusinessError('fromIndex', min(this.length - 1, toIndex - 1), fromIndex - INT_MAX_VALUE_OFFSET); } if (fromIndex < 0 || fromIndex >= toIndex || fromIndex >= this.length) { - this.createBusinessError('fromIndex', min(this.length - 1, toIndex - 1), fromIndex); + this.createRangeBusinessError('fromIndex', min(this.length - 1, toIndex - 1), fromIndex); } if (toIndex > Int.MAX_VALUE) { - this.createBusinessError('toIndex', this.length, toIndex - INT_MAX_VALUE_OFFSET); + this.createRangeBusinessError('toIndex', this.length, toIndex - INT_MAX_VALUE_OFFSET); } if (toIndex > this.length) { - this.createBusinessError('toIndex', this.length, toIndex); + this.createRangeBusinessError('toIndex', this.length, toIndex); } } - private createBusinessError(index: string, upperLimit: number, currentValue: number): void { - throw new BusinessError(OutOfBoundsErrorCodeId, new Error(`The value of \"${index}\" is out of range. It must be >= 0 && <= ${upperLimit}. Received value is: ${currentValue}`)); + private createRangeBusinessError(index: string, upperLimit: int, currentValue: int): void { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of \"${index}\" is out of range. It must be >= 0 && <= ${upperLimit}. Received value is: ${currentValue}`); } } diff --git a/static_core/plugins/ets/sdk/api/@ohos.util.Deque.ets b/static_core/plugins/ets/sdk/api/@ohos.util.Deque.ets index 62377cf96545bc65b3e5fcd385b411d17ff1aadd..a738a239225cfb8c5ab8a644fc13f7089f05ab48 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.util.Deque.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.util.Deque.ets @@ -15,9 +15,10 @@ import { BusinessError } from "@ohos.base"; -const OutOfBoundsErrorCodeId: number = 10200001; +const TypeErrorCodeId: int = 401; +const OutOfBoundsErrorCodeId: int = 10200001; -function createBusinessError(code: number, message: string) { +function createBusinessError(code: int, message: string) { let err = new BusinessError(); err.code = code; err.name = 'BusinessError'; @@ -25,12 +26,18 @@ function createBusinessError(code: number, message: string) { return err; } +export type DequeforEachCb = (value: T, index: int, deque: Deque) => void; + export default class Deque implements Iterable, JsonReplacer { private readonly DEFAULT_CAPACITY = 8; - internal capacity: int = this.DEFAULT_CAPACITY; - internal buffer: Array; - internal front: int = 0; - internal rear: int = 0; + private capacity: int = this.DEFAULT_CAPACITY; + private buffer: Array; + private front: int = 0; + private rear: int = 0; + + public toString(): string { + return "[object Deque]"; + } public jsonReplacer(): Record { const buf = this.buffer; @@ -45,7 +52,7 @@ export default class Deque implements Iterable, JsonReplacer { /** * property: The number of elements in the ArrayList. */ - get length(): number { + get length(): int { return (this.rear - this.front + this.capacity) % this.capacity; } @@ -138,7 +145,7 @@ export default class Deque implements Iterable, JsonReplacer { * @param callbackfn - The function to execute on each element, taking the value, index, and Deque as arguments. * */ - public forEach(callbackfn: (value: T, index: number, deque: Deque) => void): void { + public forEach(callbackfn: DequeforEachCb): void { let i: int = this.front, k = 0; while (i != this.rear) { callbackfn(this.buffer[i], k++, this); @@ -180,7 +187,7 @@ export default class Deque implements Iterable, JsonReplacer { * @returns {IterableIterator} An iterator for the deque. */ public override $_iterator(): IterableIterator { - return new DequeValuesIterator_T(this); + return new DequeValuesIterator_T(this, this.front, this.rear, this.capacity, this.buffer); } /** @@ -204,13 +211,9 @@ export default class Deque implements Iterable, JsonReplacer { /** * Increases the capacity of the deque. * - * @throws {RangeError} If the current capacity has reached the maximum limit. + * @throws {BusinessError} If the current capacity has reached the maximum limit. */ private increaseCapacity(): void { - if (this.capacity == Int.MAX_VALUE) { - throw new RangeError("The current capacity has reached the maximum limit."); - } - let newCap: int = min(this.capacity * 2, Int.MAX_VALUE); let newBuffer = new Array(newCap); @@ -233,12 +236,16 @@ export default class Deque implements Iterable, JsonReplacer { * @param index - The index of the element to retrieve. * * @returns The element at the specified index. - * @throws { BusinessError } 10200001 - The value of "index" is out of range. It must be >= 0 && <= 2147483647. + * @throws { BusinessError } 401 - The type of "index" must be small integer. + * @throws { BusinessError } 10200001 - The value of "index" is out of range. It must be >= 0 && <= ${length - 1}. + * Received value is: ${index} */ - public $_get(index: number): T { - if (index >= this.length || index < 0) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of \"index\" is out of range. It must be >= 0 && <= ${Int.MAX_VALUE}. Received value is: ${index}`); + public $_get(index: int): T { + this.checkEmptyContainer(); + if (index > Int.MAX_VALUE) { + throw createBusinessError(TypeErrorCodeId, `The type of \"index\" must be small integer.`); } + this.checkIndex(index); return this.buffer[this.getRealIndex(index)]; } @@ -249,37 +256,54 @@ export default class Deque implements Iterable, JsonReplacer { * @param index - The index of the element to set. * * @param val - The value to set at the specified index. - * @throws { BusinessError } 10200001 - The value of "index" is out of range. It must be >= 0 && <= 2147483647. + * @throws { BusinessError } 10200001 - The value of "index" is out of range. It must be >= 0 && <= ${length - 1}. + * Received value is: ${index} */ - public $_set(index: number, val: T): void { - if (index >= this.length || index < 0) { - throw createBusinessError(OutOfBoundsErrorCodeId, `The value of \"index\" is out of range. It must be >= 0 && <= ${Int.MAX_VALUE}. Received value is: ${index}`); - } + public $_set(index: int, val: T): void { + this.checkIndex(index); this.buffer[this.getRealIndex(index)] = val; } - private getRealIndex(index: number): number { + private getRealIndex(index: int): int { return (this.front + index) % this.capacity; } + + private checkEmptyContainer(): void { + if (this.isEmpty()) { + throw createBusinessError(OutOfBoundsErrorCodeId, `Container is empty`); + } + } + + private checkIndex(index: int): void { + if (index < 0) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of \"index\" is out of range. It must be >= 0 && <= ${this.length - 1}. Received value is: ${index}`); + } + } } class DequeValuesIterator_T implements IterableIterator { private parent: Deque private idx: int = 0; + private capacity: int + private buffer: Array; + private rear: int = 0; - constructor(parent: Deque) { + constructor(parent: Deque, front: int, rear: int, capacity: int, buffer: Array) { this.parent = parent - this.idx = parent.front; + this.idx = front; + this.rear = rear + this.capacity = capacity + this.buffer = buffer } override next(): IteratorResult { - if (this.parent.length == 0 || this.parent.rear == this.idx) { + if (this.parent.length == 0 || this.rear == this.idx) { return new IteratorResult() } - let val = new IteratorResult(this.parent.buffer[this.idx]); - this.idx = (this.idx + 1) % this.parent.capacity; + let val = new IteratorResult(this.buffer[this.idx]); + this.idx = (this.idx + 1) % this.capacity; return val; } diff --git a/static_core/plugins/ets/sdk/api/@ohos.util.LightWeightMap.ets b/static_core/plugins/ets/sdk/api/@ohos.util.LightWeightMap.ets index 955ebe6b3724f0187c5cb7d7322f97eeb1ef71e1..8ab51fcee92f16894b22ae38ce2cf30719c16ad5 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.util.LightWeightMap.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.util.LightWeightMap.ets @@ -13,13 +13,28 @@ * limitations under the License. */ +import { BusinessError } from "@ohos.base"; + +const TypeErrorCodeId: int = 401; +const OutOfBoundsErrorCodeId: int = 10200001; + +function createBusinessError(code: int, message: string) { + let err = new BusinessError(); + err.code = code; + err.name = 'BusinessError'; + err.message = message; + return err; +} + +export type LightWeightMapCbFn = (value: V, key: K, map: LightWeightMap) => void; + interface ReadonlyLightWeightMap extends Iterable<[K, V]> { /** * Returns number of Entries with unique keys in the LightWeightMap * * @returns number of Entries with unique keys in the LightWeightMap */ - get length(): number; + get length(): int; /** * Returns a value associated with key if present @@ -74,7 +89,7 @@ class LightWeightMapEntry { this.entryValues = new Array(this.capacity); } - set(index: number, hash: int, key: K, value: V): void { + set(index: int, hash: int, key: K, value: V): void { this.insert(this.entryHashs, index, hash); this.insert(this.entryKeys, index, key); this.insert(this.entryValues, index, value); @@ -97,7 +112,7 @@ class LightWeightMapEntry { } } - remove(index: number): void { + remove(index: int): void { this.entryHashs.splice(index, 1); this.entryKeys.splice(index, 1); this.entryValues.splice(index, 1); @@ -112,8 +127,8 @@ class LightWeightMapEntry { this.entryValues = new Array(this.capacity); } - private insert(a: Array, index: number, value: T): void { - for (let i: number = this.actualLength; i > index; i--) { + private insert(a: Array, index: int, value: T): void { + for (let i: int = this.actualLength; i > index; i--) { a[i] = a[i - 1]; } a[index] = value; @@ -127,7 +142,7 @@ class LightWeightMapEntry { return newArray; } - private remove(array: Array, index: number): Array { + private remove(array: Array, index: int): Array { const len = this.actualLength; for (let i = index; i < len; i++) { if ((i + 1) === len) { @@ -177,8 +192,8 @@ export default class LightWeightMap implements ReadonlyLightWeightMap implements ReadonlyLightWeightMap) => void): void { + forEach(callbackFn: LightWeightMapCbFn): void { const iter = this.entries(); let res = iter.next(); while (!res.done) { @@ -254,7 +269,7 @@ export default class LightWeightMap implements ReadonlyLightWeightMap implements ReadonlyLightWeightMap implements ReadonlyLightWeightMap Int.MAX_VALUE || index < 0 || this.buckets.actualLength <= index) { - throw new RangeError("The value of index is out of range."); - } + getKeyAt(index: int): K { + this.checkIndex(index); + this.checkEmptyContainer(); + this.checkRange(index, this.buckets.actualLength); + return this.buckets.entryKeys[index]; } @@ -290,11 +308,13 @@ export default class LightWeightMap implements ReadonlyLightWeightMap Int.MAX_VALUE || index < 0 || this.buckets.actualLength <= index) { - throw new RangeError("The value of index is out of range."); - } + getValueAt(index: int): V { + this.checkEmptyContainer(); + this.checkRange(index, this.buckets.actualLength); + return this.buckets.entryValues[index]; } @@ -309,7 +329,7 @@ export default class LightWeightMap implements ReadonlyLightWeightMap this.buckets.actualLength) { return false; } - for (let index: number = 0; index < map.length; index++) { + for (let index: int = 0; index < map.length; index++) { const key = map.getKeyAt(index)!; if (!this.hasKey(key)) { return false; @@ -351,11 +371,11 @@ export default class LightWeightMap implements ReadonlyLightWeightMap Int.MAX_VALUE) { return; } - this.buckets.increaseCapacityTo(minimumCapacity.toInt()); + this.buckets.increaseCapacityTo(minimumCapacity); } /** @@ -390,9 +410,11 @@ export default class LightWeightMap implements ReadonlyLightWeightMap implements ReadonlyLightWeightMap= this.buckets.actualLength) { - throw new RangeError("The value of index is out of range."); - } + setValueAt(index: int, newValue: V): boolean { + this.checkEmptyContainer(); + this.checkRange(index, this.buckets.actualLength.toInt()); + this.buckets.entryValues[index] = newValue; return true; } @@ -467,21 +491,21 @@ export default class LightWeightMap implements ReadonlyLightWeightMap= 0) { let right = index; while ((right < this.buckets.actualLength) && (this.buckets.entryHashs[right] === hashCode)) { if (this.buckets.entryKeys[right] === key) { - return right.toDouble(); + return right; } right++; } let left = index - 1; while ((left > 0) && (this.buckets.entryHashs[right] === hashCode)) { if (this.buckets.entryKeys[left] === key) { - return left.toDouble(); + return left; } left--; } @@ -498,7 +522,7 @@ export default class LightWeightMap implements ReadonlyLightWeightMap= 0) { return (keyHash | positiveMask) @@ -523,4 +547,23 @@ export default class LightWeightMap implements ReadonlyLightWeightMap Int.MAX_VALUE) { + throw createBusinessError(TypeErrorCodeId, + `The type of "index" must be small integer. Received value is: ${index}`) + } + } + + private checkRange(index: int, length: int): void { + if (index < 0 || index >= this.buckets.actualLength) { + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of \"index\" is out of range. It must be >= 0 && <= ${this.buckets.actualLength - 1}. Received value is: ${index}`); + } + } + + private checkEmptyContainer(): void { + if(this.isEmpty()) { + throw createBusinessError(OutOfBoundsErrorCodeId, `Container is empty`); + } + } } diff --git a/static_core/plugins/ets/sdk/api/@ohos.util.LightWeightSet.ets b/static_core/plugins/ets/sdk/api/@ohos.util.LightWeightSet.ets index 2661ae2813149fe0932ee96d9115aa9d02bba96f..cc40a553833072ef924bfec39ce3d156c0707de2 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.util.LightWeightSet.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.util.LightWeightSet.ets @@ -14,6 +14,12 @@ */ import LightWeightMap from '@ohos.util.LightWeightMap'; +import { BusinessError } from "@ohos.base"; + +const typeErrorCodeId: int = 401; +const outOfBoundsErrorCodeId: number = 10200001; + +export type LightWeightSetForEachCb = (value: T, key: T, set: LightWeightSet) => void; interface ReadonlyLightWeightSet extends Iterable { /** @@ -30,7 +36,7 @@ interface ReadonlyLightWeightSet extends Iterable { * * @returns number of unique elements in the LightWeightSet */ - get length(): number; + get length(): int; /** * Executes a provided function once per each value in the LightWeightSet object, in insertion order @@ -52,11 +58,15 @@ interface ReadonlyLightWeightSet extends Iterable { entries(): IterableIterator<[T, T]>; } -export class LightWeightSet implements ReadonlyLightWeightSet { +export class LightWeightSet implements ReadonlyLightWeightSet, JsonReplacer { private buckets: LightWeightMap; private static readonly SHIFT_COUNT = 31; + private static readonly MIN_BUCKETS_COUNT = 8; + + private capacity: int = LightWeightSet.MIN_BUCKETS_COUNT; + private iteratorForEach(x: Iterator, fn: (x: V) => void): void { while (true) { const v = x.next(); @@ -67,6 +77,23 @@ export class LightWeightSet implements ReadonlyLightWeightSet { } } + public jsonReplacer(): Record { + const buf = this.buckets; + const len = buf.length; + let arrayObj: Record = {}; + for (let i = 0; i < len; i++) { + if (Array.isArray(buf.getKeyAt(i))) { + for (let i = 0; i < (buf.getKeyAt(i) as Array).length; i++) { + if ((buf.getKeyAt(i) as Array)[i] === undefined) { + (buf.getKeyAt(i) as Array)[i] = null; + } + } + } + arrayObj[String(i)] = buf.getKeyAt(i); + } + return arrayObj; + } + constructor() { this.buckets = new LightWeightMap(); } @@ -76,8 +103,8 @@ export class LightWeightSet implements ReadonlyLightWeightSet { * * @returns number of unique elements in the LightWeightSet */ - get length(): number { - return this.buckets.length; + get length(): int { + return this.buckets.length.toInt(); } /** @@ -91,6 +118,13 @@ export class LightWeightSet implements ReadonlyLightWeightSet { if (this.buckets.hasKey(obj)) { return false; } + if (this.buckets.length >= this.capacity) { + if (this.capacity * 2 > Int.MAX_VALUE) { + return false; + } else { + this.increaseCapacityTo(this.capacity * 2); + } + } this.buckets.set(obj, obj); return true; } @@ -122,6 +156,7 @@ export class LightWeightSet implements ReadonlyLightWeightSet { */ clear(): void { this.buckets.clear(); + this.capacity = LightWeightSet.MIN_BUCKETS_COUNT; } /** @@ -154,7 +189,7 @@ export class LightWeightSet implements ReadonlyLightWeightSet { * * @param callbackfn to apply; key is always same as value */ - forEach(callbackFn: (value: T, key: T, set: LightWeightSet) => void): void { + forEach(callbackFn: LightWeightSetForEachCb): void { this.buckets.forEach((value: T, key: T): void => { callbackFn(value, key, this); }); @@ -182,14 +217,14 @@ export class LightWeightSet implements ReadonlyLightWeightSet { * * @returns the index of the value, or a negative position if the value is not found */ - getIndexOf(key: T): number { - const index = this.buckets.getIndexOfKey(key); + getIndexOf(key: T): int { + const index = this.buckets.getIndexOfKey(key).toInt(); if (index >= 0) { return index; } else { const hashCode = this.hash(key); let low: int = 0; - let high: int = this.buckets.length as int - 1; + let high: int = this.buckets.length - 1; while (low <= high) { const mid = (low + high) >>> 1; const midHash = this.hash(this.buckets.getKeyAt(mid)!); @@ -209,8 +244,15 @@ export class LightWeightSet implements ReadonlyLightWeightSet { * @param index the index to get the value from * * @returns the value at the specified index + * + * @throws BusinessError if the index exceeds Int.MAX_VALUE. */ - getValueAt(index: number): T | undefined { + getValueAt(index: int): T | undefined { + this.checkIndex(index); + + if (index < 0 || this.buckets.length <= index) { + return undefined; + } return this.buckets.getKeyAt(index); } @@ -225,7 +267,7 @@ export class LightWeightSet implements ReadonlyLightWeightSet { if (set.length > this.buckets.length) { return false; } - for (let index: number = 0; index < set.length; index++) { + for (let index: int = 0; index < set.length; index++) { const value = set.getValueAt(index)!; if (!this.has(value)) { return false; @@ -238,8 +280,18 @@ export class LightWeightSet implements ReadonlyLightWeightSet { * Increases the capacity of the LightWeightSet to the specified minimum capacity * * @param minimumCapacity the minimum capacity to increase to + * + * throws BusinessError if the minimumCapacity out of range. */ - increaseCapacityTo(minimumCapacity: number): void { + increaseCapacityTo(minimumCapacity: int): void { + if (minimumCapacity > Int.MAX_VALUE) { + return; + } + if (minimumCapacity <= 0 || this.capacity >= minimumCapacity) { + throw new BusinessError(outOfBoundsErrorCodeId, + new Error(`The value of "minimumCapacity" is out of range. It must be > ${this.capacity}. Received value is: ${minimumCapacity}`)); + } + this.capacity = minimumCapacity; this.buckets.increaseCapacityTo(minimumCapacity); } @@ -258,8 +310,12 @@ export class LightWeightSet implements ReadonlyLightWeightSet { * @param index the index to remove the value from * * @returns true if the value was removed, false otherwise + * + * @throws BusinessError if the index exceeds Int.MAX_VALUE. */ - removeAt(index: number): boolean { + removeAt(index: int): boolean { + this.checkIndex(index); + return this.buckets.removeAt(index); } @@ -292,7 +348,7 @@ export class LightWeightSet implements ReadonlyLightWeightSet { return 1; } - const keyHash: int = (key! as object).$_hashCode(); // #26217 + const keyHash: int = Runtime.getHashCodeByValue(key! as object).toInt(); // #26217 const positiveMask: int = 1 << LightWeightSet.SHIFT_COUNT; if (keyHash >= 0) { return (keyHash | positiveMask) @@ -300,4 +356,12 @@ export class LightWeightSet implements ReadonlyLightWeightSet { return (keyHash & (~positiveMask)) } } + + private checkIndex(index: int): void { + if (index > Int.MAX_VALUE) { + throw new BusinessError(typeErrorCodeId, + new Error('BusinessError', `The type of "index" must be small integer. Received value is: ${index}`, + undefined)); + } + } } diff --git a/static_core/plugins/ets/sdk/api/@ohos.util.ets b/static_core/plugins/ets/sdk/api/@ohos.util.ets index e59f7d8f0bbbbf0ddde7f03c8537f771fd304fbf..dcc9df1de4345d409c515e19ac08ee616532a522 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.util.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.util.ets @@ -15,10 +15,13 @@ import { BusinessError } from "@ohos.base"; -const TypeErrorCodeId: number = 401; +const TypeErrorCodeId: int = 401; const percentRate = 100; +const syntaxErrorCode: int = 10200002; +export type RecordData = undefined | null | Object | Record | Array; +export type PromisifiedFunc = (...args: FixedArray) => Promise; -function createBusinessError(code: number, message: string) { +function createBusinessError(code: int, message: string) { let err = new BusinessError(); err.code = code; err.name = 'BusinessError'; @@ -132,23 +135,23 @@ export namespace util { } public getCreateCount(): number { - return this.createCount as number; + return this.createCount.toDouble(); } public getMissCount(): number { - return this.missCount as number; + return this.missCount.toDouble(); } public getRemovalCount(): number { - return this.evictionCount as number; + return this.evictionCount.toDouble(); } public getMatchCount(): number { - return this.hitCount as number; + return this.hitCount.toDouble(); } public getPutCount(): number { - return this.putCount as number; + return this.putCount.toDouble(); } public $_iterator(): IterableIterator<[K, V]> { @@ -246,16 +249,12 @@ export namespace util { */ private static encodeBase64(bytes: Uint8Array, urlSafe: boolean, mime: boolean): string { let table = urlSafe ? Base64Helper.BASE64_URL_SAFE_CHARS : Base64Helper.BASE64_CHARS; - let binaryString: string = ""; - for (let i = 0; i < bytes.length; i++) { - binaryString += String.fromCharCode(bytes[i]); - } let encoded: string = ""; let buffer: int = 0; let bufferLength: int = 0; - for (let i = 0; i < binaryString.length; i++) { - buffer = (buffer << Base64Helper.BYTE_SIZE) | binaryString.charCodeAt(i); + for (let i = 0; i < bytes.length; i++) { + buffer = (buffer << Base64Helper.BYTE_SIZE) | bytes[i].toInt(); bufferLength += Base64Helper.BYTE_SIZE; while (bufferLength >= Base64Helper.BASE64_GROUP_SIZE) { bufferLength -= Base64Helper.BASE64_GROUP_SIZE; @@ -295,11 +294,11 @@ export namespace util { let bytes: Array = new Array(); for (let i = 0; i < cleaned.length; i++) { - let ch = cleaned[i]; + let ch = cleaned.charAt(i); if (ch == c'=') { break; } - let index: int = table.indexOf(ch) as int; + let index: int = table.indexOf(ch).toInt(); if (index == -1) { continue; } @@ -323,6 +322,10 @@ export namespace util { if (options === undefined) { options = Type.BASIC; } + if (options != Type.BASIC && options != Type.BASIC_URL_SAFE){ + throw createBusinessError(TypeErrorCodeId, + `Parameter error. The target encoding type option must be BASIC or BASIC_URL_SAFE.`); + } let encoded: string = Base64Helper.encodeBase64(src, options === Type.BASIC_URL_SAFE || options === Type.MIME_URL_SAFE, options === Type.MIME || options === Type.MIME_URL_SAFE); let result: Uint8Array = new Uint8Array(encoded.length); for (let i: int = 0; i < encoded.length; i++) { @@ -714,21 +717,6 @@ export namespace util { private native decode(input: Uint8Array, decodeWithStream: boolean): string; private static native nativeDestroy(ptr: long): void; } - - export interface EncodeIntoUint8ArrayInfo { - read: int; - written: int; - } - - class EncodeIntoUint8ArrayInfoInner implements EncodeIntoUint8ArrayInfo { - read: int; - written: int; - constructor(outRead: int, outWritten: int) { - this.read = outRead; - this.written = outWritten; - } - } - export class RationalNumber { private mnum: number = 0; private mden: number = 0; @@ -790,12 +778,12 @@ export namespace util { const regex = new RegExp('^(.*)([:\\/])(.*)$'); const match = str.match(regex); if (!match) { - throw new BusinessError(TypeErrorCodeId, - new Error(`Parameter error. The type of ${str} must be effective string`)); + throw createBusinessError(TypeErrorCodeId, + `Parameter error. The type of ${str} must be effective string`); } - const str1 = match[1]; + const str1 = match[1]!; const separator = match[2]; - const str2 = match[3]; + const str2 = match[3]!; if (RationalNumber.isNumeric(str1) && RationalNumber.isNumeric(str2)) { const num1 = Number(str1); const num2 = Number(str2); @@ -804,8 +792,8 @@ export namespace util { } return RationalNumber.parseRationalNumber(num1, num2); } else { - throw new BusinessError(TypeErrorCodeId, - new Error(`Parameter error. The type of ${str} must be character string`)); + throw createBusinessError(TypeErrorCodeId, + `Parameter error. The type of ${str} must be character string`); } } @@ -874,8 +862,8 @@ export namespace util { */ static getCommonFactor(firNum: number, SecNum: number): number { if (firNum === 0 || SecNum === 0) { - throw new BusinessError(TypeErrorCodeId, - new Error(`Parameter error. The Parameter cannot be zero`)); + throw createBusinessError(TypeErrorCodeId, + `Parameter error. The Parameter cannot be zero`); } if (!Number.isInteger(firNum) || !Number.isInteger(SecNum)) { console.error('getCommonFactor: The type of Parameter must be integer'); @@ -964,6 +952,11 @@ export namespace util { } } + export interface EncodeIntoUint8ArrayInfo { + read: int; + written: int; + } + export class TextEncoder { static { loadLibrary("ets_sdk_native") } @@ -977,8 +970,7 @@ export namespace util { return; } let tempString: string = encoding.toLowerCase(); - let flag: boolean = TextEncoder.checkEncodingFormat(tempString); - if (!flag) { + if (!(TextEncoder.KNOWN_ENCODINGS.has(tempString))) { throw createBusinessError(TypeErrorCodeId, `Wrong encoding format, the current '${encoding}' format is not support.`) } this.encoding_ = encoding; @@ -994,43 +986,38 @@ export namespace util { if (!input) { return new Uint8Array(0); } - return TextEncoder.doEncodeInto(input, this.encoding_); + return new Uint8Array(TextEncoder.doEncodeInto(input, this.encoding_)); } encodeIntoUint8Array(input: string, dest: Uint8Array): EncodeIntoUint8ArrayInfo { - return TextEncoder.doEncodeInfoUint8Array(input, this.encoding_, dest); + let res: Array = TextEncoder.doEncodeInfoUint8Array(input, this.encoding_, dest); + let result: EncodeIntoUint8ArrayInfo = {read: 0, written: 0}; + result.read = res[0]; + result.written = res[1]; + return result; } get encoding(): string { return this.encoding_; } - private static checkEncodingFormat(encoding: string): boolean - { - const knownEncodings: string[] = [ - "utf-8", "utf-16be", "utf-16le", "gbk", "gb2312", "gb18030", "ibm866", - "iso-8859-1", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", - "iso-8859-7", "iso-8859-8", "iso-8859-8-i", "iso-8859-10", "iso-8859-13", - "iso-8859-14", "iso-8859-15", "koi8-r", "koi8-u", "macintosh", - "windows-874", "windows-1250", "windows-1251", "windows-1252", - "windows-1253", "windows-1254", "windows-1255", "windows-1256", - "windows-1257", "windows-1258", "big5", "euc-jp", "iso-2022-jp", - "shift_jis", "euc-kr", "x-mac-cyrillic" - ]; - for (const enc of knownEncodings) { - if (enc == encoding) { - return true; - } - } - return false; - } + private static readonly KNOWN_ENCODINGS: Set = new Set([ + "utf-8", "utf-16be", "utf-16le", "gbk", "gb2312", "gb18030", "ibm866", + "iso-8859-1", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-6", + "iso-8859-7", "iso-8859-8", "iso-8859-8-i", "iso-8859-10", "iso-8859-13", + "iso-8859-14", "iso-8859-15", "koi8-r", "koi8-u", "macintosh", + "windows-874", "windows-1250", "windows-1251", "windows-1252", + "windows-1253", "windows-1254", "windows-1255", "windows-1256", + "windows-1257", "windows-1258", "big5", "euc-jp", "iso-2022-jp", + "shift_jis", "euc-kr", "x-mac-cyrillic" + ]); private static native doEncodeInto( - input: string, inputEncoding: string): Uint8Array; + input: string, inputEncoding: string): ArrayBuffer; private static native doEncodeInfoUint8Array( - input: string, inputEncoding: string, destArray: Uint8Array): EncodeIntoUint8ArrayInfo; + input: string, inputEncoding: string, destArray: Uint8Array): Array; } export class StringDecoder { @@ -1106,7 +1093,9 @@ export namespace util { static native generateRandomUUID(entropyCache: boolean): string - internal static objectToString(obj: Object, full: boolean = false): string { + static native generateRandomBinaryUUID(entropyCache: boolean): Uint8Array + + private static objectToString(obj: Object, full: boolean = false): string { return (new UtilHelper).stringifyObject(obj, '\n', 1, full) } @@ -1510,7 +1499,7 @@ export namespace util { * @returns { boolean } Returns true if the value is an iterator returned for a built-in Map instance. */ isMapIterator(value: Object): boolean { - return value instanceof IterableIterator; + return value instanceof MapIterator || value instanceof EmptyMapIterator; } /** @@ -1553,9 +1542,11 @@ export namespace util { * Check whether the entered value is the iterator type of set. * @param { Object } value - A Set iterator value * @returns { boolean } Returns true if the value is an iterator returned for a built-in Set instance. + * + * Since the current interface cannot be correctly determined, it returns false by default */ isSetIterator(value: Object): boolean { - return value instanceof IterableIterator; + return false; } /** @@ -1630,15 +1621,101 @@ export namespace util { isWeakSet(value: Object): boolean { return value instanceof WeakSet; } + + /** + * Check whether the entered value is of type asyncfunction. + * @param { Object } value - A AsyncFunction value + * @returns { boolean } Returns true if the value is a built-in AsyncFunction instance. + * + * Since the current interface cannot be correctly determined, it returns false by default + */ + isAsyncFunction(value: Object): boolean { + return false; + } + } + + export function promisify(original: Function): PromisifiedFunc { + return (...args: FixedArray): Promise => { + return new Promise((resolve, reject) => { + let callback: Function = (err: Error | null, ...value: FixedArray) => { + if (err) { + reject(err); + } else { + resolve(value); + } + }; + const newArgs: FixedArray = new Any[args.length + 1]; + for (let i = 0; i < args.length; i++) { + newArgs[i] = args[i]; + } + newArgs[args.length] = callback; + original.unsafeCall(...newArgs); + }); + }; + } + + export function callbackWrapper(original: Function): Function { + return (...args: FixedArray): void => { + if (typeof args[args.length - 1] != 'function') { + throw new Error('maybe is not function'); + } + const func: Function = args[args.length - 1] as Function; + const newArgs: FixedArray = new Any[args.length - 1]; + for (let i = 0; i < args.length - 1; i++) { + newArgs[i] = args[i]; + } + const funcReturn = original.unsafeCall(...newArgs); + if (funcReturn instanceof Promise) { + const func1 = funcReturn as Promise + func1.then((result: Any) => { + func.unsafeCall(null, result); + }).catch((err: Error) => { + func.unsafeCall(err, null); + }); + } else { + throw new Error('The function passed to callbackWrapper must return a Promise.'); + } + } } /** * Get the hash code of an object. - * @param { object } [object] - The object that need to get hash code. + * @param { RecordData } [obj] - The object that need to get hash code. * @returns { number } Return a hash code of an object. */ - export function getHash(obj: object): number { - let result: number = obj.$_hashCode(); + export function getHash(obj: RecordData): number { + if (obj === undefined || obj === null) { + throw createBusinessError(TypeErrorCodeId, `Parameter error. The type of Parameter must be object.`); + } + let result: number = Runtime.getHashCodeByValue(obj!); return result; } + + /** + * Generates a random binary UUID. + * @param { boolean } entropyCache - Whether to use cached UUIDs for generation + * @returns { Uint8Array | undefined } A Uint8Array containing the generated binary UUID, or undefined on failure. + */ + export function generateRandomBinaryUUID(entropyCache?: boolean): Uint8Array | undefined { + let arr = UtilHelper.generateRandomBinaryUUID((entropyCache == undefined) ? false : entropyCache) + return arr; + } + + /** + * Parses a UUID string into a binary representation (Uint8Array). + * @param { string } uuid - The UUID string to parse + * @returns { Uint8Array } A Uint8Array containing the 16-byte binary representation of the UUID. + */ + export function parseUUID(uuid: string): Uint8Array { + const format = new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); + if (!format.test(uuid)) { + throw createBusinessError(syntaxErrorCode, `Syntax Error.Invalid ${uuid} string`); + } + const hex = uuid.replace(new RegExp('-', 'g'), ''); + const uuidArray = new Uint8Array(16); + for (let i = 0; i < 16; i++) { + uuidArray[i] = parseInt(hex.substr(i * 2, 2), 16); + } + return uuidArray; + } } diff --git a/static_core/plugins/ets/sdk/api/@ohos.util.stream.ets b/static_core/plugins/ets/sdk/api/@ohos.util.stream.ets index 7e1be724aad1ad2542d63877d9ff5d44bb204154..baf8a8c9325312f08e4dc5284a751dcee284beb0 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.util.stream.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.util.stream.ets @@ -15,8 +15,7 @@ import { BusinessError } from '@ohos.base'; import { util } from './@ohos.util'; - -type CallbackFnType = (...params: Object[]) => void; +import buffer from '@ohos.buffer'; /** * The stream module provides a comprehensive set of stream processing capabilities, including four types of streams: @@ -39,33 +38,140 @@ export namespace stream { 'windows-1253', 'windows-1254', 'windows-1255', 'windows-1256', 'windows-1257', 'windows-1258', 'big5', 'euc-jp', 'iso-2022-jp', 'shift_jis', 'euc-kr', 'x-mac-cyrillic', 'utf-16be', 'utf-16le']; - const TYPE_ERROR_CODE_ID: number = 401; - const DO_READ_FUNC_ERROR_CODE_ID: number = 10200038; + + // BusinessError + const ERR_DOWRITE_NOT_IMPLEMENTED: BusinessError = + createBusinessError(10200035, 'The doWrite() method is not implemented.'); + const ERR_WRITE_AFTER_END: BusinessError = + createBusinessError(10200036, 'Write after end.'); + const ERR_STREAM_ALREADY_FINISHED: BusinessError = + createBusinessError(10200036, 'Stream already finished.'); + const ERR_MULTIPLE_CALLBACK: BusinessError = + createBusinessError(10200037, 'Callback called multiple times.'); + const ERR_DOREAD_NOT_IMPLEMENTED: BusinessError = + createBusinessError(10200038, 'The doRead() method is not implemented.'); + const ERR_DOTRANSFORM_NOT_IMPLEMENTED: BusinessError = + createBusinessError(10200039, 'The doTransform() method is not implemented.'); + + enum WritableEvent { + CLOSE = 'close', + DRAIN = 'drain', + ERROR = 'error', + FINISH = 'finish', + PIPE = 'pipe', + UNPIPE = 'unpipe', + } enum ReadableEvent { CLOSE = 'close', DATA = 'data', END = 'end', ERROR = 'error', - READABLE = 'readable', PAUSE = 'pause', + READABLE = 'readable', RESUME = 'resume', } - enum WritableEvent { - CLOSE = 'close', - DRAIN = 'drain', - ERROR = 'error', - FINISH = 'finish', - PIPE = 'pipe', - UNPIPE = 'unpipe', + function createBusinessError(code: int, message: string) { + let err: BusinessError = new BusinessError(); + err.name = 'BusinessError'; + err.message = message; + err.code = code; + return err; + } + + function processErrOrClose(stream: Readable, err: Error, sync: boolean = false): void { + if (stream.closed) { + return; + } + + if (err) { + stream.errored = true; + stream.closed = true; + if (sync) { + Promise.resolve().then((): void => { + emitError(stream, err); + emitClose(stream); + }); + } else { + emitError(stream, err); + emitClose(stream); + } + } + } + + function emitError(stream: Readable, err: Error): void { + if(stream.errorEmitted) { + return; + } + + stream.errorEmitted = true; + stream.listener?.emit(ReadableEvent.ERROR, err); + } + + function emitClose(stream: Readable): void { + if(stream.closedEmitted) { + return; + } + + stream.closedEmitted = true; + stream.listener?.emit(ReadableEvent.CLOSE); + } + + function onceWrapper(state: OnceState) { + if (!state.fired) { + state.target!.off(state.event!, state.wrapFn!); + state.fired = true; + return state.callback!.unsafeCall(); + } else { + const func = (): void => {}; + return func; + } + } + + function _onceWrap(target: EventEmitter, event: string, callback: Function): Function { + const onceState: OnceState = { + fired: false, + wrapFn: undefined, + target: undefined, + event: undefined, + callback: undefined + }; + onceState.fired = false; + onceState.wrapFn = undefined; + onceState.target = target; + onceState.event = event; + onceState.callback = callback; + + const wrapped = () => { + return onceWrapper(onceState); + }; + + onceState.wrapFn = wrapped; + + return wrapped; + } + + function advanceListener(stream: Writable, event: WritableEvent, callback: Function): void { + if (!stream.listener?.handlers || !stream.listener?.handlers.has(event)) { + stream.on(event, callback); + } else { + stream.listener?.handlers.get(event)?.push(callback); + } + } + + interface OnceState { + fired: boolean; + wrapFn: Function | undefined; + target: EventEmitter | undefined; + event: string | undefined; + callback: Function | undefined; } - export interface ReadablePipeStream { - write: Writable; - dataCallback: CallbackFnType; - drainCallback: CallbackFnType; - endCallback: CallbackFnType; + interface WritableBuffer { + encoding?: string; + chunk: string | Uint8Array; + callback: Function; } /** @@ -74,52 +180,56 @@ export namespace stream { */ export interface ReadableOptions { /** - * Specifies the encoding format of the data. If this parameter is provided, - * the readable stream decodes the data into a string in the specified encoding format. Default: utf8. - * If an invalid string is entered, a 401 exception is thrown in the Readable constructor. - * Supported encoding formats: utf-8, ibm866, iso-8859-2, iso-8859-3, iso-8859-4, iso-8859-5, iso-8859-6, - * iso-8859-7, iso-8859-8, iso-8859-8-i, iso-8859-10, iso-8859-13, iso-8859-14, iso-8859-15, koi8-r, koi8-u, - * macintosh, windows-874, windows-1250, windows-1251, windows-1252, windows-1253, windows-1254, windows-1255, - * windows-1256, windows-1257, windows-1258, x-mac-cyrillic, gbk, gb18030, big5, euc-jp, iso-2022-jp, shift_jis, - * euc-kr, utf-16be, utf-16le. - * - * @type { ?string } - */ + * Specifies the encoding format of the data. If this parameter is provided, + * the readable stream decodes the data into a string in the specified encoding format. Default: utf8. + * If an invalid string is entered, a 401 exception is thrown in the Readable constructor. + * Supported encoding formats: utf-8, ibm866, iso-8859-2, iso-8859-3, iso-8859-4, iso-8859-5, iso-8859-6, + * iso-8859-7, iso-8859-8, iso-8859-8-i, iso-8859-10, iso-8859-13, iso-8859-14, iso-8859-15, koi8-r, koi8-u, + * macintosh, windows-874, windows-1250, windows-1251, windows-1252, windows-1253, windows-1254, windows-1255, + * windows-1256, windows-1257, windows-1258, x-mac-cyrillic, gbk, gb18030, big5, euc-jp, iso-2022-jp, shift_jis, + * euc-kr, utf-16be, utf-16le. + * + */ encoding?: string; - highWatermark?: number; - doRead?: (size: number) => void; - } - - // TODO: to import from @ohos.util.ets - class StringDecoder { - constructor(encoding?: string) {} - - write(chunk: string | Uint8Array): string { - return 'This is the write function of the mock StringDecoder.' - } - - end(chunk?: string | Uint8Array): string { - return 'This is the end function of the mock StringDecoder.' - } } + /** + * EventEmitter to which event callbacks can be held. + * + */ class EventEmitter { - private handlers: Map>; + public handlers: Map>; + /** + * The EventEmitter constructor. + * + */ constructor() { - this.handlers = new Map>(); + this.handlers = new Map>(); } - on(event: string, callback: CallbackFnType): void { + /** + * Registering event messages. + * + * @param { string } event - Register event. + * @param { Function } callback - Event callbacks. + */ + on(event: string, callback: Function): void { if (!this.handlers.has(event)) { - const funcList: CallbackFnType[] = []; + const funcList = new Array(); this.handlers.set(event, funcList); } const funcList = this.handlers.get(event); funcList!.push(callback); } - off(event: string, callback: CallbackFnType): void { + /** + * Unregistering event messages. + * + * @param { string } event - Unregister event. + * @param { Function } callback - Event callbacks. + */ + off(event: string, callback: Function): void { if (this.handlers.has(event)) { const funcList = this.handlers.get(event); const pos = funcList!.findIndex((element) => element === callback); @@ -129,6 +239,11 @@ export namespace stream { } } + /** + * Clear event messages. + * + * @param { string } event - Clear event. + */ clear(event: string): void { if (this.handlers.has(event)) { const funcList = this.handlers.get(event); @@ -136,25 +251,39 @@ export namespace stream { } } + /** + * Emit event messages. + * + * @param { string } event - Emit event. + * @param { Object } [param] - The parameter of event callbacks. + */ emit(event: string, param?: Object): void { if (this.handlers.has(event)) { const funcList = this.handlers.get(event); - funcList!.forEach((callback: CallbackFnType) => { - if (param !== undefined) { - callback(param); - } else { - callback(); - } + funcList!.forEach((callback: Function) => { + callback.unsafeCall(param); }) } } + /** + * Judge the event is on or not. + * + * @param { string } event - The event to be judged is on or not. + * @returns { boolean } Event is on returns true, event is off returns false. + */ isOn(event: string): boolean { - return this.handlers.has(event)? this.handlers.get(event)!.length > 0 : false; + return this.handlers.has(event) ? this.handlers.get(event)!.length > 0 : false; } - listenerCount(event: string): number { - return this.handlers.has(event)? this.handlers.get(event)!.length : 0; + /** + * Find the event count in the listener. + * + * @param { string } event - The event to be found its count in the listener. + * @returns { int } The event count in the listener. + */ + listenerCount(event: string): int { + return this.handlers.has(event) ? this.handlers.get(event)!.length.toInt() : 0; } } @@ -163,116 +292,550 @@ export namespace stream { * */ export class Writable { - private writableObjectModeInner: boolean | undefined; - private writableHighWatermarkInner: number; - private writableInner: boolean | undefined; - private writableLengthInner: number | undefined; - private writableNeedDrainInner: boolean | undefined; - private writableCorkedInner: number = 0; + public doWriteFunc: ((chunk: string | Uint8Array, encoding: string, callback: Function) => void) | null; + public doWritevFunc: ((chunks: string[] | Uint8Array[], callback: Function) => void) | null; + public listener: EventEmitter | undefined; + + protected encoder = new util.TextEncoder(); + + private buffer: Array; + private closedInner: boolean | undefined; + private defaultEncoding: string | undefined; + private encoding: string | undefined; + private endCallback: Function | undefined; + private ending: boolean; + private erroredInner: Error | undefined | null; + private isDoWritevOverride: boolean; + private writableBufferLength: int; + private writableCb: int; + private writableCorkedInner: int; private writableEndedInner: boolean | undefined; private writableFinishedInner: boolean | undefined; - private erroredInner: Error | undefined | null; - private closedInner: boolean | undefined; + private writableHighWatermarkInner: int; + private writableInner: boolean | undefined; + private writableLengthInner: int | undefined; + private writableNeedDrainInner: boolean | undefined; + private writableObjectModeInner: boolean | undefined; + private writableSync: boolean; + private writeCallbackBuffer: Array; + private writaCallbackMove: boolean; + private writeCallbacks: Array; + private writing: boolean; /** * The Writable constructor. * */ - constructor() {} + constructor() { + this.doWriteFunc = this.noWriteOpes; + this.doWritevFunc = null; + this.listener = new EventEmitter(); + this.buffer = []; + this.closedInner = false; + this.defaultEncoding = DEFAULT_ENCODING; + this.encoding = DEFAULT_ENCODING; + this.endCallback = undefined; + this.ending = false; + this.erroredInner = null; + this.isDoWritevOverride = true; + this.writableBufferLength = 0; + this.writableCb = 0; + this.writableCorkedInner = 0; + this.writableEndedInner = false; + this.writableFinishedInner = false; + this.writableHighWatermarkInner = DEFAULT_HIGH_WATER_MARK; + this.writableInner = true; + this.writableLengthInner = 0; + this.writableNeedDrainInner = false; + this.writableObjectModeInner = false; + this.writableSync = true; + this.writeCallbackBuffer = []; + this.writaCallbackMove = false; + this.writeCallbacks = []; + this.writing = false; + + this.doInitialize((): void => { + this.listener?.emit(WritableEvent.ERROR); + }); + } - /** - * Returns boolean indicating whether it is in ObjectMode. - */ - get writableObjectMode(): boolean | undefined { - return this.writableObjectModeInner; + private getChunkLength(chunk: string | Uint8Array): int { + if (chunk instanceof Uint8Array) { + return (chunk as Uint8Array).byteLength.toInt(); + } else if ((chunk as string) === '') { + throw createBusinessError(401, 'Cannot read property byteLength of undefined'); + } else { + return (chunk as string).length.toInt(); + } } - /** - * Value of highWaterMark. - */ - get writableHighWatermark(): number | undefined { - return this.writableHighWatermarkInner; + private setEncoding(encoding: string): boolean { + let encodingLowCase: string = encoding.toLowerCase(); + if (encodingLowCase === 'utf8' || encodingLowCase === 'ascii') { + encoding = DEFAULT_ENCODING; + encodingLowCase = DEFAULT_ENCODING; + } + + if (ENCODING_SET.indexOf(encodingLowCase) !== -1) { + this.encoding = encodingLowCase; + try { + this.encoder = new util.TextEncoder(encodingLowCase); + } catch (e) { + this.throwError(e as Error); + this.errorFresh(); + return false; + } + return true; + } else { + this.throwError(createBusinessError(401, `Parameter error. The type of ${encoding} must be string.`)); + this.errorFresh(); + return false; + } } - /** - * Is true if it is safe to call writable.write(), which means the stream has not been destroyed, - * errored, or ended. - * - */ - get writable(): boolean | undefined { - return this.writableInner; + private writeUint8Array(chunk: Uint8Array, encoding?: string, callback?: Function): boolean { + const chunkLength = this.getChunkLength(chunk); + if (this.encoding !== this.defaultEncoding) { + this.setEncoding(this.defaultEncoding!); + } + this.writableLengthInner = this.writableLengthInner! + chunkLength; + const hasRemaining = this.writableLengthInner! < this.writableHighWatermark!; + let executed: boolean = false; + const fnBack = (...param: Object[]): void => { + if (!executed) { + executed = true; + if (param.length > 0) { + callback?.unsafeCall(param[0]); + } else { + callback?.unsafeCall(); + } + this.writing = false; + this.writableCb--; + + if (param.length > 0 && param[0] instanceof Error && !this.erroredInner) { + this.throwError(param[0] as Error); + this.errorFresh(); + return; + } + if (this.erroredInner) { + this.errorFresh(); + return; + } + + this.writableLengthInner = this.writableLengthInner! - chunkLength; + if (!this.writableSync) { + this.freshCacheV(); + this.afterWrite(); + } else { + Promise.resolve().then((): void => { + this.afterWrite(); + }); + } + } else { + if (!this.erroredInner) { + this.throwError(ERR_MULTIPLE_CALLBACK); + } + this.errorFresh(); + } + }; + if (this.writableCorkedInner === 0) { + if (!this.writing) { + this.writing = true; + this.writableCb++; + this.writableSync = true; + this.doWrite(chunk, encoding ?? DEFAULT_ENCODING, fnBack); + this.writableSync = false; + } else { + const buffInfo: WritableBuffer = { + encoding: encoding, + chunk: chunk, + callback: fnBack + }; + this.buffer.push(buffInfo); + if (callback instanceof Function) { + this.writeCallbackBuffer.push(callback); + } else { + this.writeCallbackBuffer.push((): void => {}); + } + this.writableBufferLength += chunkLength; + } + } else { + const buffInfo: WritableBuffer = { + encoding: encoding, + chunk: chunk, + callback: fnBack + }; + this.buffer.push(buffInfo); + if (callback instanceof Function) { + this.writeCallbackBuffer.push(callback); + } else { + this.writeCallbackBuffer.push((): void => {}); + } + this.writableBufferLength += chunkLength; + } + return this.erroredInner ? false : hasRemaining; } - /** - * Size of data this can be flushed, in bytes or objects. - */ - get writableLength(): number | undefined { - return this.writableLengthInner; + private writeString(chunk: string, encoding?: string, callback?: Function): boolean { + const chunkLength = this.getChunkLength(chunk); + if (this.encoding !== this.defaultEncoding) { + this.setEncoding(this.defaultEncoding!); + } + this.writableLengthInner = this.writableLengthInner! + chunkLength; + const hasRemaining = this.writableLengthInner! < this.writableHighWatermark!; + let executed: boolean = false; + const fb = (...param: Object[]): void => { + if (!executed) { + executed = true; + if (param.length > 0) { + callback?.unsafeCall(param[0]); + } else { + callback?.unsafeCall(); + } + this.writing = false; + this.writableCb--; + if (param.length > 0 && param[0] instanceof Error && !this.erroredInner) { + this.throwError(param[0] as Error); + this.errorFresh(); + return; + } + if (this.erroredInner) { + this.errorFresh(); + return; + } + this.writableLengthInner = this.writableLengthInner! - chunkLength; + if (!this.writableSync) { + this.freshCacheV(); + this.afterWrite(); + } else { + Promise.resolve().then((): void => { + this.afterWrite(); + }); + } + } else { + if (!this.erroredInner) { + this.throwError(ERR_MULTIPLE_CALLBACK); + } + this.errorFresh(); + } + }; + + if (this.writableCorkedInner === 0) { + if (!this.writing) { + this.writing = true; + this.writableCb++; + this.writableSync = true; + this.doWrite(chunk, encoding ?? DEFAULT_ENCODING, fb); + this.writableSync = false; + } else { + const buffInfo: WritableBuffer = { + encoding: encoding, + chunk: chunk, + callback: fb + }; + this.buffer.push(buffInfo); + if (callback instanceof Function) { + this.writeCallbackBuffer.push(callback); + } else { + this.writeCallbackBuffer.push((): void => {}); + } + this.writableBufferLength += chunkLength; + } + } else { + const buffInfo: WritableBuffer = { + encoding: encoding, + chunk: chunk, + callback: fb + }; + this.buffer.push(buffInfo); + if (callback instanceof Function) { + this.writeCallbackBuffer.push(callback); + } else { + this.writeCallbackBuffer.push((): void => {}); + } + this.writableBufferLength += chunkLength; + } + return this.erroredInner ? false : hasRemaining; } - /** - * If the buffer of the stream is full and true, otherwise it is false. - */ - get writableNeedDrain(): boolean | undefined { - return this.writableNeedDrainInner; + private errorFresh(): void { + if (this.writaCallbackMove) { + this.writeCallbacks.forEach(callback => { + if (callback instanceof Function) { + callback.unsafeCall(this.erroredInner ?? null); + } + }); + } + this.writeCallbackBuffer.forEach(callback => { + if (callback instanceof Function) { + callback.unsafeCall(this.erroredInner); + } + }); + this.endCallback?.unsafeCall(this.erroredInner); + this.endCallback = undefined; + this.buffer = []; + this.writeCallbacks = []; + this.writeCallbackBuffer = []; + this.writableBufferLength = 0; + this.writableLengthInner = 0; } - /** - * Number of times writable.uncork() needs to be called in order to fully uncork the stream. - */ - get writableCorked(): number | undefined { - return this.writableCorkedInner; - }; + private freshCache(): void { + if (this.writableCorkedInner !== 0) { + return; + } + let currentLength: int = this.buffer.length.toInt(); + let current: WritableBuffer[] = this.buffer.splice(0 as int, 1 as int); + this.writeCallbackBuffer.splice(0 as int, 1 as int); + while (currentLength > 0) { + this.writableBufferLength -= this.getChunkLength(current[0].chunk); + this.writing = true; + this.writableCb++; + this.writableSync = true; + this.doWrite(current[0].chunk, current[0].encoding ?? DEFAULT_ENCODING, current[0].callback); + this.writableSync = false; + if (!this.writing) { + currentLength = this.buffer.length.toInt(); + current = this.buffer.splice(0 as int, 1 as int); + } else { + break; + } + } + if (this.finishMayBe()) { + this.finishWrite(); + } + } - /** - * Whether Writable.end has been called. - */ - get writableEnded(): boolean | undefined { - return this.writableEndedInner; + private freshCacheV(): void { + if (this.writableCorkedInner !== 0) { + return; + } + if (this.buffer.length > 0) { + const bufferChunkLength = this.writableBufferLength; + this.writableBufferLength = 0; + this.writeCallbacks = []; + let executed: boolean = false; + const funCallback = (...param: Object[]): void => { + if (!executed) { + executed = true; + if (param.length > 0 && param[0] instanceof Error && !this.erroredInner) { + this.throwError(param[0] as Error); + this.errorFresh(); + return; + } + this.writing = false; + this.writableCb--; + if (this.erroredInner) { + this.errorFresh(); + return; + } + this.writableLengthInner = this.writableLengthInner! - bufferChunkLength; + this.writeCallbacks.forEach(callback => { + if (callback instanceof Function) { + callback.unsafeCall(); + } + }); + this.writeCallbacks = []; + + if (!this.writableSync) { + this.freshCacheV(); + this.afterWrite(); + } else { + Promise.resolve().then((): void => { + this.afterWrite(); + }); + } + } else { + if (!this.erroredInner) { + this.throwError(ERR_MULTIPLE_CALLBACK); + } + this.errorFresh(); + } + }; + const oldWriting = this.writing; + this.writing = true; + const oldWritableCb = this.writableCb; + this.writableCb++; + const oldWritableSync = this.writableSync; + this.writableSync = true; + this.writeCallbackBuffer.forEach((callback: Function ) => { + this.writeCallbacks.push(callback); + }); + this.writaCallbackMove = false; + let strChunks: string[] = new (string)[this.buffer.length]; + let arrayChunks: Uint8Array[] = new (Uint8Array)[this.buffer.length]; + let isString: boolean = false; + let index: int = 0; + this.buffer.forEach((value: WritableBuffer) => { + if (value.chunk instanceof string) { + strChunks[index] = value.chunk as string; + isString = true; + } else { + arrayChunks[index] = value.chunk as Uint8Array; + } + index++; + }); + if (isString) { + this.doWritev(strChunks, funCallback); + } else { + this.doWritev(arrayChunks, funCallback); + } + if (this.isDoWritevOverride) { + this.writableSync = false; + this.buffer = new Array(); + this.writeCallbackBuffer = new Array(); + this.writaCallbackMove = true; + if (this.finishMayBe()) { + this.finishWrite(); + } + } else { + this.isDoWritevOverride = false; + this.writableBufferLength = bufferChunkLength; + this.writing = oldWriting; + this.writableCb = oldWritableCb; + this.writableSync = oldWritableSync; + this.freshCache(); + } + } else { + if (this.finishMayBe()) { + this.finishWrite(); + } + } } - /** - * Whether Writable.end has been called and all buffers have been flushed. - */ - get writableFinished(): boolean | undefined { - return this.writableFinishedInner; + private finishMayBe(): boolean { + return !this.writing && this.writableCorkedInner === 0 && this.ending; } - /** - * Returns error if the stream has been destroyed with an error. - */ - get errored(): Error | undefined | null { - return this.erroredInner; + private finishWrite(): void { + if (!this.writableFinishedInner && this.writableCb === 0 && !this.erroredInner) { + if (this.writableSync) { + Promise.resolve().then((): void => { + this.writableFinishedInner = true; + this.endCallback?.unsafeCall(this.erroredInner); + this.listener?.emit(WritableEvent.FINISH); + }); + } else { + this.writableFinishedInner = true; + this.endCallback?.unsafeCall(this.erroredInner); + this.listener?.emit(WritableEvent.FINISH); + } + Promise.resolve().then((): void => { + if (!this.erroredInner) { + this.closedInner = true; + this.listener?.emit(WritableEvent.CLOSE); + } + }); + } } - /** - * Writable completes destroyfile and returns true, otherwise returns false. - */ - get closed(): boolean | undefined { - return this.closedInner; + private afterWrite(): void { + if (!this.finishMayBe() && this.writableNeedDrainInner && this.writableLengthInner === 0) { + this.writableNeedDrainInner = false; + this.listener?.emit(WritableEvent.DRAIN); + } } - /** - * writes a chunk to Writable and invokes callback when the chunk is flushed. The return value indicates - * whether the internal buffer of the Writable reaches the hightWaterMark. If true is returned, the buffer - * does not reach the hightWaterMark. If false is returned, the buffer has been reached. The write function - * should be called after the drain event is triggered. If the write function is called continuously, - * the chunk is still added to the buffer until the memory overflows - * - * @param { string | Uint8Array } [chunk] - Data to be written. - * @param { string } [encoding] - Encoding type. - * @param { Function } [callback] - Callback after writing. - * @returns { boolean } Write success returns true, write failure returns false. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types; - * 3.Parameter verification failed. + noWriteOpes(chunk: string | Uint8Array, encoding: string, callback: Function): void { + if (chunk instanceof string) { + this.doWritev([chunk as string], callback); + } else { + this.doWritev([chunk as Uint8Array], callback); + } + if (!this.isDoWritevOverride) { + this.isDoWritevOverride = true; + this.throwError(ERR_DOWRITE_NOT_IMPLEMENTED); + this.errorFresh(); + } + } + + private throwError(error: Error): void { + this.writableInner = false; + this.erroredInner = error; + if (this.listener && this.listener!.listenerCount(WritableEvent.ERROR) > 0) { + Promise.resolve().then((): void => { + this.listener?.emit(WritableEvent.ERROR, this.erroredInner as Error); + if (!this.closedInner) { + this.closedInner = true; + this.listener?.emit(WritableEvent.CLOSE); + } + }); + } else { + throw this.erroredInner as Error; + } + } + + private endInner(callback?: Function): void { + if (!this.writableFinishedInner && !this.writableEndedInner && this.writableCb === 0) { + Promise.resolve().then((): void => { + if (!this.erroredInner) { + this.writableFinishedInner = true; + callback?.unsafeCall(this.erroredInner); + this.listener?.emit(WritableEvent.FINISH); + this.closedInner = true; + this.listener?.emit(WritableEvent.CLOSE); + } + }); + } + } + + /** + * Writes a chunk to Writable and invokes callback when the chunk is flushed. The return value indicates + * whether the internal buffer of the Writable reaches the hightWaterMark. If true is returned, the buffer + * does not reach the hightWaterMark. If false is returned, the buffer has been reached. The write function + * should be called after the drain event is triggered. If the write function is called continuously, + * the chunk is still added to the buffer until the memory overflows. + * + * @param { string | Uint8Array } [chunk] - Data to be written. + * @param { string } [encoding] - Encoding type. + * @param { Function } [callback] - Callback after writing. + * @returns { boolean } Write success returns true, write failure returns false. + * @throws { BusinessError } 401 - Parameter error. * @throws { BusinessError } 10200035 - The doWrite method has not been implemented. * @throws { BusinessError } 10200036 - The stream has been ended. * @throws { BusinessError } 10200037 - The callback is invoked multiple times consecutively. - */ - write(chunk?: string | Uint8Array, encoding?: string, callback?: () => void): boolean { - return false; + */ + write(chunk?: string | Uint8Array, encoding?: string, callback?: Function): boolean { + if (encoding) { + this.setEncoding(encoding!); + } + if (chunk === null || chunk === undefined) { + throw createBusinessError(401, `Parameter error. The type of ${chunk} must be string or UintArray`); + } + if (this.ending && !this.erroredInner) { + this.erroredInner = ERR_WRITE_AFTER_END; + this.writableInner = false; + this.errorFresh(); + + Promise.resolve().then((): void => { + callback?.unsafeCall(this.erroredInner); + if (this.listener && this.listener!.listenerCount(WritableEvent.ERROR) > 0) { + this.listener!.emit(WritableEvent.ERROR, this.erroredInner as Error); + if (!this.closedInner) { + this.closedInner = true; + this.listener?.emit(WritableEvent.CLOSE); + } + } else { + throw this.erroredInner as Error; + } + }); + return false; + } + if (this.erroredInner) { + this.errorFresh(); + callback?.unsafeCall(this.erroredInner); + return false; + } + let flag: boolean = false; + if (chunk instanceof Uint8Array) { + flag = this.writeUint8Array(chunk, encoding ?? this.encoding, callback); + } else { + flag = this.writeString(chunk! as string, encoding ?? this.encoding, callback); + } + if (!flag) { + this.writableNeedDrainInner = true; + } + return flag; } /** @@ -282,28 +845,59 @@ export namespace stream { * @param { string } [encoding] - Encoding type. * @param { Function } [callback] - Callback after writing. * @returns { Writable } Returns the Writable object. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types; - * 3.Parameter verification failed. + * @throws { BusinessError } 401 - Parameter error. * @throws { BusinessError } 10200035 - The doWrite method has not been implemented. */ - end(chunk?: string | Uint8Array, encoding?: string, callback?: () => void): Writable { + end(chunk?: string | Uint8Array, encoding?: string, callback?: Function): Writable { + if (this.erroredInner) { + Promise.resolve().then((): void => {callback?.unsafeCall(this.erroredInner!)}); + return this; + } + if (this.writableFinishedInner) { + Promise.resolve().then((): void => {callback?.unsafeCall(this.erroredInner)}); + this.throwError(ERR_STREAM_ALREADY_FINISHED); + return this; + } else if (this.writableEndedInner) { + Promise.resolve().then((): void => {callback?.unsafeCall(this.erroredInner)}); + this.throwError(ERR_WRITE_AFTER_END); + return this; + } + + this.writableNeedDrainInner = false; + this.writableInner = false; + + if (chunk) { + this.write(chunk, encoding); + } + + if (this.writableCorkedInner > 0) { + this.writableCorkedInner = 1; + this.uncork(); + } + + this.endCallback = callback; + this.ending = true; + this.endInner(callback); + this.writableEndedInner = true; return this; } /** * Set the default encoding mode. * - * @param { string } [encoding] - Encoding type.Default: utf8. + * @param { string } [encoding] - Encoding type. Default: utf-8. * @returns { boolean } Setting successful returns true, setting failed returns false. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types; - * 3.Parameter verification failed. - */ + */ setDefaultEncoding(encoding?: string): boolean { - return false; + if (!encoding) { + return false; + } + + const ret = this.setEncoding(encoding); + if (ret) { + this.defaultEncoding = encoding; + } + return ret; } /** @@ -312,7 +906,8 @@ export namespace stream { * @returns { boolean } Setting successful returns true, setting failed returns false. */ cork(): boolean { - return false; + this.writableCorkedInner += 1; + return true; } /** @@ -321,42 +916,47 @@ export namespace stream { * @returns { boolean } Setting successful returns true, setting failed returns false. */ uncork(): boolean { - return false; + if (this.writableCorkedInner > 0) { + this.writableCorkedInner -= 1; + if (this.writableCorkedInner === 0 && !this.writing) { + this.freshCacheV(); + } + } + + return true; } /** * Registering Event Messages. * * @param { string } event - Register Event. - * @param { Callback } callback - event callbacks. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types; - * 3.Parameter verification failed. + * @param { Function } callback - event callbacks. */ - on(event: string, callback: CallbackFnType): void {} + on(event: string, callback: Function): void { + this.listener!.on(event, callback); + } /** * Cancel event message. * * @param { string } event - Register Event. - * @param { Callback } callback - event callbacks. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types. + * @param { Function } [callback] - event callbacks. */ - off(event: string, callback?: CallbackFnType): void {} + off(event: string, callback?: Function): void { + if (callback) { + this.listener!.off(event, callback); + } else { + this.listener!.clear(event); + } + } /** * This method is invoked by the Writable method during initialization and must not be invoked directly. * After the resource is initialized in the doInitialize method, the callback () method is invoked. * * @param { Function } callback - Callback when the stream has completed the initial. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types. */ - doInitialize(callback: () => void): void {} + doInitialize(callback: Function): void {} /** * Implemented by subclass inheritance. The implementation logic of flushing chunks in the buffer must not be @@ -365,12 +965,13 @@ export namespace stream { * @param { string | Uint8Array } [chunk] - Data to be written. * @param { string } [encoding] - Encoding type. * @param { Function } [callback] - Callback after writing. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types; - * 3.Parameter verification failed. */ - doWrite(chunk: string | Uint8Array, encoding: string, callback: () => void): void {} + doWrite(chunk: string | Uint8Array, encoding: string, callback: Function): void { + if (this.doWriteFunc !== null) { + this.doWriteFunc?.(chunk, encoding, callback); + return; + } + } /** * The implementation logic of flushing chunks in the buffer in batches should not be actively called. @@ -378,12 +979,86 @@ export namespace stream { * * @param { string[] | Uint8Array[] } [chunks] - Data to be written. * @param { Function } [callback] - Callback after writing. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types; - * 3.Parameter verification failed. */ - doWritev(chunks: string[] | Uint8Array[], callback: () => void): void {} + doWritev(chunks: string[] | Uint8Array[], callback: Function): void { + if (this.doWritevFunc !== null) { + this.doWritevFunc?.(chunks, callback); + return; + } + this.isDoWritevOverride = false; + } + + /** + * Returns boolean indicating whether it is in ObjectMode. + */ + get writableObjectMode(): boolean | undefined { + return this.writableObjectModeInner; + } + + /** + * Value of highWatermark. + */ + get writableHighWatermark(): int | undefined { + return this.writableHighWatermarkInner; + } + + /** + * Is true if it is safe to call writable.write(), which means + * the stream has not been destroyed or emitted 'error' or 'end'. + * + */ + get writable(): boolean | undefined { + return this.writableInner; + } + + /** + * Size of data that can be flushed, in bytes or objects. + */ + get writableLength(): int | undefined { + return this.writableLengthInner; + } + + /** + * If the buffer of the stream is full and true, otherwise it is false. + */ + get writableNeedDrain(): boolean | undefined { + return this.writableNeedDrainInner; + } + + /** + * Number of times Writable.uncork() needs to be called in order to fully uncork the stream. + */ + get writableCorked(): int | undefined { + return this.writableCorkedInner; + }; + + /** + * Whether Writable.end has been called. + */ + get writableEnded(): boolean | undefined { + return this.writableEndedInner; + } + + /** + * Whether Writable.end has been called and all buffers have been flushed. + */ + get writableFinished(): boolean | undefined { + return this.writableFinishedInner; + } + + /** + * Returns error if the stream has been destroyed with an error. + */ + get errored(): Error | undefined | null { + return this.erroredInner; + } + + /** + * Writable completes destroyfile and returns true, otherwise returns false. + */ + get closed(): boolean | undefined { + return this.closedInner; + } } /** @@ -392,46 +1067,110 @@ export namespace stream { * @syscap SystemCapability.Utils.Lang */ export class Readable { + public listener: EventEmitter | undefined; + public errorEmitted: boolean; + public closedEmitted: boolean; + protected encoder = new util.TextEncoder(); - protected stringDecoder = new StringDecoder(); + protected stringDecoderInner = new util.StringDecoder(); - private buf: Uint8Array; - private listener: EventEmitter | undefined; - private callbacks: Map>; + private buf: Array; + private bufIndex: int; + private awaitDrainWriters: Set; + private callbacks: Map>; + private readableDecoder: boolean; private isInitialized: boolean; private pauseInner: boolean; - private pipeWritableArrayInner: Array; - private readableObjectModeInner: boolean | undefined; + private readableHasPaused: boolean; + private pipeWritableArrayInner: Array; + private readableObjectModeInner: boolean; private readableInner: boolean; - private readableHighWatermarkInner: number; - private readableFlowingInner: boolean; - private readableLengthInner: number; - private readableEncodingInner: string; + private readableHighWatermarkInner: int; + private readableFlowingInner: boolean | null; + private readableLengthInner: int; + private readableEncodingInner: string | null; private readableEndedInner: boolean; - private erroredInner: Error | undefined; + private erroredInner: boolean; private closedInner: boolean | undefined; - private doReadFunc: ((size: number) => void) | null; + private dataListenning: boolean | undefined; + private readableListenning: boolean | undefined; + private readableHasFlowingInner: boolean; + private readableEndEmitted: boolean; + private readableNeedReadable: boolean | undefined; + private readableEmittedReadable: boolean; + private isReading: boolean | undefined; + private readableEmittedResume: boolean; + private readableSync: boolean; + private isReadingMore: boolean; + private multiAwaitDrain: boolean; + + /** + * The Readable constructor. + * + */ + constructor() { + this.initializeMembers(); + } + + /** + * The Readable constructor. + * + * @param { ReadableOptions } options - Provide options. + * @throws { BusinessError } 401 - Parameter error. + */ + constructor(options: ReadableOptions) { + this.initializeMembers(); + + if (options.encoding) { + this.readableEncodingInner = options.encoding!; + if (this.readableEncodingInner!.toLowerCase() === 'utf8') { + this.readableEncodingInner = DEFAULT_ENCODING; + } + + if (ENCODING_SET.findIndex((element) => element === this.readableEncodingInner!.toLowerCase()) === -1) { + throw createBusinessError(401, `Unknown encoding: ${this.readableEncodingInner!.toLowerCase()}`); + } + + this.stringDecoder = new util.StringDecoder(this.readableEncodingInner!); + } + } private initializeMembers(): void { - this.buf = new Uint8Array(); this.listener = new EventEmitter(); - this.callbacks = new Map>(); + this.errorEmitted = false; + this.closedEmitted = false; + this.buf = new Array(); + this.bufIndex = 0; + this.awaitDrainWriters = new Set(); + this.callbacks = new Map>(); + this.readableDecoder = false; this.isInitialized = false; this.pauseInner = false; - this.pipeWritableArrayInner = []; + this.readableHasPaused = false; + this.pipeWritableArrayInner = new Array(); this.readableObjectModeInner = false; this.readableInner = true; this.readableHighWatermarkInner = DEFAULT_HIGH_WATER_MARK; - this.readableFlowingInner = true; + this.readableFlowingInner = null; this.readableLengthInner = 0; - this.readableEncodingInner = DEFAULT_ENCODING; + this.readableEncodingInner = null; this.readableEndedInner = false; - this.erroredInner = undefined; + this.erroredInner = false; this.closedInner = undefined; - this.doReadFunc = (size: number): void => {}; + this.dataListenning = false; + this.readableListenning = undefined; + this.readableHasFlowingInner = false; + this.readableEndEmitted = false; + this.readableNeedReadable = false; + this.readableEmittedReadable = false; + this.isReading = undefined; + this.readableEmittedResume = false; + this.readableSync = true; + this.isReadingMore = false; + this.multiAwaitDrain = false; } - private computeNewReadableHighWatermark(readSize: number): number { + private computeNewReadableHighWatermark(readSize: int): int { readSize--; readSize |= readSize >>> 1; readSize |= readSize >>> 2; @@ -442,219 +1181,441 @@ export namespace stream { return readSize; } - private throwError(error: Error): void { - this.erroredInner = error; - if (this.listener !== undefined && this.listener!.listenerCount(WritableEvent.ERROR) > 0) { - setTimeout((): void => { - this.listener!.emit(WritableEvent.ERROR, error); - }); - } else { - throw error; - } + private flowInner(stream: Readable): void { + while (this.readableFlowing && stream.read() !== null); } - setEndType(): void { - Promise.resolve().then((): void => { - this.readableInner = false; - this.readableEndedInner = true; - if (this.listener !== undefined) { - this.listener!.emit(ReadableEvent.END); - } - }); + private emitReadableNextCycle(stream: Readable): void { + stream.read(0) } - /** - * The Readable constructor. - * - */ - constructor() { - this.initializeMembers(); - if (ENCODING_SET.findIndex((element) => element === this.readableEncodingInner.toLowerCase()) === -1) { - let err = new BusinessError(); - err.code = TYPE_ERROR_CODE_ID; - err.name='BusinessError'; - err.message = 'Parameter error. Incorrect parameter types.'; - throw err; + private emitReadableNow(stream: Readable): void { + stream.readableNeedReadable = false; + if (!stream.readableEmittedReadable) { + stream.readableEmittedReadable = true; + Promise.resolve().then((): void => { + stream.emitReadableInner(stream); + }); } - this.stringDecoder = new StringDecoder(this.readableEncodingInner); - this.encoder = new util.TextEncoder(this.readableEncodingInner); } - /** - * The Readable constructor. - * - * @param { ReadableOptions } options - Provide options. - * @throws { BusinessError } 401 - Parameter error. Possible causes: - * 1.Mandatory parameters are left unspecified; - * 2.Incorrect parameter types; - * 3.Parameter verification failed. - */ - constructor(options: ReadableOptions) { - this.initializeMembers(); - if (options.doRead !== undefined) { - this.doReadFunc = options.doRead!; + private emitReadableEnd(stream: Readable): void { + if (!stream.readableEndEmitted) { + stream.readableEndedInner = true; + Promise.resolve().then((): void => { + this.emitReadableEndNextCycle(stream); + }); } + } - if (options.encoding !== null && options.encoding!.toLowerCase() !== 'utf8') { - this.readableEncodingInner = options.encoding!; + private emitReadableEndNextCycle(stream: Readable): void { + if (!stream.erroredInner && !stream.closedInner + && !stream.readableEndEmitted && stream.readableLengthInner === 0) { + stream.readableEndEmitted = true; + stream.listener?.emit(ReadableEvent.END); } + } - if (options.highWatermark !== undefined) { - this.readableHighWatermarkInner = options.highWatermark!; + private emitReadableInner(stream: Readable): void { + if (!stream.erroredInner && (stream.readableLengthInner || stream.readableEndedInner)) { + stream.listener?.emit(ReadableEvent.READABLE); + stream.readableEmittedReadable = false; + } + if (!stream.readableFlowingInner && !stream.readableEndedInner + && stream.readableLengthInner <= stream.readableHighWatermarkInner) { + stream.readableNeedReadable = true; + } else { + stream.readableNeedReadable = false; } + stream.flowInner(stream) + } - if (ENCODING_SET.findIndex((element) => element === this.readableEncodingInner.toLowerCase()) === -1) { - let err = new BusinessError(); - err.code = TYPE_ERROR_CODE_ID; - err.name='BusinessError'; - err.message = 'Parameter error. Incorrect parameter types.'; - throw err; + private resumeInner(stream: Readable): void { + if (!this.isReading) { + stream.read(0); + } + stream.readableEmittedResume = false; + stream.listener?.emit(ReadableEvent.RESUME); + this.flowInner(stream); + if (stream.readableFlowingInner && !stream.isReading) { + stream.read(0); } - this.stringDecoder = new StringDecoder(this.readableEncodingInner); - this.encoder = new util.TextEncoder(this.readableEncodingInner); } - /** - * Returns boolean indicating whether it is in ObjectMode. - */ - get readableObjectMode(): boolean | undefined { - return this.readableObjectModeInner; + private endOfFlow(stream: Readable): void { + if (stream.readableEndedInner) { + return; + } + const stringDecoder = stream.readableDecoder ? stream.stringDecoderInner : null; + if (stringDecoder) { + const chunk = stringDecoder.end(); + if(chunk?.length) { + stream.buf.push(chunk); + stream.readableLengthInner += stream.readableObjectModeInner ? 1 : chunk.length.toInt(); + } + } + stream.readableEndedInner = true; + + if (stream.readableSync) { + stream.emitReadableNow(stream); + } else { + stream.readableNeedReadable = false; + stream.readableEmittedReadable = true; + stream.emitReadableInner(stream); + } } - /** - * Is true if it is safe to call readable.read(), which means - * the stream has not been destroyed or emitted 'error' or 'end'. - */ - get readable(): boolean { - if (!this.readableInner && this.readableEndedInner) { - return false; + private canBeReadMore(stream: Readable): void { + if (!stream.isReadingMore) { + stream.isReadingMore = true; + Promise.resolve().then((): void => { + while ((!stream.isReading && !stream.readableEndedInner) && + (stream.readableLengthInner < stream.readableHighWatermarkInner + || (stream.readableFlowingInner && stream.readableLengthInner === 0))) { + const preLen = stream.readableLengthInner; + stream.read(0); + if (preLen === stream.readableLengthInner) { + break; + } + stream.isReadingMore = false; + } + }) } - return true; } - /** - * Returns the value of highWatermark passed when creating this Readable. - */ - get readableHighWatermark(): number { - return this.readableHighWatermarkInner; + private bufHasSpace(stream: Readable): boolean { + return !stream.readableEndedInner && + (stream.readableLengthInner < stream.readableHighWatermarkInner || + stream.readableLengthInner === 0) } - /** - * This property reflects the current state of the readable stream null/true/false. - */ - get readableFlowing(): boolean { - return this.readableFlowingInner; + private pushChunkInner(stream: Readable, chunk: buffer.Buffer | string): void { + if (stream.readableFlowingInner && stream.dataListenning + && !stream.readableSync && stream.readableLengthInner === 0) { + this.awaitDrainWriters.clear(); + stream.listener?.emit(ReadableEvent.DATA, chunk); + } else { + if (chunk instanceof buffer.Buffer && (chunk as buffer.Buffer).length) { + stream.readableLengthInner += (chunk as buffer.Buffer).length.toInt(); + stream.buf.push(chunk); + } else if (chunk instanceof string && (chunk as string).length) { + stream.readableLengthInner += (chunk as string).length.toInt(); + stream.buf.push(chunk); + } + if (stream.readableNeedReadable) { + this.emitReadableNow(stream); + } + } + stream.canBeReadMore(stream); } - /** - * Size of the data that can be read, in bytes or objects. - */ - get readableLength(): number { - return this.readableLengthInner; + private pushByteModeChunk(stream: Readable, + chunk: Uint8Array | string | undefined | null, encoding?: string): boolean { + if (chunk === null) { + stream.isReading = false; + stream.endOfFlow(stream); + return false; + } + + let chunkBuffer: buffer.Buffer | undefined = undefined; + let chunkString: string | undefined = undefined; + let chunkIsBuffer: boolean = false; + if (chunk instanceof string) { + if (!encoding) { + encoding = DEFAULT_ENCODING; + } + if (stream.readableEncodingInner !== encoding) { + chunkBuffer = buffer.from(chunk, encoding as buffer.BufferEncoding); + chunkIsBuffer = true; + encoding = ''; + } else { + chunkString = chunk as string; + } + } else if (chunk instanceof Uint8Array) { + chunkBuffer = buffer.from(chunk); + chunkIsBuffer = true; + encoding = ''; + } + + if (!chunk || (chunkIsBuffer && chunkBuffer!.length < 0) || + (!chunkIsBuffer && (chunk as string).length < 0)) { + stream.isReading = false; + stream.canBeReadMore(stream); + return stream.bufHasSpace(stream); + } + + if (stream.readableEndedInner) { + processErrOrClose(stream, ERR_WRITE_AFTER_END); + return false; + } + + if (stream.erroredInner) { + return false; + } + + stream.isReading = false; + if (stream.readableDecoder && !encoding) { + if (chunkIsBuffer) { + chunkString = stream.stringDecoderInner.write(new Uint8Array(chunkBuffer!.buffer)); + chunkIsBuffer = false; + } + if (chunkString!.length === 0) { + stream.canBeReadMore(stream); + return stream.bufHasSpace(stream); + } + } + + if (chunkIsBuffer) { + stream.pushChunkInner(stream, chunkBuffer!); + } else { + stream.pushChunkInner(stream, chunkString!); + } + return stream.bufHasSpace(stream); } - /** - * Getter for the property encoding of a given Readable stream. The encoding property can be set using the - * readable.setEncoding() method. - */ - get readableEncoding(): string | null { - return this.readableEncodingInner; + private calculateReadSize(size?: int): int { + if (size !== undefined && (size! <= 0 || (this.readableLengthInner === 0 && this.readableEndedInner))) { + return 0; + } + + if (this.readableObjectModeInner) { + return 1; + } + + if (size === undefined) { + if(this.readableFlowingInner && this.readableLengthInner) { + const entry = this.buf[this.bufIndex]; + if (entry instanceof buffer.Buffer) { + return (entry as buffer.Buffer).length.toInt(); + } else if (entry instanceof string) { + return (entry as string).length.toInt(); + } else { + return 0; + } + } + return this.readableLengthInner; + } + + if (size !== undefined && size! <= this.readableLengthInner) { + return size!; + } + return this.readableEndedInner ? this.readableLengthInner : 0; } - /** - * Whether all data has been generated. - */ - get readableEnded(): boolean { - return this.readableEndedInner; + private readDataFromBuf(size: int): buffer.Buffer | string | null { + if (this.readableLengthInner === 0) { + return null; + } + + let num: int = this.bufIndex; + let res: buffer.Buffer | string | null = null; + + const buf = this.buf; + const len = buf.length.toInt(); + + if (this.readableObjectModeInner) { + res = buf[num]; + buf[num++] = null; + } else if (!size || size >= this.readableLengthInner) { + if (this.readableDecoder) { + res = ''; + while (num < len) { + res = (res as string) + buf[num]; + buf[num++] = null; + } + } else if (len - num === 0) { + res = buffer.alloc(0); + } else if (len - num === 1) { + res = buf[num]; + buf[num++] = null; + } else { + res = buffer.allocUninitializedFromPool(this.readableLengthInner.toInt()) as buffer.Buffer + let index: int = 0; + while (num < len) { + (buf[num] as buffer.Buffer).copy(res, index, 0 ,(buf[num] as buffer.Buffer).length); + index += (buf[num] as buffer.Buffer).length.toInt(); + buf[num++] = null; + } + } + } else if (size < (buf[num] as buffer.Buffer).length) { + res = (buf[num] as buffer.Buffer).subarray(0, size.toInt()); + buf[num] = (buf[num] as buffer.Buffer).subarray(size.toInt()); + } else if (size === (buf[num] as buffer.Buffer).length) { + res = buf[num]; + buf[num++] = null; + } else if (this.readableDecoder) { + res = ''; + while (num < len) { + const str: buffer.Buffer | string | null = buf[num]; + if (size > (str as string).length.toInt()) { + res = (res as string) + (str as string); + size -= (str as string).length.toInt(); + buf[num++] = null; + } else { + if (size === (buf[num] as string).length.toInt()) { + res = (res as string) + (str as string); + buf[num++] = null; + } else { + res = (res as string) + (str as buffer.Buffer).subarray(0, size.toInt()); + buf[num] = (str as buffer.Buffer).subarray(size.toInt()); + } + break; + } + } + } else { + res = buffer.allocUninitializedFromPool(size.toInt()) as buffer.Buffer + + const nRetLen = size.toInt(); + while (num < len) { + const data = buf[num]; + const dataLength = (data as buffer.Buffer).length + if (size > dataLength) { + (data as buffer.Buffer).copy(res, nRetLen - size.toInt(), 0, dataLength); + size -= dataLength + buf[num++] = null; + } else { + (data as buffer.Buffer).copy(res, nRetLen - size.toInt(), 0, size.toInt()); + if (size === dataLength) { + buf[num++] = null; + } else { + let remainBuf = buffer.allocUninitializedFromPool(dataLength - size.toInt()); + (data as buffer.Buffer).copy(remainBuf, nRetLen - size.toInt(), 0, size.toInt()); + buf[num] = remainBuf; + } + break; + } + } + } + + if (num === len) { + this.buf.length = 0; + this.bufIndex = 0; + } else if (num > 1024) { + this.buf.splice(0, num); + this.bufIndex = 0; + } else { + this.bufIndex = num; + } + + return res; } - /** - * Returns error if the stream has been destroyed with an error. - */ - get errored(): Error | undefined { - return this.erroredInner; + setEndType(): void { + Promise.resolve().then((): void => { + this.readableInner = false; + this.readableEndedInner = true; + this.listener?.emit(ReadableEvent.END); + }); } - /** - * Readable completes destroyfile and returns true, otherwise returns false. - */ - get closed(): boolean { - return this.closedInner === undefined? false : this.closedInner!; + private throwError(error: Error): void { + if (this.listener !== undefined && this.listener!.listenerCount(WritableEvent.ERROR) > 0) { + setTimeout((): void => { + this.listener!.emit(WritableEvent.ERROR, error); + }); + } else { + throw error; + } } /** * Reads a buffer of a specified size from the buffer. If the available buffer is sufficient, the result * of the specified size is returned. Otherwise, if Readable has ended, all remaining buffers are returned. * - * @param { number } size - Expected length of the data to be read. - * @returns { string | null } If no data is available to read, null is returned. + * @param { int } size - Expected length of the data to be read. + * @returns { Any } The return value is of the buffer and string types. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified; * 2.Incorrect parameter types; * 3.Parameter verification failed. * @throws { BusinessError } 10200038 - The doRead method has not been implemented. */ - read(size?: number): string | null { - if (this.doReadFunc === null && this.readableInner) { - this.readableInner = false; - Promise.resolve().then(() => { - this.closedInner = true; - let err = new BusinessError(); - err.code = DO_READ_FUNC_ERROR_CODE_ID; - err.name='BusinessError'; - err.message = 'The doRead() method is not implemented'; - this.erroredInner = err; - if (this.listener !== undefined) { - this.listener!.emit(ReadableEvent.ERROR, this.erroredInner!); - this.listener!.emit(ReadableEvent.CLOSE); - } - }); + read(size?: int): buffer.Buffer | string | null { + const originSize = size; + + if (size !== undefined && size! > this.readableHighWatermarkInner) { + this.readableHighWatermarkInner = this.computeNewReadableHighWatermark(size!); + } + + if (size !== 0) { + this.readableEmittedReadable = false; + } + + if (size === 0 && this.readableNeedReadable && ((this.readableHighWatermarkInner ? + this.readableLengthInner >= this.readableHighWatermarkInner : + this.readableLengthInner > 0) || this.readableEndedInner)) { + if (this.readableLengthInner === 0 && this.readableEndedInner) { + this.emitReadableEnd(this); + } else { + this.emitReadableNow(this); + } + return null; + } + + size = this.calculateReadSize(size); + + if (size === 0 && this.readableEndedInner) { + if (this.readableLengthInner === 0) { + this.emitReadableEnd(this); + } return null; } - if (size === undefined) { - size = this.readableLengthInner; + let needDoRead: boolean | undefined = this.readableNeedReadable; + + if (this.readableLengthInner === 0 || this.readableLengthInner - size! < this.readableHighWatermarkInner) { + needDoRead = true; + } + + if (this.isReading || this.readableEndedInner || this.erroredInner) { + needDoRead = false; + } else if (needDoRead) { + this.isReading = true; + this.readableSync = true; + if (this.readableLengthInner === 0) { + this.readableNeedReadable = true; + } + + try { + this.doRead(this.readableHighWatermarkInner); + } catch (error) { + processErrOrClose(this, error as Error, false); + } + this.readableSync = false; + + if (!this.isReading) { + size = this.calculateReadSize(originSize); + } + } + + let res : buffer.Buffer | string | null = null; + if (size! > 0) { + res = this.readDataFromBuf(size!); } - if (size > this.readableHighWatermarkInner) { - this.readableHighWatermarkInner = this.computeNewReadableHighWatermark(size); + if (res === null) { + this.readableNeedReadable = this.readableLengthInner <= this.readableHighWatermarkInner ? true : false; + size = 0; + } else { + this.readableLengthInner -= size!; + this.awaitDrainWriters.clear(); } - if (size > this.readableLengthInner) { - if (!this.readableFlowingInner) { - return null; - } else { - size = this.readableLengthInner; + if (this.readableLengthInner === 0) { + if (!this.readableEndedInner) { + this.readableNeedReadable = true; } - } - let buffer: string | null = null; - if (size > 0 && size <= this.readableLengthInner) { - this.readableLengthInner -= size; - buffer = this.stringDecoder.write(this.buf.slice(0 as int, size)); - this.buf = this.buf.slice(size); - if (this.doReadFunc !== null && this.listener !== undefined) { - this.listener!.emit(ReadableEvent.DATA, buffer); + if (originSize !== size && this.readableEndedInner) { + this.emitReadableEnd(this); } } - if ((!this.readableInner || size <= -1) && this.readableFlowingInner) { - return null; + if (res !== null && !this.erroredInner && !this.closedInner) { + this.listener?.emit(ReadableEvent.DATA, res); } - if (this.readableFlowingInner) { - try { - this.doRead(this.readableHighWatermarkInner); - } catch (error) { - this.readableInner = false; - this.readableEndedInner = true; - if (this.listener !== undefined) { - this.listener!.emit(ReadableEvent.ERROR, error); - this.listener!.emit(ReadableEvent.CLOSE); - } - } - } - return buffer; + return res; } /** @@ -663,6 +1624,18 @@ export namespace stream { * @returns { Readable } Return this object. */ resume(): Readable { + if (!this.readableFlowingInner) { + this.readableHasFlowingInner = true; + this.readableFlowingInner = this.readableListenning ? false : true; + if (!this.readableEmittedResume) { + this.readableEmittedResume = true; + Promise.resolve().then((): void => { + this.resumeInner(this); + }); + } + } + this.readableHasPaused = true; + this.pauseInner = false; return this; } @@ -672,6 +1645,13 @@ export namespace stream { * @returns { Readable } Return this object. */ pause(): Readable { + if (!this.readableHasFlowingInner || (this.readableHasFlowingInner && this.readableFlowingInner)) { + this.readableHasFlowingInner = true; + this.readableFlowingInner = false; + this.listener?.emit(ReadableEvent.PAUSE); + } + this.pauseInner = true; + this.readableHasPaused = true; return this; } @@ -685,44 +1665,47 @@ export namespace stream { * 2.Incorrect parameter types. */ setEncoding(encoding?: string): boolean { - if(this.readableEncodingInner === encoding) { - return true; - } - - if (encoding === undefined) { - this.readableEncodingInner = DEFAULT_ENCODING; - this.encoder = new util.TextEncoder(this.readableEncodingInner); - this.stringDecoder = new StringDecoder(this.readableEncodingInner); - return false; - } - - if (encoding!.toLowerCase() === 'utf8') { - encoding = 'utf-8'; + let res: boolean = true; + if (!encoding) { + encoding = DEFAULT_ENCODING; + res = false; } - - if (this.buf.length !== 0) { - console.error('stream: The buffer also has data, and encoding is not allowed'); - return false; + if (encoding.toLowerCase() === 'utf8') { + encoding = DEFAULT_ENCODING; } - let encodingLowCase = encoding!.toLowerCase(); + let encodingLowCase: string = encoding.toLowerCase(); if (ENCODING_SET.indexOf(encodingLowCase) !== -1) { try { - this.encoder = new util.TextEncoder(encoding); - this.stringDecoder = new StringDecoder(encoding); + this.stringDecoder = new util.StringDecoder(encoding); this.readableEncodingInner = encodingLowCase; + + // Iterate over current buffer to convert already stored Buffers: + let currentBuffer: string = ''; + for (let i: int = 0; i < this.buf.length.toInt(); i++) { + const decoder = this.stringDecoder as util.StringDecoder; + if (this.buf[i] instanceof string) { + currentBuffer += decoder.write(this.buf[i] as string); + } else { + currentBuffer += decoder.write(new Uint8Array((this.buf[i] as buffer.Buffer).buffer)); + } + } + this.buf.length = 0; + this.bufIndex = 0; + + if (currentBuffer !== '') { + this.buf.push(currentBuffer); + } + this.readableLengthInner = currentBuffer.length.toInt(); } catch (e) { this.throwError(e as Error); - return false; } - return true; } else { - let err = new BusinessError(); - err.name='BusinessError'; - err.message = `Parameter error. The type of ${encoding} must be string.`; - this.throwError(err); - return false; + this.throwError(createBusinessError(401, + `Parameter error. The type of ${encodingLowCase} must be string.`)); + res = false; } + return res; } /** @@ -731,7 +1714,7 @@ export namespace stream { * @returns { boolean } Pause state returns true, otherwise returns false. */ isPaused(): boolean { - return false; + return this.pauseInner || (this.readableHasFlowingInner && !this.readableFlowingInner); } /** @@ -746,7 +1729,112 @@ export namespace stream { * 3.Parameter verification failed. */ pipe(destination: Writable, options?: Object): Writable { - return new Writable(); + const src: Readable = this; + + if (src.pipeWritableArrayInner.length === 1 && !src.multiAwaitDrain) { + src.multiAwaitDrain = true; + } + + src.pipeWritableArrayInner.push(destination); + + // Writeable streams are automatically turned off when readable streams are turned off + const endCallback = (): void => { + destination.end(); + }; + if (src.readableEndEmitted) { + Promise.resolve().then((): void => { + endCallback(); + }); + } else { + src.listener?.on(ReadableEvent.END, _onceWrap(src.listener!, ReadableEvent.END, endCallback)); + } + + let ondrainCallback: undefined | Function = undefined; + let cleand: boolean = false; + + const cleanup = (): void => { + destination.listener?.clear(WritableEvent.CLOSE); + destination.listener?.clear(WritableEvent.FINISH); + destination.listener?.clear(WritableEvent.ERROR); + destination.listener?.clear(WritableEvent.DRAIN); + destination.listener?.clear(WritableEvent.UNPIPE); + src.listener?.clear(ReadableEvent.END); + src.listener?.clear(ReadableEvent.DATA); + cleand = true; + if(ondrainCallback && src.awaitDrainWriters.size > 0 && destination.writableNeedDrain) { + ondrainCallback!.unsafeCall(); + } + } + + const unpipeCallback = (): void => { + cleanup(); + }; + destination.on(WritableEvent.UNPIPE, unpipeCallback) + + const pause = (): void => { + if (!cleand) { + if (src.pipeWritableArrayInner.length === 1 && src.pipeWritableArrayInner[0] === destination) { + src.awaitDrainWriters.add(destination); + src.multiAwaitDrain = false; + } else if (src.pipeWritableArrayInner.length > 1) { + const objIdx: int = + src.pipeWritableArrayInner.findIndex((value: Writable) => value === destination).toInt(); + if (objIdx !== -1) { + src.awaitDrainWriters.add(destination); + } + } + src.pause(); + } + if (!ondrainCallback) { + const ondrainFunc: Function = (): void => { + src.awaitDrainWriters.delete(destination); + if (src.awaitDrainWriters.size === 0 && src.dataListenning) { + src.resume(); + } + }; + ondrainCallback = ondrainFunc; + destination.on(WritableEvent.DRAIN, ondrainCallback!); + } + }; + + const dataCallback = (data: Any): void => { + const writeData = new Uint8Array((data as buffer.Buffer).buffer); + const res = destination.write(writeData); + if (res === false) { + pause(); + } + }; + + const errorCallBack = (): void => { + destination.listener?.clear(WritableEvent.ERROR); + src.unpipe(destination); + }; + + const closeCallBack = (): void => { + destination.listener?.clear(WritableEvent.FINISH); + src.unpipe(destination); + } + + const finishCallback = (): void => { + destination.listener?.clear(WritableEvent.CLOSE); + src.unpipe(destination); + } + + src.on(ReadableEvent.DATA, dataCallback); + + advanceListener(destination, WritableEvent.ERROR, errorCallBack); + destination.listener?.on(WritableEvent.CLOSE, _onceWrap(destination.listener!, + WritableEvent.CLOSE, closeCallBack)); + destination.listener?.on(WritableEvent.FINISH, _onceWrap(destination.listener!, + WritableEvent.FINISH, finishCallback)); + destination.listener?.emit(WritableEvent.PIPE, src); + + if (destination.writableNeedDrain) { + pause(); + } else if (!src.readableFlowingInner) { + src.resume(); + } + return destination; } /** @@ -760,6 +1848,35 @@ export namespace stream { * 3.Parameter verification failed. */ unpipe(destination?: Writable): Readable { + if (this.pipeWritableArrayInner.length === 0) { + return this; + } + + if (!destination) { + // remove all pipelines + const destinations = this.pipeWritableArrayInner; + this.pipeWritableArrayInner = []; + this.pause(); + + for (let i: int = 0; i < destinations.length.toInt(); i++) { + destinations[i].listener?.emit(WritableEvent.UNPIPE, this); + } + return this; + } + + const objIdx: int = + this.pipeWritableArrayInner.findIndex((value: Writable) => value === destination).toInt(); + + if(objIdx === -1) { + return this; + } + + this.pipeWritableArrayInner.splice(objIdx, 1); + if (this.pipeWritableArrayInner.length === 0) { + this.pause(); + } + destination.listener?.emit(WritableEvent.UNPIPE, this); + return this; } @@ -767,23 +1884,104 @@ export namespace stream { * Registering Event Messages. * * @param { string } event - Registering Events. - * @param { Callback } callback - Event callback. + * @param { Function } callback - Event callback. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified; * 2.Incorrect parameter types. */ - on(event: string, callback: CallbackFnType): void {} + on(event: string, callback: Function): void { + if (!this.isInitialized) { + this.isInitialized = true; + this.doInitialize((...data: Object[]): void => {}); + } + + if (!this.callbacks.has(event)) { + const callbackList = new Array(); + this.callbacks.set(event, callbackList); + } + + this.callbacks.get(event)!.push(callback); + this.listener?.on(event, callback); + + if (event === ReadableEvent.DATA.toString()) { + this.dataListenning = true; + if (this.listener && this.listener!.listenerCount(ReadableEvent.READABLE) > 0) { + this.readableListenning = true; + } else { + this.readableListenning = false; + } + if (!this.readableHasFlowingInner || (this.readableHasFlowingInner && this.readableFlowingInner)) { + this.resume(); + } + } else if (event === ReadableEvent.READABLE.toString()) { + if (!this.readableEndEmitted || !this.readableListenning) { + this.readableHasFlowingInner = true; + this.readableListenning = true; + this.readableNeedReadable = true; + this.readableFlowingInner = false; + this.readableEmittedReadable = false; + if (this.readableLengthInner) { + this.emitReadableNow(this); + } else if (!this.isReading) { + Promise.resolve().then((): void => { + this.emitReadableNextCycle(this) + }); + } + } + } + } /** * Cancel event message. * * @param { string } event - Registering Events. - * @param { Callback } callback - Event callback. + * @param { Function } callback - Event callback. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified; * 2.Incorrect parameter types. */ - off(event: string, callback?: CallbackFnType): void {} + off(event: string, callback?: Function): void { + if (callback !== undefined) { + if (this.callbacks.has(event)) { + const callbackList = this.callbacks.get(event); + callbackList!.forEach((callbackFunc: Function) => { + if (callbackFunc === callback) { + this.listener?.off(event, callbackFunc); + } + }) + } + } else { + if (this.callbacks.has(event)) { + const calbackList = this.callbacks.get(event); + calbackList!.forEach((callbackFunc: Function) => { + this.listener?.off(event, callbackFunc); + }) + } + } + + if (event === ReadableEvent.READABLE.toString()) { + Promise.resolve().then((): void => { + if (this.listener!.listenerCount(ReadableEvent.READABLE) > 0) { + this.readableListenning = true; + } else { + this.readableListenning = false; + } + + if (!this.pauseInner && (this.readableHasPaused || this.readableEmittedResume)) { + this.readableFlowingInner = true; + this.readableHasFlowingInner = true; + } else if (this.dataListenning) { + this.resume(); + } else if (!this.readableListenning) { + this.readableFlowingInner = false; + this.readableHasFlowingInner = false; + } + }); + } else if (event === ReadableEvent.DATA.toString() && + this.listener!.listenerCount(ReadableEvent.DATA) === 0) { + this.dataListenning = false; + } + } /** * It may be implemented by child classes, and if so, will be called by the Readable class methods only. @@ -795,30 +1993,28 @@ export namespace stream { * 2.Incorrect parameter types; * 3.Parameter verification failed. */ - doInitialize(callback: () => void): void {} + doInitialize(callback: Function): void {} /** * The specific implementation of data production. It must not be actively called. * After data production, Readable.push should be called to push the produced data into the buffer. * If push is not called, doRead will not be called again. * - * @param { number } size - Expected length of the data to be read. + * @param { int } size - Expected length of the data to be read. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified; * 2.Incorrect parameter types; * 3.Parameter verification failed. */ - doRead(size: number): void { - if (this.doReadFunc !== null) { - this.doReadFunc!(size); - } + doRead(size: int): void { + throw ERR_DOREAD_NOT_IMPLEMENTED; } /** * Adds the generated data to the buffer. The return value indicates whether the data in the buffer has not * reached the highWaterMark (similar to Writable.write). If the chunk is null, all data has been generated. * - * @param { Uint8Array | string | null } chunk - Binary data to be stored in the buffer. + * @param { Uint8Array | string | undefined | null } chunk - Binary data to be stored in the buffer. * @param { string } [encoding] - Binary data encoding type. * @returns { boolean } If true is returned, the data in the buffer reaches the highWaterMark. Otherwise, the * data in the buffer does not reach the highWaterMark. @@ -826,69 +2022,107 @@ export namespace stream { * 1.Mandatory parameters are left unspecified; * 2.Incorrect parameter types. */ - push(chunk: Uint8Array | string | null, encoding?: string): boolean { - let bufferArr: Uint8Array; - if (encoding !== undefined) { - this.setEncoding(encoding!); + push(chunk: Uint8Array | string | undefined | null, encoding?: string): boolean { + return this.pushByteModeChunk(this, chunk, encoding); + } + + /** + * Returns boolean indicating whether it is in ObjectMode. + * + */ + get readableObjectMode(): boolean | undefined { + return this.readableObjectModeInner; + } + + /** + * Is true if it is safe to call readable.read(), which means + * the stream has not been destroyed or emitted 'error' or 'end'. + * + */ + get readable(): boolean { + if (this.readableInner) { + return this.readableInner && !this.errorEmitted && !this.readableEndEmitted; + } else if (!this.readableInner && this.readableEndedInner) { + return false; } + return true; + } - if (chunk instanceof string || chunk instanceof Uint8Array) { - if (chunk instanceof string) { - return false; - } else if (chunk instanceof Uint8Array) { - this.buf = chunk; - this.readableLengthInner += chunk.length; - } + /** + * Returns the value of highWatermark passed when creating this Readable. + * + */ + get readableHighWatermark(): int { + return this.readableHighWatermarkInner; + } - const highWaterMark = this.readableLengthInner <= this.readableHighWatermarkInner; - Promise.resolve().then((): void => { - try { - if (this.readableFlowingInner) { - !this.pauseInner && this.read(highWaterMark ? this.readableLengthInner : -1); - } else { - if (highWaterMark && this.doReadFunc !== null) { - this.doRead(this.readableHighWatermarkInner); - } - } - } catch (error) { - if (this.listener !== undefined) { - this.listener!.emit(ReadableEvent.ERROR, error); - this.listener!.emit(ReadableEvent.CLOSE); - } - } - if (this.listener !== undefined) { - this.listener!.emit(ReadableEvent.READABLE); - } - }); - return this.readableLengthInner < this.readableHighWatermarkInner; - } else if (chunk === null) { - if (!this.readableEndedInner && this.readableInner) { - if (!this.readableFlowingInner && this.listener !== undefined) { - this.listener!.emit(ReadableEvent.READABLE); - } + /** + * This property reflects the current state of the readable stream true/false. + * + */ + get readableFlowing(): boolean | null { + return this.readableFlowingInner; + } - this.readableInner = false; - Promise.resolve().then((): void => { - this.readableEndedInner = true; - this.pauseInner = true; - this.closedInner = true; - if (this.listener !== undefined) { - this.listener!.emit(ReadableEvent.END); - this.listener!.emit(ReadableEvent.CLOSE); - } - }); - } - return false; + /** + * Size of the data that can be read, in bytes or objects. + * + */ + get readableLength(): int { + return this.readableLengthInner; + } + + /** + * Getter for the property encoding of a given Readable stream. The encoding property can be set using the + * readable.setEncoding() method. + * + */ + get readableEncoding(): string | null { + return this.readableEncodingInner; + } + + /** + * Whether all data has been generated. + * + */ + get readableEnded(): boolean { + return this.readableEndEmitted; + } + + /** + * Returns error if the stream has been destroyed with an error. + * + */ + get errored(): boolean { + return this.erroredInner; + } + + set errored(value: boolean) { + this.erroredInner = value; + } + + /** + * Readable completes destroyfile and returns true, otherwise returns false. + * + */ + get closed(): boolean { + return this.closedInner === undefined ? false : this.closedInner!; + } + + set closed(value: boolean) { + this.closedInner = value; + } + + get stringDecoder(): Any { + return this.readableDecoder ? this.stringDecoderInner : null; + } + + set stringDecoder(value: util.StringDecoder | null) { + if (value) { + this.stringDecoderInner = value; + this.readableDecoder = true; } else { - this.readableInner = false; - let err = new BusinessError(); - err.name='BusinessError'; - err.message = 'ERR_INVALID_ARG_TYPE'; - this.erroredInner = err; - if (this.listener !== undefined) { - this.listener!.emit(ReadableEvent.ERROR, this.erroredInner!); - } - return false; + this.readableDecoder = false; } } } @@ -903,12 +2137,18 @@ export namespace stream { * @since 12 */ export class Duplex extends Readable { - private _writable: Writable; + _writable: Writable; + /** - * The Duplex constructor. + * The Duplex constructor. * */ - constructor() {} + constructor() { + super(); + this._writable = new Writable(); + this._writable.doWriteFunc = this.doWrite; + this._writable.doWritevFunc = this.doWritev; + } /** * writes a chunk to Writable and invokes callback when the chunk is flushed. The return value indicates @@ -930,8 +2170,8 @@ export namespace stream { * @throws { BusinessError } 10200039 - The doTransform method has not been implemented for * a class that inherits from Transform. */ - write(chunk?: string | Uint8Array, encoding?: string, callback?: () => void): boolean { - return false; + write(chunk?: string | Uint8Array, encoding?: string, callback?: Function): boolean { + return this._writable.write(chunk, encoding, callback); } /** @@ -948,8 +2188,23 @@ export namespace stream { * @throws { BusinessError } 10200039 - The doTransform method has not been implemented for * a class that inherits from Transform. */ - end(chunk?: string | Uint8Array, encoding?: string, callback?: () => void): Writable { - return new Writable(); + end(chunk?: string | Uint8Array, encoding?: string, callback?: Function): Writable { + super.setEndType(); + return this._writable.end(chunk, encoding, callback); + } + + on(event: string, callback: Function): void { + super.on(event, callback); + this._writable.on(event, callback); + } + + off(event: string, callback?: Function): void { + super.off(event); + this._writable.off(event, callback); + } + + getListener(): EventEmitter | undefined { + return this._writable.listener; } /** @@ -963,7 +2218,7 @@ export namespace stream { * 3.Parameter verification failed. */ setDefaultEncoding(encoding?: string): boolean { - return false; + return this._writable.setDefaultEncoding(encoding); } /** @@ -972,7 +2227,7 @@ export namespace stream { * @returns { boolean } Setting successful returns true, setting failed returns false. */ cork(): boolean { - return false; + return this._writable.cork(); } /** @@ -981,10 +2236,13 @@ export namespace stream { * @returns { boolean } Setting successful returns true, setting failed returns false. */ uncork(): boolean { - return false; + return this._writable.uncork(); } - doInitialize(callback: Function): void {} + doInitialize(callback: Function): void { + super.doInitialize(callback); + this._writable.doInitialize(callback); + } /** * Implemented by subclass inheritance. The implementation logic of flushing chunks in the buffer must not be @@ -998,7 +2256,9 @@ export namespace stream { * 2.Incorrect parameter types; * 3.Parameter verification failed. */ - doWrite(chunk: string | Uint8Array, encoding: string, callback: () => void): void {} + doWrite(chunk: string | Uint8Array, encoding: string, callback: Function): void { + this._writable.noWriteOpes(chunk, encoding, callback); + } /** * The implementation logic of flushing chunks in the buffer in batches should not be actively called. @@ -1011,38 +2271,70 @@ export namespace stream { * 2.Incorrect parameter types; * 3.Parameter verification failed. */ - doWritev(chunks: string[] | Uint8Array[], callback: () => void): void {} + doWritev(chunks: string[] | Uint8Array[], callback: Function): void {} get writableObjectMode(): boolean { - return false; + if (this._writable.writableObjectMode !== undefined) { + return this._writable.writableObjectMode as boolean; + } else { + return false; + } } - get writableHighWatermark(): number { - return 0; + get writableHighWatermark(): int { + if (this._writable.writableHighWatermark !== undefined) { + return this._writable.writableHighWatermark as int; + } else { + return 0; + } } get writable(): boolean { - return false; + if (this._writable.writable !== undefined) { + return this._writable.writable as boolean; + } else { + return false; + } } - get writableLength(): number { - return 0; + get writableLength(): int { + if (this._writable.writableLength !== undefined) { + return this._writable.writableLength as int; + } else { + return 0; + } } get writableNeedDrain(): boolean { - return false; + if (this._writable.writableNeedDrain !== undefined) { + return this._writable.writableNeedDrain as boolean; + } else { + return false; + } } - get writableCorked(): number { - return 0; + get writableCorked(): int { + if (this._writable.writableCorked !== undefined) { + return this._writable.writableCorked as int; + } else { + return 0; + } } get writableEnded(): boolean { - return false; + if (this._writable.writableEnded !== undefined) { + return this._writable.writableEnded as boolean; + } else { + return false; + } } get writableFinished(): boolean { - return false; + if (this._writable.writableFinished !== undefined) { + return this._writable.writableFinished as boolean; + } else { + return false; + } } } @@ -1057,7 +2349,51 @@ export namespace stream { * The Transform constructor. * */ - constructor() {} + constructor() { + super(); + this._writable.doWriteFunc = this.doWrite; + this._writable.doWritevFunc = this.doWritev; + } + + on(event: string, callback: Function): void { + super.on(event, callback); + } + + end(chunk?: string | Uint8Array, encoding?: string, callback?: Function): Writable { + if (!this.doTransform) { + throw ERR_DOTRANSFORM_NOT_IMPLEMENTED; + } + if (chunk instanceof Uint8Array) { + const chunkString = (this.stringDecoder as util.StringDecoder).write(chunk); + let encodingVal: string = encoding ? encoding : 'utf8' + let callbackVal: Function = callback ? callback : (...param: Object[]) => {} + this.doTransform(chunkString, encodingVal, callbackVal); + } else if (chunk instanceof string) { + let encodingVal: string = encoding ? encoding : 'utf8' + let callbackVal: Function = callback ? callback : (...param: Object[]) => {} + this.doTransform(chunk as string, encodingVal, callbackVal); + } + this.doFlush((...param: Object[]): void => { + if (param.length > 0) { + for (let idx: int = 0; idx < param.length.toInt(); idx++) { + const val = param[idx]; + if (val instanceof Uint8Array) { + this.push(val as Uint8Array, encoding); + } else if (val instanceof string) { + this.push(val as string, encoding); + } else { + this.push('', encoding); + } + } + } + }); + const write: Writable = super.end(chunk, encoding, callback); + return write; + } + + push(chunk: Uint8Array | string | undefined | null, encoding?: string): boolean { + return super.push(chunk, encoding); + } /** * Convert the input data. After the conversion, @@ -1073,7 +2409,9 @@ export namespace stream { * 2.Incorrect parameter types; * 3.Parameter verification failed. */ - doTransform(chunk: string, encoding: string, callback: () => void): void {} + doTransform(chunk: string, encoding: string, callback: Function): void { + throw ERR_DOTRANSFORM_NOT_IMPLEMENTED; + } /** * After all data is flushed to the write stream, @@ -1086,6 +2424,32 @@ export namespace stream { * 2.Incorrect parameter types; * 3.Parameter verification failed. */ - doFlush(callback: () => void): void {} + doFlush(callback: Function): void {} + + write(chunk?: string | Uint8Array, encoding?: string, callback?: Function): boolean { + if (chunk instanceof string) { + let executed: boolean = false; + const callBackFunction = (...param: Object[]): void => { + if (!executed) { + executed = true; + if (param.length > 0 && param[0] instanceof Error) { + this.getListener()?.emit(WritableEvent.ERROR, param[0] as Error); + } + } else { + this.getListener()?.emit(WritableEvent.ERROR, ERR_MULTIPLE_CALLBACK); + } + }; + let chunkVal: string = chunk ? chunk as string : '' + let encodingVal: string = encoding ? encoding : 'utf8' + this.doTransform(chunkVal, encodingVal, callBackFunction); + } + return super.write(chunk, encoding, callback); + } + + doRead(size: int): void {} + + doWrite(chunk: string | Uint8Array, encoding: string, callback: Function): void { + super.doWrite(chunk, encoding, callback); + } } } diff --git a/static_core/plugins/ets/sdk/api/@ohos.xml.ets b/static_core/plugins/ets/sdk/api/@ohos.xml.ets index a2be7f9098fbe43f21946bab20e50b99ff09e41b..2312da897a701f2a5d9b8863dd5753a18c8cf810 100644 --- a/static_core/plugins/ets/sdk/api/@ohos.xml.ets +++ b/static_core/plugins/ets/sdk/api/@ohos.xml.ets @@ -15,14 +15,14 @@ import buffer from '@ohos.buffer'; import { BusinessError } from "@ohos.base"; -const TypeErrorCodeId: number = 401; -const BufferOverFlowErrorCodeId: number = 10200062; -const IllegalPositionErrorCodeId: number = 10200063; -const EmptyErrorCodeId: number = 10200064; -const NoElementMatchErrorCodeId: number = 10200065; -const EncodingErrorCodeId: number = 10200066; - -function createBusinessError(code: number, message: string) { +const TypeErrorCodeId: int = 401; +const BufferOverFlowErrorCodeId: int = 10200062; +const IllegalPositionErrorCodeId: int = 10200063; +const EmptyErrorCodeId: int = 10200064; +const NoElementMatchErrorCodeId: int = 10200065; +const EncodingErrorCodeId: int = 10200066; + +function createBusinessError(code: int, message: string) { let err = new BusinessError(); err.code = code; err.name = 'BusinessError'; @@ -46,24 +46,24 @@ export namespace xml { WHITESPACE } - export class ParseInfoImpl implements ParseInfo { - depth: number - columnNumber: number - lineNumber: number - attributeCount: number - name: string - namespace_: string - prefix: string - text: string - emptyElementTag: boolean - whitespace: boolean - - override getAttributeCount(): number { return this.attributeCount; } - override getColumnNumber(): number { return this.columnNumber; } - override getDepth(): number { return this.depth; } - override getLineNumber(): number { return this.lineNumber; } + class ParseInfoImpl implements ParseInfo { + private depth: int + private columnNumber: int + private lineNumber: int + private attributeCount: int + private name: string + private namespace: string + private prefix: string + private text: string + private emptyElementTag: boolean + private whitespace: boolean + + override getAttributeCount(): int { return this.attributeCount; } + override getColumnNumber(): int { return this.columnNumber; } + override getDepth(): int { return this.depth; } + override getLineNumber(): int { return this.lineNumber; } override getName(): string { return this.name; } - override getNamespace(): string { return this.namespace_; } + override getNamespace(): string { return this.namespace; } override getPrefix(): string { return this.prefix; } override getText(): string { return this.text; } override isEmptyElementTag(): boolean { return this.emptyElementTag; } @@ -71,10 +71,10 @@ export namespace xml { } export interface ParseInfo { - getAttributeCount(): number - getColumnNumber(): number - getDepth(): number - getLineNumber(): number + getAttributeCount(): int + getColumnNumber(): int + getDepth(): int + getLineNumber(): int getName(): string getNamespace(): string getPrefix(): string @@ -102,31 +102,36 @@ export namespace xml { else { b = buffer.from(bufferIn) } - strIn = (encoding != undefined) ? b.toString(encoding! as buffer.BufferEncoding) : b.toString() + if (encoding != undefined) { + encoding = encoding.toLowerCase() + if (encoding !== "utf-8") { + throw createBusinessError(TypeErrorCodeId, 'Parameter error.Just support utf-8') + } + strIn = b.toString(encoding! as buffer.BufferEncoding) + } else { + strIn = b.toString() + } this.helper = new XmlParseHelper(strIn) } private helper: XmlParseHelper public parseXml(option: ParseOptions): void { - this.helper.supportDoctype = (option.supportDoctype != undefined) ? option.supportDoctype! : false - this.helper.ignoreNameSpace = (option.ignoreNameSpace != undefined) ? option.ignoreNameSpace! : false - this.helper.tagFunc_ = option.tagValueCallbackFunction - this.helper.attrFunc_ = option.attributeValueCallbackFunction - this.helper.tokenFunc_ = option.tokenValueCallbackFunction - this.helper.parseXml() + if (!this.helper.parseXml(option)) { + throw createBusinessError(TypeErrorCodeId, this.helper.xmlPullParserError()); + } } } - + type Attributes = Map interface XmlNode { name?: string attributes?: Attributes content?: string - lineNumber?: number - columnNumber?: number - pos?: number - depth?: number + lineNumber?: int + columnNumber?: int + pos?: int + depth?: int prefix?: string namespace_?: string type?: EventType @@ -164,7 +169,7 @@ export namespace xml { */ constructor(encoding: string) { if (encoding !== 'utf-8') { - throw new BusinessError(EncodingErrorCodeId, new Error('Parameter error.Just support utf-8')); + throw createBusinessError(EncodingErrorCodeId, 'Parameter error.Just support utf-8'); } this.buffer = new ArrayBuffer(MaxXmlLength); @@ -193,11 +198,11 @@ export namespace xml { this.checkPosition(); } - let temp = new StringBuilder(` ${name}=\"`); - this.writeEscaped(temp, value); - temp.append(`\"`); + let stringBuilder = new StringBuilder(` ${name}=\"`); + this.writeEscaped(stringBuilder, value); + stringBuilder.append(`\"`); this.type = XmlDynamicType.START_AND_ATTRIBUTES; - this.writeToBuffer(temp); + this.writeToBuffer(stringBuilder); } /** @@ -210,9 +215,9 @@ export namespace xml { this.checkPosition(); } - let temp = new StringBuilder(``); + let stringBuilder = new StringBuilder(``); this.type = XmlDynamicType.DECLARATION; - this.writeToBuffer(temp); + this.writeToBuffer(stringBuilder); } /** @@ -222,32 +227,24 @@ export namespace xml { * @throws {BusinessError} Throws an error if the `name` parameter is an empty string. */ public startElement(name: string): void { - this.checkEmptyParameter(name); - let temp = new StringBuilder(); - if (this.type === XmlDynamicType.START_AND_ATTRIBUTES) { - this.spliceNamespace(temp); - temp.append('>'); - } - if (this.type !== XmlDynamicType.NONE) { - this.nextItem(temp); - } + let stringBuilder = this.prepareBufferForNewItem(name); this.elementStack[this.depth].name = name; - temp.append('<'); + stringBuilder.append('<'); if (this.elementStack[this.depth].prefix !== '') { - temp.append(`${this.elementStack[this.depth].prefix}:`); + stringBuilder.append(`${this.elementStack[this.depth].prefix}:`); } else if (this.depth !== 0) { if (this.elementStack[this.depth - OffsetDepth].prefix !== '') { this.elementStack[this.depth].prefix = this.elementStack[this.depth - OffsetDepth].prefix; - temp.append(`${this.elementStack[this.depth].prefix}:`); + stringBuilder.append(`${this.elementStack[this.depth].prefix}:`); } } - temp.append(`${this.elementStack[this.depth].name}`); + stringBuilder.append(`${this.elementStack[this.depth].name}`); this.type = XmlDynamicType.START_AND_ATTRIBUTES; this.depth++; this.elementNum++; this.increaseStackLength(); - this.writeToBuffer(temp); + this.writeToBuffer(stringBuilder); } /** @@ -257,35 +254,35 @@ export namespace xml { */ public endElement(): void { if (this.elementNum < 1) { - throw new BusinessError(NoElementMatchErrorCodeId, - new Error('There is no match between the startElement and the endElement')); + throw createBusinessError(NoElementMatchErrorCodeId, + 'There is no match between the startElement and the endElement'); } this.elementNum--; - let temp = new StringBuilder(); + let stringBuilder = new StringBuilder(); if (this.type == XmlDynamicType.START_AND_ATTRIBUTES) { - this.spliceNamespace(temp); - temp.append('/>'); + this.spliceNamespace(stringBuilder); + stringBuilder.append('/>'); this.type = XmlDynamicType.OTHERS; this.depth--; - this.writeToBuffer(temp); + this.writeToBuffer(stringBuilder); return; } this.depth--; if (this.type !== XmlDynamicType.TEXT) { - this.nextItem(temp); + this.nextItem(stringBuilder); } - temp.append(''); - this.writeToBuffer(temp); + stringBuilder.append('>'); + this.writeToBuffer(stringBuilder); } /** @@ -299,10 +296,10 @@ export namespace xml { this.checkEmptyParameter(prefix); this.checkEmptyParameter(namespace); - let temp = new StringBuilder(); + let stringBuilder = new StringBuilder(); if (this.type === XmlDynamicType.START_AND_ATTRIBUTES) { - this.spliceNamespace(temp); - temp.append('>'); + this.spliceNamespace(stringBuilder); + stringBuilder.append('>'); } this.elementStack[this.depth].prefix = prefix; @@ -313,7 +310,83 @@ export namespace xml { } this.multNsp.set(this.depth, node); this.type = XmlDynamicType.OTHERS; - this.writeToBuffer(temp); + this.writeToBuffer(stringBuilder); + } + + /** + * Adds an empty XML element with the specified name to the output buffer. + * + * @param {string} name - The name of the empty XML element to be added. + * @throws {BusinessError} Throws an error if the `name` parameter is an empty string. + */ + public addEmptyElement(name: string): void { + let stringBuilder = this.prepareBufferForNewItem(name); + + stringBuilder.append(`<${name}/>`); + this.type = XmlDynamicType.OTHERS; + this.writeToBuffer(stringBuilder); + } + + /** + * Sets a comment in the XML output. + * + * @param {string} comment - The comment string to be added to the XML output. + * @throws {BusinessError} Throws an error if the `text` parameter is an empty string. + */ + public setComment(text: string): void { + let stringBuilder = this.prepareBufferForNewItem(text); + + stringBuilder.append(``); + this.type = XmlDynamicType.OTHERS; + this.writeToBuffer(stringBuilder); + } + + /** + * Sets the CDATA section for the XML content. + * + * @param {string} data - The string data to be wrapped in a Cdata section. + * @throws {BusinessError} Throws an error if the `text` parameter is an empty string. + */ + public setCdata(text: string): void { + let stringBuilder = this.prepareBufferForNewItem(text); + + text = text.replaceAll(']]>', ']]]]>'); + stringBuilder.append(``); + this.type = XmlDynamicType.OTHERS; + this.writeToBuffer(stringBuilder); + } + + /** + * Sets the text content for the current XML element. + * + * @param {string} text - The text content to set for the current XML element. + * @throws {BusinessError} Throws an error if the `text` parameter is an empty string. + */ + public setText(text: string): void { + this.checkEmptyParameter(text); + let stringBuilder = new StringBuilder(); + if (this.type === XmlDynamicType.START_AND_ATTRIBUTES) { + this.spliceNamespace(stringBuilder); + stringBuilder.append('>'); + } + + this.writeEscaped(stringBuilder, text); + this.type = XmlDynamicType.TEXT; + this.writeToBuffer(stringBuilder); + } + + /** + * Sets the document type (DOCTYPE) for the XML output. + * + * @param {string} text - The DOCTYPE declaration to be added to the XML output. + * @throws {BusinessError} Throws an error if the `text` parameter is an empty string. + */ + public setDocType(text: string): void { + let stringBuilder = this.prepareBufferForNewItem(text); + + stringBuilder.append(``); + this.type = XmlDynamicType.OTHERS; + this.writeToBuffer(stringBuilder); } /** @@ -366,11 +439,11 @@ export namespace xml { let iLenTemp = tempString.length; if (this.iPos + iLenTemp <= MaxXmlLength) { for (let i = 0; i < iLenTemp; i++) { - this.buffer.set((i + this.iPos) as int, tempString[i] as byte); + this.buffer.set((i + this.iPos) as int, tempString.charAt(i).toByte()); } this.iPos += iLenTemp; } else { - throw new BusinessError(BufferOverFlowErrorCodeId, new Error('The length has exceeded the upper limit')); + throw createBusinessError(BufferOverFlowErrorCodeId, 'The length has exceeded the upper limit'); } } @@ -420,7 +493,7 @@ export namespace xml { */ private checkEmptyParameter(para: string): void { if (para.length === 0) { - throw new BusinessError(EmptyErrorCodeId, new Error('Parameter error. Parameter cannot be empty')); + throw createBusinessError(EmptyErrorCodeId, 'Parameter error. Parameter cannot be empty'); } } @@ -428,433 +501,66 @@ export namespace xml { * Checks the position of the XML serializer and throws an error if the position is illegal. */ private checkPosition(): void { - throw new BusinessError(IllegalPositionErrorCodeId, new Error('Illegal position for xml')); + throw createBusinessError(IllegalPositionErrorCodeId, 'Illegal position for xml'); } - } - - export class XmlParseHelper { - - public supportDoctype: boolean = false - public ignoreNameSpace: boolean = false - public tagFunc_?: (name: string, value: string) => boolean - public attrFunc_?: (name: string, value: string) => boolean - public tokenFunc_?: (eventType: EventType, value: ParseInfo) => boolean - - private strXml_ : string = " " - private nodeMap = new Map(); - constructor(str: string) { - this.strXml_ = str - } - - private static readonly XML = "])*?/>", "m") - private static readonly startTagRegExp = new RegExp("<([\\w:]+)[\\s\\S]*?>", "m") - private static readonly closeTagRegExp = new RegExp("])*?>", "m") - private static readonly textTagRegExp = new RegExp(">[&|\\w:|\\s][^<>]+<", "m") - private static readonly attributeRegExp = new RegExp("([\\w:-]+)\\s*=\\s*(\"[^\"]*\"|'[^']*'|\\w+)\\s*", "m") - private static readonly stripRegExp = new RegExp("^['\"]|['\"]$","g") - private static readonly entityRegExp = new RegExp("\\w+) ('|\")(?.*?)('|\")>", "gm") - private static readonly nodeNameRegExp = new RegExp("^[\\w:]+$", "gm") - - private static readonly serviceTagRegExpMap = new Map([ - [EventType.INSTRUCTION, new RegExp("<\\?[^>]+?>","g")], - [EventType.COMMENT, new RegExp("","g")], - [EventType.ENTITY_REFERENCE, new RegExp("","g")], - [EventType.DOCDECL, new RegExp("","g")], - [EventType.CDSECT, new RegExp("","gm")] - ]) - - private static readonly xmlEscapeChars = new Map([ - [""", "\""], - ["&", "&"], - ["<", "<"], - [">", ">"], - ["'", "'"] - ]) - - private replaceEntities(node: XmlNode): XmlNode { - let str = (node.content != undefined) ? node.content! : "" - if (str == "") { - return node - } - if (this.supportDoctype && str.includes("&")) { - this.documentEntities.forEach((value: string, key: string) => { - let entityRef = new StringBuilder().append("&").append(key).toString() - if (str.includes(entityRef)) { - str = str.replace(entityRef, value) - node.type = EventType.ENTITY_REFERENCE - node.name = key - node.content = str - } - }) - } - return node - } - - private replaceXmlSpecChars(str: string): string { - for (let key of XmlParseHelper.xmlEscapeChars.keys()) { - str = str.replace(key, XmlParseHelper.xmlEscapeChars.get(key!) as string) - } - - return str - } - - private removeSpecTags(str: string): string { - if (str.includes("CDATA")) { - str = str.replace("", "") - } - if (str.includes("", "") - } - if (str.includes("", "") - } - if (str.includes("", "") - } - if (str.includes("", "") - } - return str - } - - public calcPosition(str: string, node: XmlNode) { - let lineEndMatch: NullishType - let pos: number = 1 - let end = str.length > 1 ? str.length + 1 : 1 - let lineChanged: boolean = false - node.lineNumber = 1 - node.columnNumber = 1 - node.pos = end - while ((pos < end) && (lineEndMatch = XmlParseHelper.lineEndRegExp.exec(str.substr(pos)))) { - node.lineNumber!++ - pos += lineEndMatch!.index + 1 - lineChanged = true - } - - if (lineChanged) { - node.columnNumber = (abs(end - pos) + 1) - } else { - node.columnNumber = end - } - } - - private parseToken(node: XmlNode): ParseInfo { - - let info = new ParseInfoImpl() - info.depth = (node.depth != undefined) ? node.depth! : 0 - info.columnNumber = (node.columnNumber != undefined) ? node.columnNumber! : 0 - info.lineNumber = (node.lineNumber != undefined) ? node.lineNumber! : 0 - info.attributeCount = (node.attributes != undefined) ? node.attributes!.size : 0 - info.emptyElementTag = (node.isSelfClose != undefined) ? (node.isSelfClose!) : false - info.whitespace = (node.content != undefined) ? (node.content! == " " || node.content! == "") : true - info.namespace_ = (node.namespace_ != undefined) ? node.namespace_! : "" - info.name = (node.name != undefined) ? node.name! : "" - info.prefix = (node.prefix != undefined) ? node.prefix! : "" - info.text = (node.content != undefined) ? this.replaceXmlSpecChars(node.content!) : "" - - return info - } - private startDocumentFound: boolean = false - private documentEntities = new Map() - private processServiceTag(regexp: RegExp, type: EventType) { - let m : NullishType - let pos: number = 0 - while (m = regexp.exec(this.strXml_.substr(pos))) { - pos = m!.index + 1 - for (let item of m!.result) { - let resType = type - if (type == EventType.INSTRUCTION && item.substr(0, XmlParseHelper.XML.length) == XmlParseHelper.XML) { - resType = EventType.START_DOCUMENT - pos = 1 - } else if (type == EventType.INSTRUCTION) { - pos += item.length - } - if (this.supportDoctype && (type == EventType.ENTITY_REFERENCE)) { - let match = XmlParseHelper.entityRegExp.exec(item) - if (match && match.result.length > 3) { - let entityName = match.result[1] - let entityValue = match.result[3] - this.documentEntities.set(entityName, entityValue) - } - } - let node : XmlNode = { - type: resType, - content: this.removeSpecTags(item), - isServiceTagNode: true, - isSelfClose: false, - pos: pos - } - this.calcPosition(this.strXml_.substr(0, pos), node) - if (resType == EventType.START_DOCUMENT) { - if (item != undefined) { - node.attributes = this.getAttributes(item, node) - } - this.startDocumentFound = true - } - if ((type != EventType.DOCDECL) || this.supportDoctype) { - this.nodeMap.set(node!.pos!, node!) - } - } - } - } - - private processAllStartTags() { - let pos: number = 0 - let m : NullishType - while (m = XmlParseHelper.startTagRegExp.exec(this.strXml_.substr(pos))) { - pos += m!.index - let item = m!.result[0] - pos += item.length - if (item && !item.match(XmlParseHelper.selfCloseTagRegExp)) { - let name = item.substr(1, item.length - 2).split(" ")[0] - if (!this.isNodeNameValid(name)) { - throw createBusinessError(TypeErrorCodeId,'The node name contains invalid characters'); - } - let node = { - pos: pos, - type: EventType.START_TAG, - name: name, - isSelfClose: false - } as XmlNode - node.attributes = this.getAttributes(item, node) - this.processNameSpace(node) - this.calcPosition(this.strXml_.substr(0, pos), node!) - this.nodeMap.set(node!.pos!, node!) - } - } - } - - private isNodeNameValid(name: string): boolean { - let m = name.match(XmlParseHelper.nodeNameRegExp) - if (m && m.result) { - return m.result.length > 0 - } - return false - } - - private processAllEndTags() { - let pos: number = 0 - let m : NullishType - while (m = XmlParseHelper.closeTagRegExp.exec(this.strXml_.substr(pos))) { - pos += m!.index - let item = m!.result[0] - pos += item.length - let name = item.substr(2, item.length - 3) - if (!this.isNodeNameValid(name)) { - throw createBusinessError(TypeErrorCodeId,'The node name contains invalid characters'); - } - if (item) { - let node = { - pos: pos, - name: name, - isSelfClose: false - } as XmlNode - node!.type = EventType.END_TAG - this.processNameSpace(node) - this.calcPosition(this.strXml_.substr(0, pos), node!) - this.nodeMap.set(node!.pos!, node!) - } - } - } - - private processAllSelfCloseTags() { - let m : NullishType - let pos: number = 0 - while (m = XmlParseHelper.selfCloseTagRegExp.exec(this.strXml_.substr(pos))) { - pos += m!.index - let item = m!.result[0] - if (item) { - pos += item.length - let name = item.substr(1, item.length - 3).split(" ")[0] - if (!this.isNodeNameValid(name)) { - throw createBusinessError(TypeErrorCodeId,'The node name contains invalid characters'); - } - let node = { - pos: pos, - type: EventType.START_TAG, - name: name, - isSelfClose: true - } as XmlNode - node.attributes = this.getAttributes(item, node) - this.processNameSpace(node) - this.calcPosition(this.strXml_.substr(0, pos), node!) - this.nodeMap.set(pos - 1, node) - let endNode = { - pos: pos, - type: EventType.END_TAG, - name: name, - isSelfClose: true, - columnNumber: node.columnNumber, - lineNumber: node.lineNumber, - prefix: node.prefix, - namespace_: node.namespace_ - } as XmlNode - this.nodeMap.set(pos, endNode) - } - } - } - - private processAllTextTags() { - let m : NullishType - let pos:number = 0 - while (m = XmlParseHelper.textTagRegExp.exec(this.strXml_.substr(pos))) { - pos += m!.index - for (let item of m!.result) { - pos += item.length - let content = item.substr(1, item.length - 2) - let node = { - pos: pos, - content: this.removeSpecTags(content) - } as XmlNode - node!.type = (node!.content!.replaceAll(" ", "") == "") ? EventType.WHITESPACE : EventType.TEXT - this.calcPosition(this.strXml_.substr(0, pos - 1), node!) - this.nodeMap.set(node!.pos!, node!) - } - } - } - - public parseXml(): void { - if (this.strXml_ == "") { - return - } - - XmlParseHelper.serviceTagRegExpMap.forEach((value: RegExp, key: EventType) => { - this.processServiceTag(value, key) - }) - - this.processAllStartTags() - this.processAllSelfCloseTags() - this.processAllTextTags() - this.processAllEndTags() - - let keys = Array.from(this.nodeMap.keys()).sort((a, b) => a - b) - - let name = "" - let depth = 0 - let res = true - keys.forEach((key: number) => { - let node = this.nodeMap.get(key)! - if (node.type == EventType.START_TAG) { - name = node!.name! - ++depth - } - if (node.type == EventType.END_TAG && (depth > 1)) { - --depth - } - node.depth = depth - if (res) { - node = this.replaceEntities(node) - res = this.processNode(node) - } - }) - - let endNode: XmlNode = { - name: '', - type: EventType.END_DOCUMENT, - depth: 0 - } - this.calcPosition(this.strXml_, endNode) - this.processNode(endNode) - } - - private isNullish(obj: NullishType): boolean { - return (obj == undefined) - } - - private getAttributes(str: string, node: XmlNode): Map { - let attributes = new Map() - let keyValueMatch : NullishType - while (!this.isNullish(keyValueMatch = str.match(XmlParseHelper.attributeRegExp))) { - if (node.attributes) { - attributes = node.attributes! - } - attributes!.set(this.replaceXmlSpecChars(keyValueMatch!.result[1] as string), - this.replaceXmlSpecChars(this.strip(keyValueMatch!.result[2] as string))) - str = str.replace(keyValueMatch!.result[0] as string,'') - } - return attributes - } - - private strip(val: string): string { - return val.replace(XmlParseHelper.stripRegExp, "") - } - - private nsMap = new Map() - private processNameSpace(node: XmlNode): void { - if (this.ignoreNameSpace) { - return - } - - if (node.attributes) { - node.attributes!.forEach ((value: string, key: string) => { - if (key! === XmlParseHelper.XMLNS) { - this.nsMap.set(XmlParseHelper.UNDESCORE, value) - } - if (key.indexOf(XmlParseHelper.XMLNS_PREFIX) === 0) { - let prefix = key!.replace(XmlParseHelper.XMLNS_PREFIX,'') - this.nsMap.set(prefix, value) - } - }) + /** + * Prepares the buffer for adding a new item by handling namespace line break and indentation. + * + * @param {StringBuilder} stringBuilder - The StringBuilder instance to append content to. + */ + private prepareBufferForNewItem(name: string): StringBuilder { + this.checkEmptyParameter(name); + let stringBuilder = new StringBuilder(); + if (this.type === XmlDynamicType.START_AND_ATTRIBUTES) { + this.spliceNamespace(stringBuilder); + stringBuilder.append('>'); } - if (node.name) { - let hasPrefix = (node.name!.indexOf(XmlParseHelper.SEMICOLON) > 0) - if (hasPrefix) { - let prefix = node.name!.split(XmlParseHelper.SEMICOLON)[0] - if ((prefix != XmlParseHelper.HTTP) && (prefix != XmlParseHelper.HTTPS) && this.nsMap.has(prefix)) { - node.name = node.name!.replace(`${prefix}:`,"") - node.prefix = prefix - node.namespace_ = this.nsMap.get(prefix) - } - } else { - if (this.nsMap.get(XmlParseHelper.UNDESCORE)) { - node.name = this.nsMap.get(XmlParseHelper.UNDESCORE) + node.name! - } - } + if (this.type !== XmlDynamicType.NONE) { + this.nextItem(stringBuilder); } - } - private onlyStartTag(node: XmlNode): boolean { - return !(node.type == EventType.END_TAG || node.type == EventType.TEXT || node.type == EventType.WHITESPACE) - } - - private isStartOrEndDocument(node: XmlNode): boolean { - return node.type == EventType.START_DOCUMENT || node.type == EventType.END_DOCUMENT + return stringBuilder; } + } - private processNode(node: XmlNode): boolean { - if (!this.isNullish(this.tagFunc_) && !(this.isNullish(node.name) && this.isNullish(node.content)) && !this.isStartOrEndDocument(node)) { - if (!this.tagFunc_!(node.name ? node.name! : "", node.content ? this.replaceXmlSpecChars(node.content!) : "")) { - return false - } - } - if (!this.isNullish(this.tokenFunc_)) { - let info = this.parseToken(node) - if (!this.tokenFunc_!(node.type != undefined ? node.type! : ((node.content == " ") ? EventType.WHITESPACE : EventType.TEXT), info)) { - return false - } - } - if (!this.isNullish(this.attrFunc_) && (!this.isNullish(node.attributes)) && (node.attributes!.size > 0) - && this.onlyStartTag(node) && !this.isStartOrEndDocument(node)) { - let res = true - node.attributes!.forEach((value: string, key: string) => { - if (res && !this.attrFunc_!(key, value)) { - res = false - return - } - }) - return res - } - return true + export class XmlParseHelper { + private static finalizationRegistry = new FinalizationRegistry( + (pointer: long) => { XmlParseHelper.releaseNative(pointer); } + ); + private nativeBinding: long; + + static { loadLibrary('ets_sdk_native'); } + + constructor(content: string) { + this.nativeBinding = XmlParseHelper.bindNative(content); + XmlParseHelper.finalizationRegistry.register(this, this.nativeBinding); + } + + public parseXml(option: ParseOptions): boolean { + return XmlParseHelper.parseXmlInner(this.nativeBinding, + option.ignoreNameSpace ?? false, + option.supportDoctype ?? false, + option.attributeValueCallbackFunction, + option.tagValueCallbackFunction, + option.tokenValueCallbackFunction + ); + } + + private static native bindNative(content: string): long; + private static native releaseNative(pointer: long): boolean; + private static native parseXmlInner(pointer: long, + ignoreNameSpace: boolean, + supportDoctype: boolean, + attributeValueCallbackFunction?: (name: string, value: string) => boolean, + tagValueCallbackFunction?: (name: string, value: string) => boolean, + tokenValueCallbackFunction?: (eventType: EventType, value: ParseInfo) => boolean + ): boolean; + private static native parserErrorInfo(pointer: long): string; + + public xmlPullParserError(): string { + return XmlParseHelper.parserErrorInfo(this.nativeBinding); } } @@ -889,7 +595,7 @@ export namespace xml { */ constructor(buffer: ArrayBuffer | DataView, encoding: string) { if (encoding !== 'utf-8') { - throw new BusinessError(TypeErrorCodeId, new Error('Parameter error.Just support utf-8')); + throw createBusinessError(TypeErrorCodeId, 'Parameter error.Just support utf-8'); } this.buffer = buffer; @@ -916,14 +622,14 @@ export namespace xml { public setAttributes(name: string, value: string): void { this.checkEmptyParameter(name); if (this.type !== XmlType.START_AND_ATTRIBUTES) { - throw new BusinessError(TypeErrorCodeId, new Error('Illegal position for attribute')); + throw createBusinessError(TypeErrorCodeId, 'Illegal position for attribute'); } - let temp = new StringBuilder(` ${name}=\"`); - this.writeEscaped(temp, value); - temp.append(`\"`); + let stringBuilder = new StringBuilder(` ${name}=\"`); + this.writeEscaped(stringBuilder, value); + stringBuilder.append(`\"`); this.type = XmlType.START_AND_ATTRIBUTES; - this.writeToBuffer(temp, 'SetAttributes'); + this.writeToBuffer(stringBuilder, 'SetAttributes'); } /** @@ -933,60 +639,60 @@ export namespace xml { */ public startElement(name: string): void { this.checkEmptyParameter(name); - let temp = new StringBuilder(); + let stringBuilder = new StringBuilder(); if (this.type === XmlType.START_AND_ATTRIBUTES) { - this.spliceNamespace(temp); - temp.append('>'); + this.spliceNamespace(stringBuilder); + stringBuilder.append('>'); } if (this.type !== XmlType.NONE && this.type !== XmlType.DECLARATION) { - this.nextItem(temp); + this.nextItem(stringBuilder); } this.elementStack[this.depth].name = name; - temp.append('<'); + stringBuilder.append('<'); if (this.elementStack[this.depth].prefix !== '') { - temp.append(`${this.elementStack[this.depth].prefix}:`); + stringBuilder.append(`${this.elementStack[this.depth].prefix}:`); } else if (this.depth !== 0) { if (this.elementStack[this.depth - DepthOffset].prefix !== '') { this.elementStack[this.depth].prefix = this.elementStack[this.depth - DepthOffset].prefix; - temp.append(`${this.elementStack[this.depth].prefix}:`); + stringBuilder.append(`${this.elementStack[this.depth].prefix}:`); } } - temp.append(`${this.elementStack[this.depth].name}`); + stringBuilder.append(`${this.elementStack[this.depth].name}`); this.type = XmlType.START_AND_ATTRIBUTES; this.depth++; this.increaseStackLength(); - this.writeToBuffer(temp, 'StartElement'); + this.writeToBuffer(stringBuilder, 'StartElement'); } /** * Handles the end of an XML element during parsing. */ public endElement(): void { - let temp = new StringBuilder(); + let stringBuilder = new StringBuilder(); if (this.type == XmlType.START_AND_ATTRIBUTES) { - this.spliceNamespace(temp); - temp.append('/>'); + this.spliceNamespace(stringBuilder); + stringBuilder.append('/>'); this.type = XmlType.OTHERS; this.depth--; - this.writeToBuffer(temp, 'EndElement'); + this.writeToBuffer(stringBuilder, 'EndElement'); return; } this.depth--; if (this.type !== XmlType.TEXT) { - this.nextItem(temp); + this.nextItem(stringBuilder); } - temp.append(''); - this.writeToBuffer(temp, 'EndElement'); + stringBuilder.append('>'); + this.writeToBuffer(stringBuilder, 'EndElement'); } /** @@ -998,13 +704,13 @@ export namespace xml { public setNamespace(prefix: string, namespace: string): void { this.checkEmptyParameter(prefix); if (namespace.length === 0) { - throw new BusinessError(TypeErrorCodeId, - new Error(`Parameter error. The type of ${namespace} must be string`)); + throw createBusinessError(TypeErrorCodeId, + `Parameter error. The type of ${namespace} must be string`); } - let temp = new StringBuilder(); + let stringBuilder = new StringBuilder(); if (this.type === XmlType.START_AND_ATTRIBUTES) { - this.spliceNamespace(temp); - temp.append('>'); + this.spliceNamespace(stringBuilder); + stringBuilder.append('>'); } this.elementStack[this.depth].prefix = prefix; @@ -1015,7 +721,94 @@ export namespace xml { } this.multNsp.set(this.depth, node); this.type = XmlType.OTHERS; - this.writeToBuffer(temp, 'SetNamespace'); + this.writeToBuffer(stringBuilder, 'SetNamespace'); + } + + /** + * Adds an empty XML element with the specified name to the output buffer. + * + * @param {string} name - The name of the empty XML element to be added. + */ + public addEmptyElement(name: string): void { + let stringBuilder = this.prepareBufferForNewItem(name); + + stringBuilder.append(`<${name}/>`); + this.type = XmlType.OTHERS; + this.writeToBuffer(stringBuilder, 'AddEmptyElement'); + } + + /** + * Sets the XML declaration for the current document. + * + * @throws {BusinessError} Throws an error if the declaration is set in an illegal position. + */ + public setDeclaration(): void { + if (this.isHasDecl) { + throw createBusinessError(TypeErrorCodeId, 'illegal position for declaration'); + } + + let stringBuilder = new StringBuilder(); + stringBuilder.append(``); + this.isHasDecl = true; + this.writeToBuffer(stringBuilder, 'SetDeclaration'); + } + + /** + * Sets a comment in the XML output. + * + * @param {string} comment - The comment string to be added to the XML output. + */ + public setComment(text: string): void { + let stringBuilder = this.prepareBufferForNewItem(text); + + stringBuilder.append(``); + this.type = XmlType.OTHERS; + this.writeToBuffer(stringBuilder, 'SetComment'); + } + + /** + * Sets the CDATA section for the XML content. + * + * @param {string} data - The string data to be wrapped in a Cdata section. + */ + public setCDATA(text: string): void { + let stringBuilder = this.prepareBufferForNewItem(text); + + text = text.replaceAll(']]>', ']]]]>'); + stringBuilder.append(``); + this.type = XmlType.OTHERS; + this.writeToBuffer(stringBuilder, 'SetCdata'); + } + + /** + * Sets the text content for the current XML element. + * + * @param {string} text - The text content to set for the current XML element. + */ + public setText(text: string): void { + this.checkEmptyParameter(text); + let stringBuilder = new StringBuilder(); + if (this.type === XmlType.START_AND_ATTRIBUTES) { + this.spliceNamespace(stringBuilder); + stringBuilder.append('>'); + } + + this.writeEscaped(stringBuilder, text); + this.type = XmlType.TEXT; + this.writeToBuffer(stringBuilder, 'SetText'); + } + + /** + * Sets the document type (DOCTYPE) for the XML output. + * + * @param {string} text - The DOCTYPE declaration to be added to the XML output. + */ + public setDocType(text: string): void { + let stringBuilder = this.prepareBufferForNewItem(text); + + stringBuilder.append(``); + this.type = XmlType.OTHERS; + this.writeToBuffer(stringBuilder, 'SetDocType'); } /** @@ -1060,15 +853,15 @@ export namespace xml { if (this.buffer.byteLength > this.iPos + iLenTemp - BufferIndexOffset) { for (let i = 0; i < iLenTemp; i++) { if (this.buffer instanceof ArrayBuffer) { - (this.buffer as ArrayBuffer).set((i + this.iPos) as int, tempString[i] as byte); + (this.buffer as ArrayBuffer).set((i + this.iPos) as int, tempString.charAt(i).toByte()); } else { ((this.buffer as DataView).buffer as ArrayBuffer).set((i + this.iPos) as int, - tempString[i] as byte); + tempString.charAt(i).toByte()); } } this.iPos += iLenTemp; } else { - throw new BusinessError(TypeErrorCodeId, new Error(`XmlSerializer:: ${name} memcpy_s failed`)); + throw createBusinessError(TypeErrorCodeId, `XmlSerializer:: ${name} memcpy_s failed`); } } @@ -1119,8 +912,27 @@ export namespace xml { */ private checkEmptyParameter(para: string): void { if (para.length === 0) { - throw new BusinessError(TypeErrorCodeId, new Error('Parameter error. Parameter cannot be empty')); + throw createBusinessError(TypeErrorCodeId, 'Parameter error. Parameter cannot be empty'); + } + } + + /** + * Prepares the buffer for adding a new item by handling namespace line break and indentation. + * + * @param {StringBuilder} stringBuilder - The StringBuilder instance to append content to. + */ + private prepareBufferForNewItem(name: string): StringBuilder { + this.checkEmptyParameter(name); + let stringBuilder = new StringBuilder(); + if (this.type === XmlType.START_AND_ATTRIBUTES) { + this.spliceNamespace(stringBuilder); + stringBuilder.append('>'); + } + if (this.type !== XmlType.NONE) { + this.nextItem(stringBuilder); } + + return stringBuilder; } } } diff --git a/static_core/plugins/ets/sdk/api/UtilHelper.ets b/static_core/plugins/ets/sdk/api/UtilHelper.ets index e4db7d89201cda5f1a20f31a732d589e1bbecc66..b240352775f1254652c72b862efe59df39e9b1d1 100644 --- a/static_core/plugins/ets/sdk/api/UtilHelper.ets +++ b/static_core/plugins/ets/sdk/api/UtilHelper.ets @@ -23,7 +23,9 @@ class UtilHelper { loadLibrary("ets_sdk_native") } - internal static native generateRandomUUID(entropyCache: boolean): string + public static native generateRandomUUID(entropyCache: boolean): string + + public static native generateRandomBinaryUUID(entropyCache: boolean): Uint8Array } // class UtilHelper } // namespace util diff --git a/static_core/plugins/ets/sdk/arkts/@arkts.collections.ets b/static_core/plugins/ets/sdk/arkts/@arkts.collections.ets index b85f344dcd5e65da1f4eed4717a8f803772ec981..335c6ffc7852623d823c7b9371659e4448df7556 100644 --- a/static_core/plugins/ets/sdk/arkts/@arkts.collections.ets +++ b/static_core/plugins/ets/sdk/arkts/@arkts.collections.ets @@ -16,11 +16,19 @@ import { BusinessError } from "@ohos.base" -const OutOfBoundsErrorCodeId: number = 10200001; -const TypeErrorCodeId: number = 401; +const OutOfBoundsErrorCodeId: int = 10200001; +const TypeErrorCodeId: int = 401; + +function createBusinessError(code: int, message: string) { + let err = new BusinessError(); + err.code = code; + err.name = 'BusinessError'; + err.message = message; + return err; +} export namespace collections { - final class BitVectorIterator implements IterableIterator { + final class BitVectorIterator implements IterableIterator { private index: int = 0; private parent: BitVector; @@ -28,15 +36,15 @@ export namespace collections { this.parent = parent; } - override next(): IteratorResult { + override next(): IteratorResult { if (this.parent.length === 0 || this.index >= this.parent.length) { - return new IteratorResult(); + return new IteratorResult(); } - return new IteratorResult(this.parent[this.index++]); + return new IteratorResult(this.parent[this.index++]); } - override $_iterator(): IterableIterator { + override $_iterator(): IterableIterator { return this; } } @@ -49,10 +57,10 @@ export namespace collections { // ↑ // elementId // |---------------buffer--------------------| - elementId: number; - index: number; + elementId: int; + index: int; - constructor(elementId: number, index: number) { + constructor(elementId: int, index: int) { this.elementId = elementId; this.index = index } @@ -64,7 +72,7 @@ export namespace collections { * it must be synchronized externally. * */ - export class BitVector implements Iterable { + export class BitVector implements Iterable { static readonly BIT_SET_LENGTH = 32; // ArkTS Specification, Release 1.1.0 private buffer: Array; @@ -73,15 +81,13 @@ export namespace collections { /** * A constructor used to create a BitVector object. * - * @param { number } length - The length of BitVector object. + * @param { int } length - The length of BitVector object. */ - public constructor(length: number) { - this.integerCheck("length", length); - + public constructor(length: int) { if (length === 0) { this.buffer = new Array(); } else { - this._length = length as int; + this._length = length; this.buffer = Array.create(this.getUsedCapacity(), 0); } } @@ -90,21 +96,17 @@ export namespace collections { * Gets the element number of the BitVector. This is a number one higher than the highest index in the bit vector. * It can be changed by resize(). */ - public get length(): number { + public get length(): int { return this._length; } /** * Appends the bit element to the end of this bit vector. * - * @param { number } element - Element to be appended to this bit vector (0 means 0, else means 1). + * @param { int } element - Element to be appended to this bit vector (0 means 0, else means 1). * @returns { boolean } The boolean type, returns true if the addition is successful, and returns false if it fails. */ - public push(element: number): boolean { - if (this.length >= Int.MAX_VALUE) { - return false; - } - + public push(element: int): boolean { let index = this.computeElementIdAndBitId(this._length); this.checkAndIncrement(index.elementId); @@ -116,9 +118,9 @@ export namespace collections { /** * Retrieves and removes the bit element to the end of this bit vector. * - * @returns { number | undefined } The boolean type, if the bit push successfully, return true, else return false. + * @returns { int | undefined } The boolean type, if the bit push successfully, return true, else return false. */ - public pop(): number | undefined { + public pop(): int | undefined { if (this._length === 0) { return undefined; } @@ -129,11 +131,11 @@ export namespace collections { /** * Returns the item at that index. * - * @param { number } index - The zero-based index of the desired code unit. - * @returns { number } The element in the bitVector matching the given index. + * @param { int } index - The zero-based index of the desired code unit. + * @returns { int } The element in the bitVector matching the given index. * @throws { BusinessError } 10200001 - If the index is out of range. */ - public $_get(index: number): number { + public $_get(index: int): int { this.checkIndex(index); return this.getBitUnsafe(index); @@ -142,11 +144,11 @@ export namespace collections { /** * Sets the value of item at that index. * - * @param { number } index - The zero-based index of the desired code unit. - * @param { number } value - The value to set at the given index. + * @param { int } index - The zero-based index of the desired code unit. + * @param { int } value - The value to set at the given index. * @throws { BusinessError } 10200001 - If the index is out of range. */ - public $_set(index: number, value: number): void { + public $_set(index: int, value: int): void { this.checkIndex(index); this.setBitUnsafe(index, value); @@ -154,9 +156,9 @@ export namespace collections { /** * Check if bit vector contains a particular bit element. * - * @param { number } element - Element to be contained (0 means 0, else means 1). - * @param { number } fromIndex - The starting position of the index, containing the value at that index position. - * @param { number } toIndex - The end of the index, containing the value at that index. + * @param { int } element - Element to be contained (0 means 0, else means 1). + * @param { int } fromIndex - The starting position of the index, containing the value at that index position. + * @param { int } toIndex - The end of the index, containing the value at that index. * @returns { boolean } The boolean type, if bit vector contains the specified element, return true, else return false. * @throws { BusinessError } 401 - Parameter error. Possible causes: @@ -164,30 +166,30 @@ export namespace collections { * 2.Incorrect parameter types. * @throws { BusinessError } 10200001 - The value of fromIndex or toIndex is out of range. */ - public has(element: number, fromIndex: number, toIndex: number): boolean { + public has(element: int, fromIndex: int, toIndex: int): boolean { this.checkRange(fromIndex, toIndex); - let from = this.computeElementIdAndBitId(fromIndex); - let to = this.computeElementIdAndBitId(toIndex); + let fromElem = this.computeElementIdAndBitId(fromIndex); + let toElem = this.computeElementIdAndBitId(toIndex); - if (from.elementId === to.elementId) { - return this.checkBit(this.buffer[from.elementId], from.index, to.index - 1, element); + if (fromElem.elementId === toElem.elementId) { + return this.checkBit(this.buffer[fromElem.elementId], fromElem.index, toElem.index - 1, element); } - if (this.checkBit(this.buffer[from.elementId], from.index, BitVector.BIT_SET_LENGTH - 1, element)) { + if (this.checkBit(this.buffer[fromElem.elementId], fromElem.index, BitVector.BIT_SET_LENGTH - 1, element)) { return true; } - from.elementId++; - while (from.elementId < to.elementId) { - if (this.checkBit(this.buffer[from.elementId], 0, BitVector.BIT_SET_LENGTH - 1, element)) { + fromElem.elementId++; + while (fromElem.elementId < toElem.elementId) { + if (this.checkBit(this.buffer[fromElem.elementId], 0, BitVector.BIT_SET_LENGTH - 1, element)) { return true; } - from.elementId++; + fromElem.elementId++; } - if (to.index > 0) { - return this.checkBit(this.buffer[to.elementId], 0, to.index - 1, element); + if (toElem.index > 0) { + return this.checkBit(this.buffer[toElem.elementId], 0, toElem.index - 1, element); } return false; @@ -196,16 +198,16 @@ export namespace collections { /** * Returns an iterator that iterates over bit vector. * - * @returns { IterableIterator } A new iterable iterator object. + * @returns { IterableIterator } A new iterable iterator object. */ - public override $_iterator(): IterableIterator { + public override $_iterator(): IterableIterator { return new BitVectorIterator(this); } /** * Returns an iterable of values in the bit vector * - * @returns { IterableIterator } A new iterable iterator object. + * @returns { IterableIterator } A new iterable iterator object. * @throws { BusinessError } 10200011 - The values method cannot be bound. * @throws { BusinessError } 10200201 - Concurrent modification error. * @syscap SystemCapability.Utils.Lang @@ -214,21 +216,20 @@ export namespace collections { * @since 20 * @arkts 1.2 */ - public values(): IterableIterator { + public values(): IterableIterator { return this.$_iterator(); } /** * Flips the bit value by index in a bit vector.(Flip 0 to 1, flip 1 to 0) * - * @param { number } index - The index in the bit vector. + * @param { int } index - The index in the bit vector. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified. * 2.Incorrect parameter types. * @throws { BusinessError } 10200001 - The value of index is out of range. */ - public flipBitByIndex(index: number): void { - this.integerCheck("index", index); + public flipBitByIndex(index: int): void { this.checkIndex(index); let bitIndex = this.computeElementIdAndBitId(index); @@ -238,58 +239,58 @@ export namespace collections { /** * Flips a range of bit values in a bit vector.(Flip 0 to 1, flip 1 to 0). * - * @param { number } fromIndex - The starting position of the index, containing the value at that index position. - * @param { number } toIndex - The end of the index, excluding the value at that index. + * @param { int } fromIndex - The starting position of the index, containing the value at that index position. + * @param { int } toIndex - The end of the index, excluding the value at that index. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified. * 2.Incorrect parameter types. * @throws { BusinessError } 10200001 - The value of fromIndex or toIndex is out of range. */ - public flipBitsByRange(fromIndex: number, toIndex: number): void { + public flipBitsByRange(fromIndex: int, toIndex: int): void { this.checkRange(fromIndex, toIndex); - let from = this.computeElementIdAndBitId(fromIndex); - let to = this.computeElementIdAndBitId(toIndex); + let fromElem = this.computeElementIdAndBitId(fromIndex); + let toElem = this.computeElementIdAndBitId(toIndex); - if (from.elementId === to.elementId) { - this.buffer[from.elementId] = this.flipBits(this.buffer[from.elementId], from.index, to.index - 1); + if (fromElem.elementId === toElem.elementId) { + this.buffer[fromElem.elementId] = this.flipBits(this.buffer[fromElem.elementId], fromElem.index, toElem.index - 1); return; } - this.buffer[from.elementId] = this.flipBits(this.buffer[from.elementId], from.index, BitVector.BIT_SET_LENGTH - 1); + this.buffer[fromElem.elementId] = this.flipBits(this.buffer[fromElem.elementId], fromElem.index, BitVector.BIT_SET_LENGTH - 1); - ++from.elementId; - while (from.elementId < to.elementId) { - this.buffer[from.elementId] = this.flipBits(this.buffer[from.elementId], 0, BitVector.BIT_SET_LENGTH - 1); - ++from.elementId; + ++fromElem.elementId; + while (fromElem.elementId < toElem.elementId) { + this.buffer[fromElem.elementId] = this.flipBits(this.buffer[fromElem.elementId], 0, BitVector.BIT_SET_LENGTH - 1); + ++fromElem.elementId; } - if (to.index > 0) { - this.buffer[to.elementId] = this.flipBits(this.buffer[to.elementId], 0, to.index - 1); + if (toElem.index > 0) { + this.buffer[toElem.elementId] = this.flipBits(this.buffer[toElem.elementId], 0, toElem.index - 1); } } /** * Returns the bit values in a range of indices in a bit vector. * - * @param { number } fromIndex - The starting position of the index, containing the value at that index position. - * @param { number } toIndex - The end of the index, excluding the value at that index. + * @param { int } fromIndex - The starting position of the index, containing the value at that index position. + * @param { int } toIndex - The end of the index, excluding the value at that index. * @returns { BitVector } The BitVector type, returns the bit values in a range of indices in a bit vector. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified. * 2.Incorrect parameter types. * @throws { BusinessError } 10200001 - The value of fromIndex or toIndex is out of range. */ - public getBitsByRange(fromIndex: number, toIndex: number): BitVector { + public getBitsByRange(fromIndex: int, toIndex: int): BitVector { this.checkRange(fromIndex, toIndex); - let from = this.computeElementIdAndBitId(fromIndex); - let to = this.computeElementIdAndBitId(toIndex); + let fromElem = this.computeElementIdAndBitId(fromIndex); + let toElem = this.computeElementIdAndBitId(toIndex); let newBitVector = new BitVector(toIndex - fromIndex); - if (from.elementId === to.elementId) { - newBitVector.buffer[0] = this.getBits(this.buffer[from.elementId], from.index, to.index - 1); + if (fromElem.elementId === toElem.elementId) { + newBitVector.buffer[0] = this.getBits(this.buffer[fromElem.elementId], fromElem.index, toElem.index - 1); return newBitVector; } @@ -303,44 +304,44 @@ export namespace collections { /** * Sets a range of bits in a bit vector to a particular element. * - * @param { number } element - Element to be set (0 means 0, else means 1). - * @param { number } fromIndex - The starting position of the index, containing the value at that index position. - * @param { number } toIndex - The end of the index, excluding the value at that index. + * @param { int } element - Element to be set (0 means 0, else means 1). + * @param { int } fromIndex - The starting position of the index, containing the value at that index position. + * @param { int } toIndex - The end of the index, excluding the value at that index. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified. * 2.Incorrect parameter types. * @throws { BusinessError } 10200001 - The value of fromIndex or toIndex is out of range. */ - public setBitsByRange(element: number, fromIndex: number, toIndex: number): void { + public setBitsByRange(element: int, fromIndex: int, toIndex: int): void { this.checkRange(fromIndex, toIndex); - let from = this.computeElementIdAndBitId(fromIndex); - let to = this.computeElementIdAndBitId(toIndex); + let fromElem = this.computeElementIdAndBitId(fromIndex); + let toElem = this.computeElementIdAndBitId(toIndex); - if (from.elementId === to.elementId) { - this.buffer[from.elementId] = this.setBits(this.buffer[from.elementId], from.index, to.index - 1, element); + if (fromElem.elementId === toElem.elementId) { + this.buffer[fromElem.elementId] = this.setBits(this.buffer[fromElem.elementId], fromElem.index, toElem.index - 1, element); return; } - this.buffer[from.elementId] = this.setBits(this.buffer[from.elementId], from.index, BitVector.BIT_SET_LENGTH - 1, element); + this.buffer[fromElem.elementId] = this.setBits(this.buffer[fromElem.elementId], fromElem.index, BitVector.BIT_SET_LENGTH - 1, element); - ++from.elementId; - while (from.elementId < to.elementId) { - this.buffer[from.elementId] = this.setBits(this.buffer[from.elementId], 0, BitVector.BIT_SET_LENGTH - 1, element); - ++from.elementId; + ++fromElem.elementId; + while (fromElem.elementId < toElem.elementId) { + this.buffer[fromElem.elementId] = this.setBits(this.buffer[fromElem.elementId], 0, BitVector.BIT_SET_LENGTH - 1, element); + ++fromElem.elementId; } - if (to.index != 0) { - this.buffer[from.elementId] = this.setBits(this.buffer[to.elementId], 0, to.index - 1, element); + if (toElem.index != 0) { + this.buffer[fromElem.elementId] = this.setBits(this.buffer[toElem.elementId], 0, toElem.index - 1, element); } } /** * Sets all of bits in a bit vector to a particular element. * - * @param { number } element - Element to be set (0 means 0, else means 1). + * @param { int } element - Element to be set (0 means 0, else means 1). */ - public setAllBits(element: number): void { + public setAllBits(element: int): void { if (element === 0) { for (let i = 0; i < this.getUsedCapacity(); i++) { this.buffer[i] = 0; @@ -355,21 +356,19 @@ export namespace collections { /** * Resize the bitVector's length. * - * @param { number } size - The new size for bitVector. If count is greater than the current size of bitVector, + * @param { int } size - The new size for bitVector. If count is greater than the current size of bitVector, * the additional bit elements are set to 0. * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified. * 2.Incorrect parameter types. * @throws { BusinessError } 10200001 - The value of size is out of range. */ - public resize(size: number): void { - this.integerCheck("size", size); - + public resize(size: int): void { 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}`)); + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of \"length\" is out of range. It must be >= 0. Received value is: ${size}`); } - let sizeInt: int = size as int; + let sizeInt: int = size; if (this._length >= sizeInt) { this._length = sizeInt; return; @@ -394,94 +393,183 @@ export namespace collections { /** * Counts the number of times a certain bit element occurs within a range of bits in a bit vector. * - * @param { number } element - Element to be counted (0 means 0, else means 1). - * @param { number } fromIndex - The starting position of the index, containing the value at that index position. - * @param { number } toIndex - The end of the index, excluding the value at that index. - * @returns { number } The number type, return the number of times a certain bit element + * @param { int } element - Element to be counted (0 means 0, else means 1). + * @param { int } fromIndex - The starting position of the index, containing the value at that index position. + * @param { int } toIndex - The end of the index, excluding the value at that index. + * @returns { int } The number type, return the number of times a certain bit element * @throws { BusinessError } 401 - Parameter error. Possible causes: * 1.Mandatory parameters are left unspecified. * 2.Incorrect parameter types. * @throws { BusinessError } 10200001 - The value of fromIndex or toIndex is out of range. */ - public getBitCountByRange(element: number, fromIndex: number, toIndex: number): number { + public getBitCountByRange(element: int, fromIndex: int, toIndex: int): int { this.checkRange(fromIndex, toIndex); - let from = this.computeElementIdAndBitId(fromIndex); - let to = this.computeElementIdAndBitId(toIndex); + let fromElem = this.computeElementIdAndBitId(fromIndex); + let toElem = this.computeElementIdAndBitId(toIndex); - if (from.elementId === to.elementId) { - let count = this.countBit1InRange(this.buffer[from.elementId], from.index, to.index - 1); + if (fromElem.elementId === toElem.elementId) { + let count = this.countBit1InRange(this.buffer[fromElem.elementId], fromElem.index, toElem.index - 1); return element === 0 ? (toIndex - fromIndex - count) : count; } - let count = this.countBit1InRange(this.buffer[from.elementId], from.index, BitVector.BIT_SET_LENGTH - 1); + let count = this.countBit1InRange(this.buffer[fromElem.elementId], fromElem.index, BitVector.BIT_SET_LENGTH - 1); - ++from.elementId; - while (from.elementId < to.elementId) { - count += this.countBit1InRange(this.buffer[from.elementId], 0, BitVector.BIT_SET_LENGTH - 1); - ++from.elementId; + ++fromElem.elementId; + while (fromElem.elementId < toElem.elementId) { + count += this.countBit1InRange(this.buffer[fromElem.elementId], 0, BitVector.BIT_SET_LENGTH - 1); + ++fromElem.elementId; } - if (to.index != 0) { - count += this.countBit1InRange(this.buffer[to.elementId], 0, to.index - 1); + if (toElem.index != 0) { + count += this.countBit1InRange(this.buffer[toElem.elementId], 0, toElem.index - 1); } return element === 0 ? (toIndex - fromIndex - count) : count; } - private checkAndIncrement(index: number): void { + /** + * Locates the first occurrence of a certain bit value within a range of bits in a bit vector. + * + * @param { int } element - Element to be Located (0 means 0, else means 1). + * @param { int } fromIndex - The starting position of the index, containing the value at that index position. + * @param { int } toIndex - The end of the index, excluding the value at that index. + * @returns { int } The number type, return the first index of specified bit within a range, + * or -1 if this range of the bitVector does not contain the element. + * @throws { BusinessError } 401 - Parameter error. Possible causes: + * 1.Mandatory parameters are left unspecified. + * 2.Incorrect parameter types. + * @throws { BusinessError } 10200001 - The value of fromIndex or toIndex is out of range. + */ + public getIndexOf(element: int, fromIndex: int, toIndex: int): int { + this.checkRange(fromIndex, toIndex); + + let fromElem = this.computeElementIdAndBitId(fromIndex); + let toElem = this.computeElementIdAndBitId(toIndex); + + if (fromElem.elementId === toElem.elementId) { + let bitIndex = this.findSignificantBit(this.buffer[fromElem.elementId], fromElem.index, toElem.index - 1, element); + return bitIndex != -1 ? this.computeIndex(fromElem.elementId, bitIndex) : -1; + } + + let bitIndex = this.findSignificantBit(this.buffer[fromElem.elementId], fromElem.index, BitVector.BIT_SET_LENGTH - 1, element); + if (bitIndex != -1) { + return this.computeIndex(fromElem.elementId, bitIndex) + } + + ++fromElem.elementId; + while (fromElem.elementId < toElem.elementId) { + bitIndex = this.findSignificantBit(this.buffer[fromElem.elementId], 0, BitVector.BIT_SET_LENGTH - 1, element); + if (bitIndex != -1) { + return this.computeIndex(fromElem.elementId, bitIndex); + } + + ++fromElem.elementId; + } + + if (toElem.index > 0) { + bitIndex = this.findSignificantBit(this.buffer[toElem.elementId], 0, toElem.index - 1, element); + if (bitIndex != -1) { + return this.computeIndex(toElem.elementId, bitIndex); + } + } + + return -1; + } + + /** + * Locates the last occurrence of a certain bit value within a range of bits in a bit vector. + * + * @param { int } element - Element to be Located (0 means 0, else means 1). + * @param { int } fromIndex - The starting position of the index, containing the value at that index position. + * @param { int } toIndex - The end of the index, excluding the value at that index. + * @returns { int } The number type, return the last index of specified bit within a range, + * or -1 if this range of the bitVector does not contain the element. + * @throws { BusinessError } 401 - Parameter error. Possible causes: + * 1.Mandatory parameters are left unspecified. + * 2.Incorrect parameter types. + * @throws { BusinessError } 10200001 - The value of fromIndex or toIndex is out of range. + */ + public getLastIndexOf(element: int, fromIndex: int, toIndex: int): int { + this.checkRange(fromIndex, toIndex); + + let fromElem = this.computeElementIdAndBitId(fromIndex); + let toElem = this.computeElementIdAndBitId(toIndex); + + if (fromElem.elementId === toElem.elementId) { + let bitIndex = this.findSignificantBit(this.buffer[fromElem.elementId], fromElem.index, toElem.index - 1, element, false); + return bitIndex != -1 ? this.computeIndex(fromElem.elementId, bitIndex) : -1; + } + + if (toElem.index > 0) { + let bitIndex = this.findSignificantBit(this.buffer[toElem.elementId], 0, toElem.index - 1, element, false); + if (bitIndex != -1) { + return this.computeIndex(toElem.elementId, bitIndex); + } + } + + --toElem.elementId; + while (fromElem.elementId < toElem.elementId) { + let bitIndex = this.findSignificantBit(this.buffer[toElem.elementId], 0, BitVector.BIT_SET_LENGTH - 1, element, false); + if (bitIndex != -1) { + return this.computeIndex(toElem.elementId, bitIndex); + } + + --toElem.elementId; + } + + let bitIndex = this.findSignificantBit(this.buffer[fromElem.elementId], fromElem.index, BitVector.BIT_SET_LENGTH - 1, element, false); + if (bitIndex != -1) { + return this.computeIndex(fromElem.elementId, bitIndex); + } + + return -1; + } + + private checkAndIncrement(index: int): void { if (this.buffer.length === index) { this.buffer.push(0); } } - private checkRange(fromIndex: number, toIndex: number): void { - this.integerCheck("fromIndex", fromIndex); - this.integerCheck("toIndex", toIndex); - + private checkRange(fromIndex: int, toIndex: int): void { 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}`)); + throw createBusinessError(OutOfBoundsErrorCodeId, `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}`)); + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of \"fromIndex\" is out of range. It must be >= 0 && <= ${toIndex - 1}. Received value is: ${fromIndex}`); } } - private checkIndex(index: number): void { + private checkIndex(index: int): 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}`)); + throw createBusinessError(OutOfBoundsErrorCodeId, `The value of \"index\" is out of range. It must be >= 0 && <= ${this._length - 1}. Received value is: ${index}`); } } - 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 as number / BitVector.BIT_SET_LENGTH); + private getUsedCapacity(): int { + return Math.ceil(this._length.toDouble() / BitVector.BIT_SET_LENGTH).toInt(); } - private computeElementIdAndBitId(index: number): BitIndex { - let elementId = Math.floor(index / BitVector.BIT_SET_LENGTH); + private computeElementIdAndBitId(index: int): BitIndex { + let elementId = Math.floor(index / BitVector.BIT_SET_LENGTH).toInt(); let bitId = index % BitVector.BIT_SET_LENGTH; return new BitIndex(elementId, bitId); } - private computeIndex(elementId: number, bitId: number): number { + private computeIndex(elementId: int, bitId: int): int { return elementId * BitVector.BIT_SET_LENGTH + bitId; } - private getBitUnsafe(index: number): number { + private getBitUnsafe(index: int): int { let bitIndex = this.computeElementIdAndBitId(index); let value = (this.buffer[bitIndex.elementId] >> bitIndex.index) & 1; return value; } - private setBitUnsafe(index: number, value: number): void { + private setBitUnsafe(index: int, value: int): void { let bitIndex = this.computeElementIdAndBitId(index); if (value === 0) { let mask = ~(1 << bitIndex.index); @@ -491,7 +579,7 @@ export namespace collections { } } - private checkBit(element: int, fromIndex: number, toIndex: number, target_bit: number): boolean { + private checkBit(element: int, fromIndex: int, toIndex: int, target_bit: int): boolean { let length = toIndex - fromIndex + 1; let mask: int = this.getRangeMask(fromIndex, length); @@ -502,14 +590,14 @@ export namespace collections { } } - private flipBits(num: int, fromIndex: number, toIndex: number): int { + private flipBits(num: int, fromIndex: int, toIndex: int): int { let length = toIndex - fromIndex + 1; let mask = this.getRangeMask(fromIndex, length); return num ^ mask; } - private getRangeMask(fromIndex: number, length: number): int { + private getRangeMask(fromIndex: int, length: int): int { let mask: int = 0x0; if (length == BitVector.BIT_SET_LENGTH) { @@ -521,13 +609,13 @@ export namespace collections { return mask; } - private getBits(num: int, fromIndex: number, toIndex: number): int { + private getBits(num: int, fromIndex: int, toIndex: int): 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 { + private setBits(num: int, fromIndex: int, toIndex: int, element: int): int { let length = toIndex - fromIndex + 1; let mask: int = this.getRangeMask(fromIndex, length); if (element != 0) { @@ -537,7 +625,7 @@ export namespace collections { } } - private countBit1InRange(num: int, fromIndex: number, toIndex: number): number { + private countBit1InRange(num: int, fromIndex: int, toIndex: int): int { let length = toIndex - fromIndex + 1; let mask: int = this.getRangeMask(fromIndex, length); @@ -551,5 +639,62 @@ export namespace collections { return count; } + + private findLSB(num: int): int { + if (num === 0) { + return -1; + } + + // Get the lowest 1 bit + let isolatedBit: int = num & -num; + + let pos = 0; + // Check upper 16 bits; if set, add 16 and shift right + if (isolatedBit & 0xFFFF0000) { pos += 16; isolatedBit >>= 16; } + // Check next 8 bits; if set, add 8 and shift right + if (isolatedBit & 0x0000FF00) { pos += 8; isolatedBit >>= 8; } + // Check next 4 bits; if set, add 4 and shift right + if (isolatedBit & 0x000000F0) { pos += 4; isolatedBit >>= 4; } + // Check next 2 bits; if set, add 2 and shift right + if (isolatedBit & 0x0000000C) { pos += 2; isolatedBit >>= 2; } + // Check bit 1; if set, add 1 + if (isolatedBit & 0x00000002) { pos += 1; } + + return pos; + } + + private findMSB(num: int): int { + if (num === 0) { + return -1; + } + + let pos = 0; + // Check upper 16 bits; if set, add 16 and shift right + if (num & 0xFFFF0000) { pos += 16; num >>= 16; } + // Check next 8 bits; if set, add 8 and shift right + if (num & 0xFF00) { pos += 8; num >>= 8; } + // Check next 4 bits; if set, add 4 and shift right + if (num & 0xF0) { pos += 4; num >>= 4; } + // Check next 2 bits; if set, add 2 and shift right + if (num & 0xC) { pos += 2; num >>= 2; } + // Check bit 1; if set, add 1 + if (num & 0x2) { pos += 1; } + + return pos; + } + + private findSignificantBit(element: int, fromIndex: int, toIndex: int, target: int, isLSB: boolean = true): int { + let length = toIndex - fromIndex + 1; + let mask: int = this.getRangeMask(fromIndex, length); + + let masked_value: int = 0; + if (target != 0) { + masked_value = element & mask; + } else { + masked_value = (~element) & mask; + } + + return isLSB ? this.findLSB(masked_value) : fromIndex + this.findMSB(masked_value >> fromIndex); + } } } diff --git a/static_core/plugins/ets/sdk/arkts/@arkts.math.Decimal.ets b/static_core/plugins/ets/sdk/arkts/@arkts.math.Decimal.ets index ac9d9cfc638ccca4418fddcdad8942ed2fe8e8e6..ea5507b0b7fd524ecb431d63692147424879dd76 100644 --- a/static_core/plugins/ets/sdk/arkts/@arkts.math.Decimal.ets +++ b/static_core/plugins/ets/sdk/arkts/@arkts.math.Decimal.ets @@ -15,17 +15,17 @@ import { BusinessError } from "@ohos.base"; -export type Rounding = number -export type Modulo = number -export type DecimalValue = Decimal | number | string; +export type Rounding = int +export type Modulo = int +export type Value = Decimal | double | string; -const RANGE_ERROR_CODE = 10200001; -const TYPE_ERROR_CODE = 401; -const PRECISION_LIMIT_EXCEEDED_ERROR_CODE = 10200060; -const CRYPTO_UNAVAILABLE_ERROR_CODE = 10200061; +const RANGE_ERROR_CODE: int = 10200001; +const TYPE_ERROR_CODE: int = 401; +const PRECISION_LIMIT_EXCEEDED_ERROR_CODE: int = 10200060; +const CRYPTO_UNAVAILABLE_ERROR_CODE: int = 10200061; -const EXP_LIMIT: number = 9e15; -const MAX_DIGITS: number = 1e9; +const EXP_LIMIT: double = 9e15; +const MAX_DIGITS: double = 1e9; const NUMERALS: string = '0123456789abcdef'; const LN10 = ('2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341' @@ -39,6 +39,17 @@ const LN10 = ('2.302585092994045684017991454684364207601101488628772976033327900 + '8243182375253577097505395651876975103749708886921802051893395072385392051446341972652872869651108625714921988499' + '78748873771345686209167058'); +const PI = ('3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214' + + '8086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933' + + '4461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209' + + '2096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179' + + '3105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860' + + '9437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249' + + '5343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804' + + '9951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617' + + '1776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989' + + '380952572010654858632789'); + const DEFAULTS_PRECISION = 20; const DEFAULTS_ROUNDING = 4; const DEFAULTS_MODULO = 1; @@ -54,73 +65,78 @@ const isHex: RegExp = new RegExp("^0x([0-9a-f]+(\\.[0-9a-f]*)?|\\.[0-9a-f]+)(p[+ const isOctal: RegExp = new RegExp("^0o([0-7]+(\\.[0-7]*)?|\\.[0-7]+)(p[+-]?\\d+)?$", "i"); const isDecimal: RegExp = new RegExp("^(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?$", "i"); -const BASE: number = 1e7; -const LOG_BASE: number = 7; -const MAX_SAFE_INTEGER: number = 9007199254740991; +const BASE: double = 1e7; +const LOG_BASE: int = 7; +const MAX_SAFE_INTEGER: double = 9007199254740991; const LN10_PRECISION = LN10.length - 1; +const PI_PRECISION = PI.length - 1; export interface DecimalConfig { - precision?: number; - rounding?: number; - toExpNeg?: number; - toExpPos?: number; - minE?: number; - maxE?: number; + precision?: double; + rounding?: Rounding; + toExpNeg?: double; + toExpPos?: double; + minE?: double; + maxE?: double; crypto?: boolean; - modulo?: number; + modulo?: Modulo; defaults?: boolean; } export class Decimal { - internal digits: Array | null = new Array(); - internal exponent: number; - internal sign: number; + //NOTE(ekaterinazaytseva): unable to replace internal - used in friend class Utils + internal digits: Array | null = new Array(); + //NOTE(ekaterinazaytseva): unable to replace internal - used in friend class Utils + internal exponent: double; + private sign: double; + //NOTE(ekaterinazaytseva): unable to replace internal - used in friend class Utils internal toStringTag: string = '[object Decimal]'; - - public static maxE: number = EXP_LIMIT; - public static minE: number = -EXP_LIMIT; - public static precision: number = DEFAULTS_PRECISION; - public static rounding: number = DEFAULTS_ROUNDING; - public static toExpNeg: number = DEFAULTS_TOEXPNEG; - public static toExpPos: number = DEFAULTS_TOEXPPOS; - public static modulo: number = DEFAULTS_MODULO; + private static quadrant: int = 0; + + public static maxE: double = EXP_LIMIT; + public static minE: double = -EXP_LIMIT; + public static precision: double = DEFAULTS_PRECISION; + public static rounding: Rounding = DEFAULTS_ROUNDING; + public static toExpNeg: double = DEFAULTS_TOEXPNEG; + public static toExpPos: double = DEFAULTS_TOEXPPOS; + public static modulo: Modulo = DEFAULTS_MODULO; public static crypto: boolean = false; // defined in d.ets, See "@arkts.math.Decimal.d.ets" for details. - public static readonly ROUND_UP: number = 0; - public static readonly ROUND_DOWN: number = 1; - public static readonly ROUND_CEILING: number = 2; - public static readonly ROUND_FLOOR: number = 3; - public static readonly ROUND_HALF_UP: number = 4; - public static readonly ROUND_HALF_DOWN: number = 5; - public static readonly ROUND_HALF_EVEN: number = 6; - public static readonly ROUND_HALF_CEILING: number = 7; - public static readonly ROUND_HALF_FLOOR: number = 8; - public static readonly EUCLIDEAN: number = 9; - - get d(): Array | null { + public static readonly ROUND_UP: int = 0; + public static readonly ROUND_DOWN: int = 1; + public static readonly ROUND_CEILING: int = 2; + public static readonly ROUND_FLOOR: int = 3; + public static readonly ROUND_HALF_UP: int = 4; + public static readonly ROUND_HALF_DOWN: int = 5; + public static readonly ROUND_HALF_EVEN: int = 6; + public static readonly ROUND_HALF_CEILING: int = 7; + public static readonly ROUND_HALF_FLOOR: int = 8; + public static readonly EUCLIDEAN: int = 9; + + get d(): Array | null { return this.digits; } - get e(): number { + get e(): double { return this.exponent; } - get s(): number { + get s(): double { return this.sign; } /** * Return a new Decimal whose value is the absolute value of this Decimal. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} */ - constructor(n: DecimalValue) { + constructor(n: Value) { if (Utils.isDecimalInstance(n)) { this.initializeByDecimal(n as Decimal); } else if (typeof n === 'number') { - this.initializeByNumber(n as number); + this.initializeByNumber(n as double); } else if (typeof n === 'string') { this.initializeByString(n as string); } else { @@ -175,12 +191,12 @@ export class Decimal { * Return a new Decimal whose value is the value of this Decimal clamped to the range * delineated by `min` and `max`. * - * @param { DecimalValue } min {number | string | Decimal} - * @param { DecimalValue } max {number | string | Decimal} + * @param { Value } min {double | string | Decimal} + * @param { Value } max {double | string | Decimal} * @returns { Decimal } the Decimal type * @throws { BusinessError } 10200001 - The value of `min` is out of range. */ - public clamp(min: DecimalValue, max: DecimalValue): Decimal { + public clamp(min: Value, max: Value): Decimal { let minDecimal = new Decimal(min); let maxDecimal = new Decimal(max); if (!minDecimal.s || !maxDecimal.s) { @@ -244,8 +260,19 @@ export class Decimal { * * @returns { boolean } the boolean type */ - public isZero (): boolean { - return !!this.digits && this.digits![0] === 0; + public isZero(): boolean { + return !!this.digits && this.digits!.at(0) === 0; + } + + /** + * Return a new Decimal whose value is the integer part of dividing the value of this Decimal + * by the value of `n`, rounded to `precision` significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + */ + public dividedToIntegerBy(n: Value): Decimal { + return Utils.finalise(Utils.divide(this, new Decimal(n), 0, 1, true), Decimal.precision, Decimal.rounding); } /** @@ -272,11 +299,11 @@ export class Decimal { * Return a string representing the value of this Decimal in base 2, round to `significantDigits` * significant digits. * - * @param { number } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `significantDigits` is out of range. */ - public toBinary(significantDigits: number): string { + public toBinary(significantDigits: double): string { return this.toStringBinary(2, significantDigits); } @@ -284,12 +311,12 @@ export class Decimal { * Return a string representing the value of this Decimal in base 2, round to `significantDigits` * significant digits using rounding mode `rounding`. * - * @param { number } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `significantDigits | rounding` is out of range. */ - public toBinary(significantDigits: number, rounding: Rounding): string { + public toBinary(significantDigits: double, rounding: Rounding): string { return this.toStringBinary(2, significantDigits, rounding); } @@ -305,11 +332,11 @@ export class Decimal { /** * Return a string representing the value of this Decimal in base 8, round to `significantDigits` significant. * - * @param { number } significantDigits {number | string | Decimal} + * @param { double } significantDigits {double | string | Decimal} * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `significantDigits` is out of range. */ - public toOctal(significantDigits: number): string { + public toOctal(significantDigits: double): string { return this.toStringBinary(8, significantDigits); } @@ -317,12 +344,12 @@ export class Decimal { * Return a string representing the value of this Decimal in base 8, round to `significantDigits` significant * digits using rounding mode `rounding`. * - * @param { number } significantDigits {number | string | Decimal} + * @param { double } significantDigits {double | string | Decimal} * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `significantDigits | rounding` is out of range. */ - public toOctal(significantDigits: number, rounding: Rounding): string { + public toOctal(significantDigits: double, rounding: Rounding): string { return this.toStringBinary(8, significantDigits, rounding); } @@ -338,11 +365,11 @@ export class Decimal { /** * Return a string representing the value of this Decimal in base 16, round to `significantDigits` significant. * - * @param { number } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `significantDigits` is out of range. */ - public toHexadecimal(significantDigits: number): string { + public toHexadecimal(significantDigits: double): string { return this.toStringBinary(16, significantDigits); } @@ -350,15 +377,37 @@ export class Decimal { * Return a string representing the value of this Decimal in base 16, round to `significantDigits` significant * digits using rounding mode `rounding`. * - * @param { number } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `significantDigits | rounding` is out of range. */ - public toHexadecimal(significantDigits: number, rounding: Rounding): string { + public toHexadecimal(significantDigits: double, rounding: Rounding): string { return this.toStringBinary(16, significantDigits, rounding); } + /** + * Return an array representing the value of this Decimal as a simple fraction with an integer + * numerator and an integer denominator. + * + * @returns { Decimal[] } the Decimal[] type + */ + public toFraction(): Decimal[] { + return this.fraction(); + } + + /** + * Return an array representing the value of this Decimal as a simple fraction with an integer + * numerator and an integer denominator. The denominator will be a positive non-zero value + * less than or equal to `max_denominator`. + * + * @param { Value } maxDenominator {double | string | Decimal} + * @returns { Decimal[] } the Decimal[] type + */ + public toFraction(maxDenominator: Value): Decimal[] { + return this.fraction(maxDenominator); + } + /** * Return a new Decimal whose value is the value of this Decimal. * @@ -372,10 +421,10 @@ export class Decimal { * Return a new Decimal whose value is the value of this Decimal rounded to a maximum of `decimalPlaces` * decimal places. * - * @param { number } decimalPlaces Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } decimalPlaces Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @returns { Decimal } the Decimal type */ - public toDecimalPlaces(decimalPlaces: number): Decimal { + public toDecimalPlaces(decimalPlaces: double): Decimal { return this.toDecimalPlaces(decimalPlaces, Decimal.rounding); } @@ -383,11 +432,11 @@ export class Decimal { * Return a new Decimal whose value is the value of this Decimal rounded to a maximum of `decimalPlaces` * decimal places using rounding mode `rounding`. * - * @param { number } decimalPlaces Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } decimalPlaces Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { Decimal } the Decimal type */ - public toDecimalPlaces(decimalPlaces: number, rounding: Rounding): Decimal { + public toDecimalPlaces(decimalPlaces: double, rounding: Rounding): Decimal { let decimal = this.toDecimalPlaces(); Utils.checkInt32(decimalPlaces, 0, MAX_DIGITS); Utils.checkInt32(rounding, 0, 8); @@ -408,11 +457,11 @@ export class Decimal { * Return a string representing the value of this Decimal in exponential notation rounded to * `decimalPlaces` fixed decimal places. * - * @param { number } decimalPlaces Decimal places. Integer, 0 to MAX_DIGITS inclusive. + * @param { double } decimalPlaces Decimal places. Integer, 0 to MAX_DIGITS inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `decimalPlaces` is out of range. */ - public toExponential(decimalPlaces: number): string { + public toExponential(decimalPlaces: double): string { return this.toExponential(decimalPlaces, Decimal.rounding); } @@ -420,12 +469,12 @@ export class Decimal { * Return a string representing the value of this Decimal in exponential notation rounded to * `decimalPlaces` fixed decimal places using rounding mode `rounding`. * - * @param { number } decimalPlaces Decimal places. Integer, 0 to MAX_DIGITS inclusive. + * @param { double } decimalPlaces Decimal places. Integer, 0 to MAX_DIGITS inclusive. * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `decimalPlaces | rounding` is out of range. */ - public toExponential(decimalPlaces: number, rounding: Rounding): string { + public toExponential(decimalPlaces: double, rounding: Rounding): string { Utils.checkInt32(decimalPlaces, 0, MAX_DIGITS); Utils.checkInt32(rounding, 0, 8); let x = Utils.finalise(new Decimal(this), decimalPlaces + 1, rounding); @@ -447,11 +496,11 @@ export class Decimal { * Return a string representing the value of this Decimal in normal (fixed-point) notation to * `decimalPlaces` fixed decimal places. * - * @param { number } decimalPlaces Decimal places. Integer, 0 to MAX_DIGITS inclusive. + * @param { double } decimalPlaces Decimal places. Integer, 0 to MAX_DIGITS inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `decimalPlaces` is out of range. */ - public toFixed(decimalPlaces: number): string { + public toFixed(decimalPlaces: double): string { return this.toFixed(decimalPlaces, Decimal.rounding); } @@ -459,12 +508,12 @@ export class Decimal { * Return a string representing the value of this Decimal in normal (fixed-point) notation to * `decimalPlaces` fixed decimal places and rounded using rounding mode `rounding`. * - * @param { number } decimalPlaces Decimal places. Integer, 0 to MAX_DIGITS inclusive. + * @param { double } decimalPlaces Decimal places. Integer, 0 to MAX_DIGITS inclusive. * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `decimalPlaces | rounding` is out of range. */ - public toFixed(decimalPlaces: number, rounding: Rounding): string { + public toFixed(decimalPlaces: double, rounding: Rounding): string { Utils.checkInt32(decimalPlaces, 0, MAX_DIGITS); Utils.checkInt32(rounding, 0, 8); let y = Utils.finalise(new Decimal(this), decimalPlaces + this.e + 1, rounding); @@ -475,10 +524,10 @@ export class Decimal { /** * Returns a new Decimal whose value is the nearest multiple of `n`. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - public toNearest(n: DecimalValue): Decimal { + public toNearest(n: Value): Decimal { return this.toNearest(n, Decimal.rounding); } @@ -486,12 +535,12 @@ export class Decimal { * Returns a new Decimal whose value is the nearest multiple of `n` in the direction of rounding * mode `rounding`, to the value of this Decimal. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { Decimal } the Decimal type * @throws { BusinessError } 10200001 - The value of `rounding` is out of range. */ - public toNearest(n: DecimalValue, rounding: Rounding): Decimal { + public toNearest(n: Value, rounding: Rounding): Decimal { let x = new Decimal(this); let y = new Decimal(n); Utils.checkInt32(rounding, 0, 8); @@ -537,11 +586,11 @@ export class Decimal { /** * Return a string representing the value of this Decimal rounded to `significantDigits` significant digits. * - * @param { number } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `significantDigits` is out of range. */ - public toPrecision(significantDigits: number): string { + public toPrecision(significantDigits: double): string { return this.toPrecision(significantDigits, Decimal.rounding); } @@ -549,12 +598,12 @@ export class Decimal { * Return a string representing the value of this Decimal rounded to `significantDigits` significant digits * using rounding mode `rounding`. * - * @param { number } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { string } the string type * @throws { BusinessError } 10200001 - The value of `significantDigits | rounding` is out of range. */ - public toPrecision(significantDigits: number, rounding: Rounding): string { + public toPrecision(significantDigits: double, rounding: Rounding): string { Utils.checkInt32(significantDigits, 1, MAX_DIGITS); Utils.checkInt32(rounding, 0, 8); let x = Utils.finalise(new Decimal(this), significantDigits, rounding); @@ -575,11 +624,11 @@ export class Decimal { * Return a new Decimal whose value is the value of this Decimal rounded to a maximum of `significantDigits` * significant digits. * - * @param { number } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @returns { Decimal } the Decimal type * @throws { BusinessError } 10200001 - The value of `significantDigits` is out of range. */ - public toSignificantDigits(significantDigits: number): Decimal { + public toSignificantDigits(significantDigits: double): Decimal { return this.toSignificantDigits(significantDigits, Decimal.rounding); } @@ -587,12 +636,12 @@ export class Decimal { * Return a new Decimal whose value is the value of this Decimal rounded to a maximum of `significantDigits` * significant digits using rounding mode `rounding`. * - * @param { number } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 1 to MAX_DIGITS inclusive. * @param { Rounding } rounding Rounding mode. Integer, 0 to 8 inclusive. * @returns { Decimal } the Decimal type * @throws { BusinessError } 10200001 - The value of `significantDigits | rounding` is out of range. */ - public toSignificantDigits(significantDigits: number, rounding: Rounding): Decimal { + public toSignificantDigits(significantDigits: double, rounding: Rounding): Decimal { Utils.checkInt32(significantDigits, 1, MAX_DIGITS); Utils.checkInt32(rounding, 0, 8); return Utils.finalise(new Decimal(this), significantDigits, rounding); @@ -601,9 +650,9 @@ export class Decimal { /** * Return the value of this Decimal converted to a number primitive. Zero keeps its sign. * - * @returns { number } the number type + * @returns { double } the number type */ - public toNumber(): number { + public toNumber(): double { return Utils.toNumber(this.valueOf()); } @@ -622,17 +671,18 @@ export class Decimal { /** * Return the number of decimal places of the value of this Decimal. * - * @returns { number } the number type + * @returns { double } the number type */ - public decimalPlaces(): number { + public decimalPlaces(): double { let digits = this.digits; - let result: number = NaN; + let result: double = NaN; if (digits) { let len = digits!.length - 1; result = (len - Math.floor(this.exponent / LOG_BASE)) * LOG_BASE; - let value = digits[len]; + let value = digits!.at(len); if (value) { - for (; value % 10 == 0; value /= 10) { + let v: double = value! + for (; v % 10 == 0; v /= 10) { result--; } } @@ -654,55 +704,97 @@ export class Decimal { return this.isNegative() ? '-' + str : str; } + /** + * Return the number of significant digits of the value of this Decimal. + * + * @returns { double } the number type + */ + public precision(): double { + let result: double; + if (this.digits) { + result = Utils.getPrecision(this.digits!); + } else { + result = NaN; + } + return result; + } + + /** + * Return the number of significant digits of the value of this Decimal, whether to count + * integer-part trailing zeros. + * + * @param { boolean | int } includeZeros Whether to count integer-part trailing zeros: true, false, + * 1 or 0. + * @returns { double } the number type + * @throws { BusinessError } 10200001 - The value of `includeZeros` is out of range. + */ + public precision(includeZeros: boolean | int): double { + let result: double; + if (includeZeros != undefined && includeZeros !== !!includeZeros && includeZeros !== 1 && includeZeros !== 0) { + throw Utils.createBusinessError(RANGE_ERROR_CODE, + `The value of includeZeros is out of range. It must be 0 or 1. Received value is: ${includeZeros}`); + } + + if (this.digits) { + result = Utils.getPrecision(this.digits!); + if (includeZeros && this.exponent + 1 > result) { + result = this.exponent + 1; + } + } else { + result = NaN; + } + return result; + } + /** * Return a new Decimal whose value is the absolute value of `n`. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static abs(n: DecimalValue): Decimal { + static abs(n: Value): Decimal { return new Decimal(n).abs(); } /** * Return a new Decimal whose value is `n` round to an integer using `ROUND_FLOOR`. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static floor(n: DecimalValue): Decimal { + static floor(n: Value): Decimal { return new Decimal(n).floor(); } /** * Return a new Decimal whose value is `n` rounded to an integer using `ROUND_CEIL`. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static ceil(n: DecimalValue): Decimal { + static ceil(n: Value): Decimal { return new Decimal(n).ceil(); } /** * Return a new Decimal whose value is `n` truncated to an integer. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static trunc(n: DecimalValue): Decimal { + static trunc(n: Value): Decimal { return new Decimal(n).trunc(); } /** * Return a new Decimal whose value is `n` clamped to the range delineated by `min` and `max`. * - * @param { DecimalValue } n {number | string | Decimal} - * @param { DecimalValue } min {number | string | Decimal} - * @param { DecimalValue } max {number | string | Decimal} + * @param { Value } n {double | string | Decimal} + * @param { Value } min {double | string | Decimal} + * @param { Value } max {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static clamp(n: DecimalValue, min: DecimalValue, max: DecimalValue): Decimal { + static clamp(n: Value, min: Value, max: Value): Decimal { return new Decimal(n).clamp(min, max); } @@ -710,11 +802,11 @@ export class Decimal { * Return a new Decimal whose value is the sum of `x` and `y`, rounded to `precision` significant * digits using rounding mode `rounding`. * - * @param { DecimalValue } x {number | string | Decimal} - * @param { DecimalValue } y {number | string | Decimal} + * @param { Value } x {double | string | Decimal} + * @param { Value } y {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static add(x: DecimalValue, y: DecimalValue): Decimal { + static add(x: Value, y: Value): Decimal { return new Decimal(x).add(y); } @@ -724,10 +816,10 @@ export class Decimal { * * Only the result is rounded, not the intermediate calculations. * - * @param { DecimalValue[] } n {number | string | Decimal} + * @param { Value[] } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static sum(...values: DecimalValue[]): Decimal { + static sum(...values: Value[]): Decimal { let result = new Decimal(values[0]); external = false; for (let i = 1; i < values.length; i++) { @@ -744,11 +836,11 @@ export class Decimal { * Return a new Decimal whose value is `x` minus `y`, rounded to `precision` significant digits * using rounding mode `rounding`. * - * @param { DecimalValue } x {number | string | Decimal} - * @param { DecimalValue } y {number | string | Decimal} + * @param { Value } x {double | string | Decimal} + * @param { Value } y {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static sub(x: DecimalValue, y: DecimalValue): Decimal { + static sub(x: Value, y: Value): Decimal { return new Decimal(x).sub(y); } @@ -756,22 +848,323 @@ export class Decimal { * Return a new Decimal whose value is `x` multiplied by `y`, rounded to `precision` significant * digits using rounding mode `rounding`. * - * @param { DecimalValue } x {number | string | Decimal} - * @param { DecimalValue } y {number | string | Decimal} + * @param { Value } x {double | string | Decimal} + * @param { Value } y {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static mul(x: DecimalValue, y: DecimalValue): Decimal { + static mul(x: Value, y: Value): Decimal { return new Decimal(x).mul(y); } + /** + * Return a new Decimal whose value is `x` divided by `y`, rounded to `precision` significant + * digits using rounding mode `rounding`. + * + * @param { Value } x {double | string | Decimal} + * @param { Value } y {double | string | Decimal} + * @returns { Decimal } the Decimal type + */ + static div(x: Value, y: Value): Decimal { + return new Decimal(x).div(y); + } + + /** + * Return a new Decimal whose value is `x` modulo `y`, rounded to `precision` significant digits + * using rounding mode `rounding`. + * + * @param { Value } x {double | string | Decimal} + * @param { Value } y {double | string | Decimal} + * @returns { Decimal } the Decimal type + */ + static mod(x: Value, y: Value): Decimal { + return new Decimal(x).mod(y); + } + + /** + * Return a new Decimal whose value is the square root of `n`, rounded to `precision` significant + * digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + */ + static sqrt(n: Value): Decimal { + return new Decimal(n).sqrt(); + } + + /** + * Return a new Decimal whose value is the cube root of `n`, rounded to `precision` significant + * digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + */ + static cbrt(n: Value): Decimal { + return new Decimal(n).cbrt(); + } + + /** + * Return a new Decimal whose value is `base` raised to the power `exponent`, rounded to precision + * significant digits using rounding mode `rounding`. + * + * @param { Value } base {double | string | Decimal} The base. + * @param { Value } exponent {double | string | Decimal} The exponent. + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static pow(base: Value, exponent: Value): Decimal { + return new Decimal(base).pow(exponent); + } + + /** + * Return a new Decimal whose value is the natural exponential of `n`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static exp(n: Value): Decimal { + return new Decimal(n).exp(); + } + + /** + * Return a new Decimal whose value is the log of `n` to the base `base`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @param { Value } base {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static log(n: Value, base: Value): Decimal { + return new Decimal(n).log(base); + } + + /** + * Return a new Decimal whose value is the natural logarithm of `n`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static ln(n: Value): Decimal { + return new Decimal(n).ln(); + } + + /** + * Return a new Decimal whose value is the base 2 logarithm of `n`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static log2(n: Value): Decimal { + return new Decimal(n).log(2); + } + + /** + * Return a new Decimal whose value is the base 10 logarithm of `n`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static log10(n: Value): Decimal { + return new Decimal(n).log(10); + } + + /** + * Return a new Decimal whose value is the cosine of `n`, rounded to `precision` significant + * digits using rounding mode `rounding` + * + * @param { Value } n {double | string | Decimal} A value in radians. + * @returns { Decimal } the Decimal type + */ + static cos(n: Value): Decimal { + return new Decimal(n).cos(); + } + + /** + * Return a new Decimal whose value is the sine of `n`, rounded to `precision` significant digits + * using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} A value in radians. + * @returns { Decimal } the Decimal type + */ + static sin(n: Value): Decimal { + return new Decimal(n).sin(); + } + + /** + * Return a new Decimal whose value is the tangent of `n`, rounded to `precision` significant + * digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} A value in radians. + * @returns { Decimal } the Decimal type + */ + static tan(n: Value): Decimal { + return new Decimal(n).tan(); + } + + /** + * Return a new Decimal whose value is the hyperbolic cosine of `n`, rounded to precision + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} A value in radians. + * @returns { Decimal } the Decimal type + */ + static cosh(n: Value): Decimal { + return new Decimal(n).cosh(); + } + + /** + * Return a new Decimal whose value is the hyperbolic sine of `n`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + */ + static sinh(n: Value): Decimal { + return new Decimal(n).sinh(); + } + + /** + * Return a new Decimal whose value is the hyperbolic tangent of `n`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} A value in radians. + * @returns { Decimal } the Decimal type + */ + static tanh(n: Value): Decimal { + return new Decimal(n).tanh(); + } + + /** + * Return a new Decimal whose value is the arccosine in radians of `n`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static acos(n: Value): Decimal { + return new Decimal(n).acos(); + } + + /** + * Return a new Decimal whose value is the arcsine in radians of `n`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static asin(n: Value): Decimal { + return new Decimal(n).asin(); + } + + /** + * Return a new Decimal whose value is the arctangent in radians of `n`, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static atan(n: Value): Decimal { + return new Decimal(n).atan(); + } + + /** + * Return a new Decimal whose value is the inverse of the hyperbolic cosine of `n`, rounded to + * `precision` significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static acosh(n: Value): Decimal { + return new Decimal(n).acosh(); + } + + /** + * Return a new Decimal whose value is the inverse of the hyperbolic sine of `n`, rounded to + * `precision` significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} A value in radians. + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + + */ + static asinh(n: Value): Decimal { + return new Decimal(n).asinh(); + } + + /** + * Return a new Decimal whose value is the inverse of the hyperbolic tangent of `n`, rounded to + * `precision` significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} A value in radians. + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static atanh(n: Value): Decimal { + return new Decimal(n).atanh(); + } + + /** + * Return a new Decimal whose value is the arctangent in radians of `y/x` in the range -pi to pi + * (inclusive), rounded to `precision` significant digits using rounding mode `rounding`. + * + * @param { Value } y {double | string | Decimal} The y-coordinate. + * @param { Value } x {double | string | Decimal} The x-coordinate. + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + static atan2(y: Value, x: Value): Decimal { + y = new Decimal(y); + x = new Decimal(x); + let pr = Decimal.precision; + let rm = Decimal.rounding; + let wpr = pr + 4; + let result: Decimal; + // Either NaN + if (!y.s || !x.s) { + result = new Decimal(NaN); + } else if (!y.d && !x.d) { // Both ±Infinity + result = Utils.getPi(wpr, 1).mul(x.s > 0 ? 0.25 : 0.75); + result.sign = y.s; + } else if (!x.d || y.isZero()) { // x is ±Infinity or y is ±0 + result = x.s < 0 ? Utils.getPi(pr, rm) : new Decimal(0); + result.sign = y.s; + } else if (!y.d || x.isZero()) { // y is ±Infinity or x is ±0 + result = Utils.getPi(wpr, 1).mul(0.5); + result.sign = y.s; + } else if (x.s < 0) { // Both non-zero and finite + Decimal.precision = wpr; + Decimal.rounding = 1; + result = Decimal.atan(Utils.divide(y, x, wpr, 1)); + x = Utils.getPi(wpr, 1); + Decimal.precision = pr; + Decimal.rounding = rm; + result = y.s < 0 ? result.sub(x) : result.add(x); + } else { + result = Decimal.atan(Utils.divide(y, x, wpr, 1)); + } + + return result; + } + /** * Return a new Decimal whose value is the square root of the sum of the squares of the arguments, * rounded to `precision` significant digits using rounding mode `rounding`. * - * @param { DecimalValue[] } n {number | string | Decimal} Decimal + * @param { Value[] } n {double | string | Decimal} Decimal * @returns { Decimal } the Decimal type */ - static hypot(...n: DecimalValue[]): Decimal { + static hypot(...n: Value[]): Decimal { let t = new Decimal(0); external = false; for (let i = 0; i < n.length;) { @@ -793,10 +1186,10 @@ export class Decimal { /** * Return a new Decimal whose value is the maximum of the arguments. * - * @param { DecimalValue[] } n {number | string | Decimal} + * @param { Value[] } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static max(...values: DecimalValue[]): Decimal { + static max(...values: Value[]): Decimal { let result = new Decimal(values[0]); for (let i = 1; i < values.length; i++) { let other = new Decimal(values[i]); @@ -813,10 +1206,10 @@ export class Decimal { /** * Return a new Decimal whose value is the minimum of the arguments. * - * @param { DecimalValue[] } n {number | string | Decimal} + * @param { Value[] } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static min(...values: DecimalValue[]): Decimal { + static min(...values: Value[]): Decimal { let result = new Decimal(values[0]); for (let i = 1; i < values.length; i++) { let other = new Decimal(values[i]); @@ -844,11 +1237,11 @@ export class Decimal { * Returns a new Decimal with a random value equal to or greater than 0 and less than 1, and with * `significantDigits` significant digits (or less if trailing zeros are produced). * - * @param { number } significantDigits Significant digits. Integer, 0 to MAX_DIGITS inclusive. + * @param { double } significantDigits Significant digits. Integer, 0 to MAX_DIGITS inclusive. * @returns { Decimal } the Decimal type * @throws { BusinessError } 10200061 - Crypto unavailable */ - static random(significantDigits: number): Decimal { + static random(significantDigits: double): Decimal { return Utils.random(significantDigits); } @@ -860,10 +1253,10 @@ export class Decimal { * -0 if x is -0, * NaN otherwise * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static sign(n: DecimalValue): number { + static sign(n: Value): double { let x = new Decimal(n); return x.d ? (x.d![0] ? x.s : 0 * x.s) : x.s || NaN; } @@ -871,10 +1264,10 @@ export class Decimal { /** * Return a new Decimal whose value is `n` rounded to an integer using rounding mode `rounding`. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - static round(n: DecimalValue): Decimal { + static round(n: Value): Decimal { let x = new Decimal(n) return Utils.finalise(x, x.e + 1, Decimal.rounding); } @@ -883,14 +1276,14 @@ export class Decimal { * Configures the 'global' settings for this particular Decimal constructor. * * @param { DecimalConfig } config object with one or more of the following properties, - * precision {number} - * rounding {number} - * toExpNeg {number} - * toExpPos {number} - * maxE {number} - * minE {number} - * modulo {number} - * crypto {boolean|number} + * precision {double} + * rounding {Rounding} + * toExpNeg {double} + * toExpPos {double} + * maxE {double} + * minE {double} + * modulo {Modulo} + * crypto {boolean} * defaults {true} * @returns { void } * @throws { BusinessError } 10200001 - The value of `DecimalConfig.properties` is out of range. @@ -898,13 +1291,16 @@ export class Decimal { */ static set(config: DecimalConfig): void { let useDefaults = (config.defaults != undefined && config.defaults === true); - Decimal.precision = Utils.checkRange(config.precision, 1, MAX_DIGITS, useDefaults, DEFAULTS_PRECISION); - Decimal.rounding = Utils.checkRange(config.rounding, 0, 8, useDefaults, DEFAULTS_ROUNDING); - Decimal.toExpNeg = Utils.checkRange(config.toExpNeg, -EXP_LIMIT, 0, useDefaults, DEFAULTS_TOEXPNEG); - Decimal.toExpPos = Utils.checkRange(config.toExpPos, 0, EXP_LIMIT, useDefaults, DEFAULTS_TOEXPPOS); - Decimal.maxE = Utils.checkRange(config.maxE, 0, EXP_LIMIT, useDefaults, EXP_LIMIT); - Decimal.minE = Utils.checkRange(config.minE, -EXP_LIMIT, 0, useDefaults, -EXP_LIMIT); - Decimal.modulo = Utils.checkRange(config.modulo, 0, 9, useDefaults, DEFAULTS_MODULO); + Decimal.precision = + Utils.checkRange(config.precision, 1, MAX_DIGITS, useDefaults, [DEFAULTS_PRECISION, Decimal.precision]); + Decimal.rounding = Utils.checkRange(config.rounding, 0, 8, useDefaults, [DEFAULTS_ROUNDING, Decimal.rounding]); + Decimal.toExpNeg = + Utils.checkRange(config.toExpNeg, -EXP_LIMIT, 0, useDefaults, [DEFAULTS_TOEXPNEG, Decimal.toExpNeg]); + Decimal.toExpPos = + Utils.checkRange(config.toExpPos, 0, EXP_LIMIT, useDefaults, [DEFAULTS_TOEXPPOS, Decimal.toExpPos]); + Decimal.maxE = Utils.checkRange(config.maxE, 0, EXP_LIMIT, useDefaults, [EXP_LIMIT, Decimal.maxE]); + Decimal.minE = Utils.checkRange(config.minE, -EXP_LIMIT, 0, useDefaults, [-EXP_LIMIT, Decimal.minE]); + Decimal.modulo = Utils.checkRange(config.modulo, 0, 9, useDefaults, [DEFAULTS_MODULO, Decimal.modulo]); if (useDefaults) { Decimal.crypto = false; @@ -927,7 +1323,7 @@ export class Decimal { public sqrt(): Decimal { let x = this; let d = x.d; - let s: number = x.s; + let s: double = x.s; if (s !== 1 || !d || !d![0]) { return new Decimal(!s || s < 0 && (!d || d![0]) ? NaN : d ? x : Infinity); @@ -936,7 +1332,7 @@ export class Decimal { external = false; s = Math.sqrt(+(x.toNumber())); let n: string = ''; - let e: number = x.e; + let e: double = x.e; let r: Decimal; if (s == 0 || s == Infinity) { n = Utils.digitsToString(d); @@ -944,7 +1340,7 @@ export class Decimal { n += '0'; } s = Math.sqrt(Utils.toNumber(n)); - e = Math.floor((e + 1) / 2) - ((e < 0 || e % 2) as number); + e = Math.floor((e + 1) / 2) - (e < 0 ? 1 : e % 2 ); if (s == Infinity) { n = '5e' + e; } else { @@ -957,7 +1353,7 @@ export class Decimal { } let sd = (e = Decimal.precision) + 3; - let rep: number = 0; + let rep: int = 0; let m: boolean = false; while (true) { let t = r; @@ -975,8 +1371,7 @@ export class Decimal { sd += 4; rep = 1; } else { - if (Utils.toNumber(n) > 0 || Utils.toNumber(n.slice(1)) > 0 - && (n.charAt(0) == c'5')) { + if (!Utils.toNumber(n) || !Utils.toNumber(n.slice(1)) && (n.charAt(0) == c'5')) { Utils.finalise(r, e + 1, 1); m = !r.mul(r).equals(x); } @@ -989,205 +1384,870 @@ export class Decimal { } /** - * Return true if the value of this Decimal is equal to the value of `n`, otherwise return false. + * Return a new Decimal whose value is the cube root of the value of this Decimal, rounded to + * `precision` significant digits using rounding mode `rounding`. * - * @param { DecimalValue } n {number | string | Decimal} - * @returns { boolean } the boolean type + * @returns { Decimal } the Decimal type */ - public equals(n: DecimalValue): boolean { - return this.comparedTo(n) === 0; - } + public cbrt(): Decimal { + let x: Decimal = this; + if (!x.isFinite() || x.isZero()) { + return new Decimal(x); + } + external = false; - /** - * Return true if the value of this Decimal is greater than the value of `n`, otherwise return false. - * - * @param { DecimalValue } n {number | string | Decimal} - * @returns { boolean } the boolean type - */ - public greaterThan(n: DecimalValue): boolean { - return this.comparedTo(n) > 0; - } + // Initial estimate. + let s: double = x.s * Math.pow(x.s * x.toNumber(), 1 / 3); - /** - * Return true if the value of this Decimal is greater than or equal to the value of `n`, - * otherwise return false. - * - * @param { DecimalValue } n {number | string | Decimal} - * @returns { boolean } the boolean type - */ - public greaterThanOrEqualTo(n: DecimalValue): boolean { - let k = this.comparedTo(n); - return k == 1 || k === 0; - } + // Math.cbrt underflow/overflow? + // Pass x to Math.pow as integer, then adjust the exponent of the result. + let n: string = ''; + let e: double = 0; + let r: Decimal; + if (!s || Math.abs(s) == Infinity) { + n = Utils.digitsToString(x.d!); + e = x.e; - /** - * Return true if the value of this Decimal is less than `n`, otherwise return false. - * - * @param { DecimalValue } n {number | string | Decimal} - * @returns { boolean } the boolean type - */ - public lessThan(n: DecimalValue): boolean { - return this.comparedTo(n) < 0; + // Adjust n exponent so it is a multiple of 3 away from x exponent. + if (s = (e - n.length + 1) % 3) { + n += (s == 1 || s == -2 ? '0' : '00'); + } + s = Math.pow(Utils.toNumber(n), 1 / 3); + + // Rarely, e may be one less than the result exponent value. + e = Math.floor((e + 1) / 3) - ((e % 3 == (e < 0 ? -1 : 2)) ? 1 : 0); + + if (s == Infinity) { + n = '5e' + e; + } else { + n = Utils.toExponential(s); + n = n.slice(0, n.indexOf('e') + 1) + e; + } + + r = new Decimal(n); + r.sign = x.s; + } else { + r = new Decimal(s); + } + + let sd = (e = Decimal.precision) + 3; + + // Halley's method. Compare Newton's method. + let rep: int = 0; + let m: boolean = false; + while (true) { + let t = r; + let t3 = t.mul(t).mul(t); + let t3plusx = t3.add(x); + r = Utils.divide(t3plusx.add(x).mul(t), t3plusx.add(t3), sd + 2, 1); + // Replace with for-loop and checkRoundingDigits. + if (Utils.digitsToString(t.d!).slice(0, sd) === (n = Utils.digitsToString(r.d!)).slice(0, sd)) { + n = n.slice(sd - 3, sd + 1); + // The 4th rounding digit may be in error by -1 so if the 4 rounding digits are 9999 or 4999 + // , i.e. approaching a rounding boundary, continue the iteration. + if (n == '9999' || !rep && n == '4999') { + // On the first iteration only, check to see if rounding up gives the exact result as the + // nines may infinitely repeat. + if (!rep) { + Utils.finalise(t, e + 1, 0); + if (t.mul(t).mul(t).equals(x)) { + r = t; + break; + } + } + sd += 4; + rep = 1; + } else { + // If the rounding digits are null, 0{0,4} or 50{0,3}, check for an exact result. + // If not, then there are further digits and m will be truthy. + if (!+Utils.toNumber(n) || !+Utils.toNumber(n.slice(1)) && (n.charAt(0) == c'5')) { + // Truncate to the first rounding digit. + Utils.finalise(r, e + 1, 1); + m = !r.mul(r).mul(r).equals(x); + } + break; + } + } + } + + external = true; + return Utils.finalise(r, e, Decimal.rounding, m); + } + + /** + * Return true if the value of this Decimal is equal to the value of `n`, otherwise return false. + * + * @param { Value } n {double | string | Decimal} + * @returns { boolean } the boolean type + */ + public equals(n: Value): boolean { + return this.comparedTo(n) === 0; + } + + /** + * Return + * 1 if the value of this Decimal is greater than the value of `n`, + * -1 if the value of this Decimal is less than the value of `n`, + * 0 if they have the same value, + * NaN if the value of either Decimal is NaN. + * + * @param { Value } n {double | string | Decimal} + * @returns { double } the number type + */ + public comparedTo(n: Value): double { + let y = new Decimal(n); + let xd = this.d; + let yd = y.d; + let xs = this.s; + let ys = y.s; + + if (!xd || !yd) { + return (!xs || !ys) ? NaN : (xs !== ys) ? xs : (xd === yd) ? 0 : (!xd ^ xs < 0) ? 1 : -1; + } + + if (!xd![0] || !yd![0]) { + return xd![0] ? xs : yd![0] ? -ys : 0; + } + + if (xs !== ys) { + return xs; + } + + if (this.e !== y.e) { + return this.e > y.e ^ xs < 0 ? 1 : -1; + } + + let xdL: number = xd.length; + let ydL: number = yd.length; + + for (let i = 0, j = xdL < ydL ? xdL : ydL; i < j; ++i) { + if (xd![i] !== yd![i]) { + return xd![i] > yd![i] ^ xs < 0 ? 1 : -1; + } + } + return xdL === ydL ? 0 : xdL > ydL ^ xs < 0 ? 1 : -1; + } + + /** + * Return a new Decimal whose value is the value of this Decimal divided by `n`, rounded to + * `precision` significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + */ + public div(n: Value): Decimal { + return Utils.divide(this, new Decimal(n)); + } + + /** + * Return a new Decimal whose value is the value of this Decimal modulo `n`, rounded to + * `precision` significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal }the Decimal type + */ + public mod(n: Value): Decimal { + let y = new Decimal(n); + if (!this.d || !y.s || y.d && !y.d![0]) { + return new Decimal(NaN); + } + + if (!y.d || this.d && !this.d![0]) { + return Utils.finalise(new Decimal(this), Decimal.precision, Decimal.rounding); + } + + external = false; + let q: Decimal; + if (Decimal.modulo == 9) { + q = Utils.divide(this, y.abs(), 0, 3, true); + q.sign *= y.s; + } else { + q = Utils.divide(this, y, 0, Decimal.modulo, true); + } + + q = q.mul(y); + external = true; + return this.sub(q); + } + + /** + * Return a new Decimal whose value is the value of this Decimal raised to the power `n`, rounded + * to `precision` significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + */ + public pow(n: Value): Decimal { + let y = new Decimal(n); + let yn = +y.toNumber(); + if (!this.d || !y.d || !this.d![0] || !y.d![0]) { + return new Decimal(Math.pow(+this.toNumber(), yn)); + } + + let x = new Decimal(this); + if (x.equals(1)) { + return x; + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + if (y.equals(1)) { + return Utils.finalise(x, pr, rm); + } + let e = Math.floor(y.e / LOG_BASE); + let k: double; + if (e >= y.d!.length - 1 && (k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) { + let r = this.intPow(x, k, pr); + return y.s < 0 ? new Decimal(1).div(r) : Utils.finalise(r, pr, rm); + } + + let s: double = x.s; + if (s < 0) { + if (e < y.d!.length - 1) { + return new Decimal(NaN); + } + if ((e < y.d!.length ? y.d![e.toInt()] & 1 : 0) == 0) { + s = 1; + } + if (x.e == 0 && x.d![0] == 1 && x.d!.length == 1) { + x.sign = s; + return x; + } + } + + k = Math.pow(+x.toNumber(), yn); + e = k == 0 || !isFinite(k) + ? Math.floor(yn * (Math.log(Utils.toNumber('0.' + Utils.digitsToString(x.d!))) / Utils.toNumber(LN10) + x.e + 1)) + : new Decimal(k + '').e; + + if (e > Decimal.maxE + 1 || e < Decimal.minE - 1) { + return new Decimal(e > 0 ? s * Infinity : 0); + } + + external = false; + x.sign = 1; + Decimal.rounding = 1; + // Estimate the extra guard digits needed to ensure five correct rounding digits from + // naturalLogarithm(x). Example of failure without these extra digits (precision: 10): + // new Decimal(2.32456).pow('2087987436534566.46411') + // should be 1.162377823e+764914905173815, but is 1.162355823e+764914905173815 + k = Math.min(12, (e + '').length); + let r = this.naturalExponential(y.mul(this.naturalLogarithm(x, pr + k)), pr); + if (r.d) { + r = Utils.finalise(r, pr + 5, 1); + if (Utils.checkRoundingDigits(r.d!, pr, rm)) { + e = pr + 10; + r = Utils.finalise(this.naturalExponential(y.mul(this.naturalLogarithm(x, e + k)), e), e + 5, 1); + if (Utils.toNumber(Utils.digitsToString(r.d!).slice(pr + 1, pr + 15)) + 1 == 1e14) { + r = Utils.finalise(r, pr + 1, 0); + } + } + } + r.sign = s; + external = true; + Decimal.rounding = rm; + return Utils.finalise(r, pr, rm); + } + + /** + * Return a new Decimal whose value is the natural exponential of the value of this Decimal, + * i.e. the base e raised to the power the value of this Decimal, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @returns { Decimal } the Decimal type + */ + public exp(): Decimal { + return this.naturalExponential(this); + } + + /** + * Return the logarithm of the value of this Decimal to the specified base, rounded to `precision` + * significant digits using rounding mode `rounding`. + * + * @param { Value } n {double | string | Decimal} + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + public log(n: Value): Decimal { + let base = new Decimal(n); + let d = base.d; + + // Return NaN if base is negative, or non-finite, or is 0 or 1. + if (base.s < 0 || !d || !d[0] || base.equals(1)) { + return new Decimal(NaN); + } + + let isBase10 = base.equals(10); + d = this.d; + // Is arg negative, non-finite, 0 or 1? + if (this.s < 0 || !d || !d[0] || this.equals(1)) { + return new Decimal(d && !d[0] ? -Infinity : this.s != 1 ? NaN : d ? 0 : Infinity); + } + + // The result will have a non-terminating decimal expansion if base is 10 and arg is not an + // integer power of 10. + let inf: boolean = false; + let k: double; + if (isBase10) { + if (d.length > 1) { + inf = true; + } else { + for (k = d[0]; k % 10 === 0;) { + k /= 10; + } + inf = k !== 1; + } + } + + external = false; + let guard = 5; + let pr = Decimal.precision; + let rm = Decimal.rounding; + let sd = pr + guard; + let num = this.naturalLogarithm(this, sd); + let denominator = isBase10 ? this.getLn10(sd + 10) : this.naturalLogarithm(base, sd); + + // The result will have 5 rounding digits. + let r = Utils.divide(num, denominator, sd, 1); + + // If at a rounding boundary, i.e. the result's rounding digits are [49]9999 or [50]0000, + // calculate 10 further digits. + // + // If the result is known to have an infinite decimal expansion, repeat this until it is clear + // that the result is above or below the boundary. Otherwise, if after calculating the 10 + // further digits, the last 14 are nines, round up and assume the result is exact. + // Also assume the result is exact if the last 14 are zero. + // + // Example of a result that will be incorrectly rounded: + // log[1048576](4503599627370502) = 2.60000000000000009610279511444746... + // The above result correctly rounded using ROUND_CEILING to 1 decimal place should be 2.7, but it + // will be given as 2.6 as there are 15 zeros immediately after the requested decimal place, so + // the exact result would be assumed to be 2.6, which rounded using ROUND_CEILING to 1 decimal + // place is still 2.6. + if (Utils.checkRoundingDigits(r.d!, k = pr, rm)) { + do { + sd += 10; + num = this.naturalLogarithm(this, sd); + denominator = isBase10 ? this.getLn10(sd + 10) : this.naturalLogarithm(base, sd); + r = Utils.divide(num, denominator, sd, 1); + + if (!inf) { + // Check for 14 nines from the 2nd rounding digit, as the first may be 4. + if (+Utils.toNumber(Utils.digitsToString(r.d!).slice(k + 1, k + 15)) + 1 == 1e14) { + r = Utils.finalise(r, pr + 1, 0); + } + break; + } + } while (Utils.checkRoundingDigits(r.d!, k += 10, rm)); + } + external = true; + + return Utils.finalise(r, pr, rm); + } + + /** + * Return a new Decimal whose value is the natural logarithm of the value of this Decimal, + * rounded to `precision` significant digits using rounding mode `rounding`. + * + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + public ln(): Decimal { + return this.naturalLogarithm(this); + } + + /** + * Return a new Decimal whose value is the cosine of the value in radians of this Decimal. + * + * @returns { Decimal } the Decimal type + */ + public cos(): Decimal { + let x: Decimal = this; + if (!x.d) { + return new Decimal(NaN); + } + + if (!x.d![0]) { + return new Decimal(1); + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + Decimal.precision = pr + Math.max(this.e, x.precision()) + LOG_BASE; + Decimal.rounding = 1; + + x = this.cosine(this.toLessThanHalfPi(x)); + Decimal.precision = pr; + Decimal.rounding = rm; + + return Utils.finalise(Decimal.quadrant == 2 || Decimal.quadrant == 3 ? x.negate() : x, pr, rm, true); + } + + /** + * Return a new Decimal whose value is the sine of the value in radians of this Decimal. + * + * @returns { Decimal } the Decimal type + */ + public sin(): Decimal { + let x: Decimal = this; + if (!x.isFinite()) { + return new Decimal(NaN); + } + if (x.isZero()) { + return new Decimal(x); + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + Decimal.precision = pr + Math.max(x.e, x.precision()) + LOG_BASE; + Decimal.rounding = 1; + + x = this.sine(this.toLessThanHalfPi(x)); + + Decimal.precision = pr; + Decimal.rounding = rm; + + return Utils.finalise(Decimal.quadrant > 2 ? x.negate() : x, pr, rm, true); + } + + /** + * Return a new Decimal whose value is the tangent of the value in radians of this Decimal. + * + * @returns { Decimal } the Decimal type + */ + public tan(): Decimal { + let x: Decimal = this; + if (!x.isFinite()) { + return new Decimal(NaN); + } + if (x.isZero()) { + return new Decimal(x); + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + Decimal.precision = pr + 10; + Decimal.rounding = 1; + + x = x.sin(); + x.sign = 1; + x = Utils.divide(x, new Decimal(1).sub(x.mul(x)).sqrt(), pr + 10, 0); + + Decimal.precision = pr; + Decimal.rounding = rm; + + return Utils.finalise(Decimal.quadrant == 2 || Decimal.quadrant == 4 ? x.negate() : x, pr, rm, true); + } + + /** + * Return a new Decimal whose value is the hyperbolic cosine of the value in radians of this + * Decimal. + * + * @returns { Decimal } the Decimal type + */ + public cosh(): Decimal { + let x: Decimal = this; + if (!x.isFinite()) { + return new Decimal(x.s ? Infinity : NaN); + } + let one = new Decimal(1); + if (x.isZero()) { + return one; + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + Decimal.precision = pr + Math.max(x.e, x.precision()) + 4; + Decimal.rounding = 1; + let len: number = x.d!.length; + + // Argument reduction: cos(4x) = 1 - 8cos^2(x) + 8cos^4(x) + 1 + // i.e. cos(x) = 1 - cos^2(x/4)(8 - 8cos^2(x/4)) + + // Estimate the optimum number of times to use the argument reduction. + let k: double = 0; + let n: string = ''; + if (len < 32) { + k = Math.ceil(len / 3); + n = Utils.toString((1 / Utils.tinyPow(4, k))); + } else { + k = 16; + n = '2.3283064365386962890625e-10'; + } + + x = this.taylorSeries(1, x.mul(n), new Decimal(1), true); + + // Reverse argument reduction + let i = k; + let d8 = new Decimal(8); + for (; i--;) { + let cosh2_x = x.mul(x); + x = one.sub(cosh2_x.mul(d8.sub(cosh2_x.mul(d8)))); + } + + return Utils.finalise(x, Decimal.precision = pr, Decimal.rounding = rm, true); + } + + /** + * Return a new Decimal whose value is the hyperbolic sine of the value in radians of this Decimal. + * + * @returns { Decimal } the Decimal type + */ + public sinh(): Decimal { + let x: Decimal = this; + if (!x.isFinite() || x.isZero()) { + return new Decimal(x); + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + Decimal.precision = pr + Math.max(x.e, x.precision()) + 4; + Decimal.rounding = 1; + let len: number = x.d!.length; + + if (len < 3) { + x = this.taylorSeries(2, x, x, true); + } else { + // Alternative argument reduction: sinh(3x) = sinh(x)(3 + 4sinh^2(x)) + // i.e. sinh(x) = sinh(x/3)(3 + 4sinh^2(x/3)) + // 3 multiplications and 1 addition + // Argument reduction: sinh(5x) = sinh(x)(5 + sinh^2(x)(20 + 16sinh^2(x))) + // i.e. sinh(x) = sinh(x/5)(5 + sinh^2(x/5)(20 + 16sinh^2(x/5))) + // 4 multiplications and 2 additions + // Estimate the optimum number of times to use the argument reduction. + let k = 1.4 * Math.sqrt(len); + k = k > 16 ? 16 : k | 0; + x = x.mul(1 / Utils.tinyPow(5, k)); + x = this.taylorSeries(2, x, x, true); + + // Reverse argument reduction + let d5 = new Decimal(5); + let d16 = new Decimal(16); + let d20 = new Decimal(20); + for (; k--;) { + let sinh2_x = x.mul(x); + x = x.mul(d5.add(sinh2_x.mul(d16.mul(sinh2_x).add(d20)))); + } + } + + Decimal.precision = pr; + Decimal.rounding = rm; + + return Utils.finalise(x, pr, rm, true); + } + + /** + * Return a new Decimal whose value is the hyperbolic tangent of the value in radians of this Decimal. + * + * @returns { Decimal } the Decimal type + */ + public tanh(): Decimal { + let x: Decimal = this; + if (!x.isFinite()) { + return new Decimal(x.s); + } + if (x.isZero()) { + return new Decimal(x); + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + Decimal.precision = pr + 7; + Decimal.rounding = 1; + + return Utils.divide(x.sinh(), x.cosh(), Decimal.precision = pr, Decimal.rounding = rm); + } + + /** + * Return true if the value of this Decimal is greater than the value of `n`, otherwise return false. + * + * @param { Value } n {double | string | Decimal} + * @returns { boolean } the boolean type + */ + public greaterThan(n: Value): boolean { + return this.comparedTo(n) > 0; + } + + /** + * Return true if the value of this Decimal is greater than or equal to the value of `n`, + * otherwise return false. + * + * @param { Value } n {double | string | Decimal} + * @returns { boolean } the boolean type + */ + public greaterThanOrEqualTo(n: Value): boolean { + let k = this.comparedTo(n); + return k == 1 || k === 0; + } + + /** + * Return true if the value of this Decimal is less than `n`, otherwise return false. + * + * @param { Value } n {double | string | Decimal} + * @returns { boolean } the boolean type + */ + public lessThan(n: Value): boolean { + return this.comparedTo(n) < 0; + } + + /** + * Return true if the value of this Decimal is less than or equal to `n`, otherwise return false. + * + * @param { Value } n {double | string | Decimal} + * @returns { boolean } the boolean type + */ + public lessThanOrEqualTo(n: Value): boolean { + return this.comparedTo(n) <= 0; + } + + /** + * Return a new Decimal whose value is the arccosine (inverse cosine) in radians of the value of this Decimal. + * + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. + */ + public acos(): Decimal { + let x: Decimal = this; + let k = x.abs().comparedTo(1); + let pr = Decimal.precision; + let rm = Decimal.rounding; + + if (k != -1) { + return k == 0 + // |x| is 1 + ? x.isNegative() ? Utils.getPi(pr, rm) : new Decimal(0) + // |x| > 1 or x is NaN + : new Decimal(NaN); + } + + if (x.isZero()) { + return Utils.getPi(pr + 4, rm).mul(0.5); + } + + Decimal.precision = pr + 6; + Decimal.rounding = 1; + + x = (new Decimal(1)).sub(x).div(x.add(1)).sqrt().atan(); + + Decimal.precision = pr; + Decimal.rounding = rm; + + return x.mul(2); } /** - * Return true if the value of this Decimal is less than or equal to `n`, otherwise return false. + * Return a new Decimal whose value is the arcsine (inverse sine) in radians of the value of this + * Decimal. * - * @param { DecimalValue } n {number | string | Decimal} - * @returns { boolean } the boolean type + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. */ - public lessThanOrEqualTo(n: DecimalValue): boolean { - return this.comparedTo(n) <= 0; + public asin(): Decimal { + let x: Decimal = this; + if (x.isZero()) { + return new Decimal(x); + } + + let k = x.abs().comparedTo(1); + let pr = Decimal.precision; + let rm = Decimal.rounding; + if (k != -1) { + // |x| is 1 + if (k == 0) { + let halfPi = Utils.getPi(pr + 4, rm).mul(0.5); + halfPi.sign = x.s; + return halfPi; + } + // |x| > 1 or x is NaN + return new Decimal(NaN); + } + + Decimal.precision = pr + 6; + Decimal.rounding = 1; + + x = x.div(new Decimal(1).sub(x.mul(x)).sqrt().add(1)).atan(); + + Decimal.precision = pr; + Decimal.rounding = rm; + + return x.mul(2); } /** - * Return - * 1 if the value of this Decimal is greater than the value of `n`, - * -1 if the value of this Decimal is less than the value of `n`, - * 0 if they have the same value, - * NaN if the value of either Decimal is NaN. + * Return a new Decimal whose value is the arctangent (inverse tangent) in radians of the value of this Decimal. * - * @param { DecimalValue } n {number | string | Decimal} - * @returns { number } the number type + * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. */ - public comparedTo(n: DecimalValue): number { - let y = new Decimal(n); - let xd = this.d; - let yd = y.d; - let xs = this.s; - let ys = y.s; - - if (!xd || !yd) { - return (!xs || !ys) ? NaN : (xs !== ys) ? xs : (xd === yd) ? 0 : (!xd ^ xs < 0) ? 1 : -1; + public atan(): Decimal { + let x: Decimal = this; + let pr = Decimal.precision; + let rm = Decimal.rounding; + let r: Decimal; + if (!x.isFinite()) { + if (!x.s) { + return new Decimal(NaN); + } + if (pr + 4 <= PI_PRECISION) { + r = Utils.getPi(pr + 4, rm).mul(0.5); + r.sign = x.s; + return r; + } + } else if (x.isZero()) { + return new Decimal(x); + } else if (x.abs().equals(1) && pr + 4 <= PI_PRECISION) { + r = Utils.getPi(pr + 4, rm).mul(0.25); + r.sign = x.s; + return r; } - if (!xd![0] || !yd![0]) { - return xd![0] ? xs : yd![0] ? -ys : 0; + let wpr: double = pr + 10; + Decimal.precision = wpr; + Decimal.rounding = 1; + + // Argument reduction + // Ensure |x| < 0.42 + // atan(x) = 2 * atan(x / (1 + sqrt(1 + x^2))) + let k = Math.min(28, wpr / LOG_BASE + 2 | 0); + let i: int = 0; + for (i = k.toInt(); i; --i) { + x = x.div(x.mul(x).add(1).sqrt().add(1)); } + external = false; - if (xs !== ys) { - return xs; + let j = Math.ceil(wpr / LOG_BASE).toInt(); + let n = 1; + let x2 = x.mul(x); + r = new Decimal(x); + let px = x; + + // atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ... + for (; i != -1;) { + px = px.mul(x2); + let t = r.sub(px.div(n += 2)); + px = px.mul(x2); + r = t.add(px.div(n += 2)); + if (j < r.d!.length && r.d![j] != undefined) { + for (i = j; r.d![i] == (i < t.d!.length ? t.d![i] : undefined) && i--;); + } } - if (this.e !== y.e) { - return this.e > y.e ^ xs < 0 ? 1 : -1; + if (k) { + r = r.mul(2 << (k - 1)); } - let xdL = xd.length; - let ydL = yd.length; + external = true; - for (let i = 0, j = xdL < ydL ? xdL : ydL; i < j; ++i) { - if (xd![i] !== yd![i]) { - return xd![i] > yd![i] ^ xs < 0 ? 1 : -1; - } - } - return xdL === ydL ? 0 : xdL > ydL ^ xs < 0 ? 1 : -1; + return Utils.finalise(r, Decimal.precision = pr, Decimal.rounding = rm, true); } /** - * Return a new Decimal whose value is the value of this Decimal divided by `n`, rounded to - * `precision` significant digits using rounding mode `rounding`. + * Return a new Decimal whose value is the inverse of the hyperbolic cosine in radians of the + * value of this Decimal. * - * @param { DecimalValue } n {number | string | Decimal} * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. */ - public div(n: DecimalValue): Decimal { - return Utils.divide(this, new Decimal(n)); + public acosh(): Decimal { + let x: Decimal = this; + if (x.comparedTo(1) == -1) { + return new Decimal(x.equals(1) ? 0 : NaN); + } + if (!x.isFinite()) { + return new Decimal(x); + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + Decimal.precision = pr + Math.max(Math.abs(x.e), x.precision()) + 4; + Decimal.rounding = 1; + external = false; + + x = x.mul(x).sub(1).sqrt().add(x); + + external = true; + Decimal.precision = pr; + Decimal.rounding = rm; + + return x.ln(); } /** - * Return a new Decimal whose value is the value of this Decimal raised to the power `n`, rounded - * to `precision` significant digits using rounding mode `rounding`. + * Return a new Decimal whose value is the inverse of the hyperbolic sine in radians of the value + * of this Decimal. * - * @param { DecimalValue } n {number | string | Decimal} * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. */ - public pow(n: DecimalValue): Decimal { - let y = new Decimal(n); - let yn = +y.toNumber(); - if (!this.d || !y.d || !this.d![0] || !y.d![0]) { - return new Decimal(Math.pow(+this.toNumber(), yn)); - } - - let x = new Decimal(this); - if (x.equals(1)) { - return x; + public asinh(): Decimal { + let x: Decimal = this; + if (!x.isFinite() || x.isZero()) { + return new Decimal(x); } let pr = Decimal.precision; let rm = Decimal.rounding; - if (y.equals(1)) { - return Utils.finalise(x, pr, rm); - } - let e = Math.floor(y.e / LOG_BASE); - let k: number; - if (e >= y.d!.length - 1 && (k = yn < 0 ? -yn : yn) <= MAX_SAFE_INTEGER) { - let r = this.intPow(x, k, pr); - return y.s < 0 ? new Decimal(1).div(r) : Utils.finalise(r, pr, rm); - } - - let s: number = x.s; - if (s < 0) { - if (e < y.d!.length - 1) { - return new Decimal(NaN); - } - if ((y.d![e] & 1) == 0) { - s = 1; - } - if (x.e == 0 && x.d![0] == 1 && x.d!.length == 1) { - x.sign = s; - return x; - } - } - - k = Math.pow(+x.toNumber(), yn); - e = k == 0 || !isFinite(k) - ? Math.floor(yn * (Math.log(Utils.toNumber('0.' + Utils.digitsToString(x.d!))) / Math.LN10 + x.e + 1)) - : new Decimal(k + '').e; + Decimal.precision = pr + 2 * Math.max(Math.abs(x.e), x.precision()) + 6; + Decimal.rounding = 1; + external = false; - if (e > Decimal.maxE + 1 || e < Decimal.minE - 1) { - return new Decimal(e > 0 ? s / 0 : 0); - } + x = x.mul(x).add(1).sqrt().add(x); - external = false; - Decimal.rounding = x.sign = 1; - k = Math.min(12, (e + '').length); - let r = this.naturalExponential(y.mul(this.naturalLogarithm(x, pr + k)), pr); - if (r.d) { - r = Utils.finalise(r, pr + 5, 1); - if (Utils.checkRoundingDigits(r.d!, pr, rm)) { - e = pr + 10; - r = Utils.finalise(this.naturalExponential(y.mul(this.naturalLogarithm(x, e + k)), e), e + 5, 1); - if (Utils.toNumber(Utils.digitsToString(r.d!).slice(pr + 1, pr + 15)) + 1 == 1e14) { - r = Utils.finalise(r, pr + 1, 0); - } - } - } - r.sign = s; external = true; + Decimal.precision = pr; Decimal.rounding = rm; - return Utils.finalise(r, pr, rm); + + return x.ln(); } /** - * Return a new Decimal whose value is the natural exponential of the value of this Decimal, - * i.e. the base e raised to the power the value of this Decimal, rounded to `precision` - * significant digits using rounding mode `rounding`. + * Return a new Decimal whose value is the inverse of the hyperbolic tangent in radians of the + * value of this Decimal. * * @returns { Decimal } the Decimal type + * @throws { BusinessError } 10200060 - Precision limit exceeded. */ - public exp(): Decimal { - return this.naturalExponential(this); + public atanh(): Decimal { + let x: Decimal = this; + if (!x.isFinite()) { + return new Decimal(NaN); + } + if (x.e >= 0) { + return new Decimal(x.abs().equals(1) ? x.s * Infinity : x.isZero() ? x : NaN); + } + + let pr = Decimal.precision; + let rm = Decimal.rounding; + let xsd = x.precision(); + + if (Math.max(xsd, pr) < 2 * -x.e - 1) { + return Utils.finalise(new Decimal(x), pr, rm, true); + } + + let wpr = xsd - x.e; + Decimal.precision = wpr; + + x = Utils.divide(x.add(1), new Decimal(1).sub(x), wpr + pr, 1); + + Decimal.precision = pr + 4; + Decimal.rounding = 1; + + x = x.ln(); + + Decimal.precision = pr; + Decimal.rounding = rm; + + return x.mul(0.5); } /** * Return a new Decimal whose value is the value of this Decimal plus `n`, rounded to `precision` * significant digits using rounding mode `rounding`. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - public add(n: DecimalValue): Decimal { + public add(n: Value): Decimal { let y = new Decimal(n); if (!this.d || !y.d) { if (!this.s || !y.s) { @@ -1202,13 +2262,13 @@ export class Decimal { y.sign = -y.s; return this.sub(y); } - let xd = this.d; - let yd = y.d; + let xd = this.d!; + let yd = y.d!; let pr = Decimal.precision; let rm = Decimal.rounding; - if (!xd![0] || !yd![0]) { - if (!yd![0]) { + if (!xd[0] || !yd[0]) { + if (!yd[0]) { y = new Decimal(this); } return external ? Utils.finalise(y, pr, rm) : y; @@ -1216,21 +2276,25 @@ export class Decimal { let k = Math.floor(this.e / LOG_BASE); let e = Math.floor(y.e / LOG_BASE); xd = xd!.slice(); - let i = k - e; - let len: number; - let d: Array; + let i = (k - e).toInt(); + let len: int; + let d: Array; if (i) { if (i < 0) { d = xd; - i = -i; - len = yd!.length; + if (i == Int.MIN_VALUE) { + i = Int.MAX_VALUE; + } else { + i = -i; + } + len = yd.length; } else { - d = yd!; + d = yd; e = k; len = xd.length; } k = Math.ceil(pr / LOG_BASE); - len = k > len ? k + 1 : len + 1; + len = (k > len ? k + 1 : len + 1).toInt(); if (i > len) { i = len; d.length = 1; @@ -1242,17 +2306,17 @@ export class Decimal { d.reverse(); } len = xd.length; - i = yd!.length; + i = yd.length; if (len - i < 0) { i = len; - d = yd!; + d = yd; yd = xd; xd = d; } - let carry: number = 0; + let carry: Long = 0; for (carry = 0; i;) { --i; - xd[i] = xd[i] + yd![i] + carry; + xd[i] = xd[i] + yd[i] + carry; carry = (xd[i] / BASE) | 0; xd[i] = xd[i] % BASE; } @@ -1274,10 +2338,10 @@ export class Decimal { * Return a new Decimal whose value is the value of this Decimal minus `n`, rounded to `precision` * significant digits using rounding mode `rounding`. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - public sub(n: DecimalValue): Decimal { + public sub(n: Value): Decimal { let y: Decimal = new Decimal(n); let x = this; if (!x.d || !y.d) { @@ -1296,14 +2360,14 @@ export class Decimal { return x.add(y); } - let xd = x.d; - let yd = y.d; + let xd = x.d!; + let yd = y.d!; let pr = Decimal.precision; let rm = Decimal.rounding; - if (!xd![0] || !yd![0]) { - if (yd![0]) { + if (!xd[0] || !yd[0]) { + if (yd[0]) { y.sign = -y.s; - } else if (xd![0]) { + } else if (xd[0]) { y = new Decimal(x); } else { return new Decimal(rm === 3 ? -0 : 0); @@ -1314,22 +2378,22 @@ export class Decimal { // Calculate base 1e7 exponents. let yExp = Math.floor(y.e / LOG_BASE); let xExp = Math.floor(x.e / LOG_BASE); - xd = xd!.slice(); + xd = xd.slice(); let expDiff = xExp - yExp; - let len: number; - let i: number; + let len: int; + let i: int; let xLessThanY: boolean = false; if (expDiff) { xLessThanY = expDiff < 0; - let d: Array; + let d: Array; if (xLessThanY) { - d = xd!; + d = xd; expDiff = -expDiff; - len = yd!.length; + len = yd.length; } else { - d = yd!; + d = yd; yExp = xExp; - len = xd!.length; + len = xd.length; } let i = Math.max(Math.ceil(pr / LOG_BASE), len) + 2; if (expDiff > i) { @@ -1342,15 +2406,15 @@ export class Decimal { } d.reverse(); } else { - let i = xd!.length; - len = yd!.length; + let i = xd.length; + len = yd.length; xLessThanY = i < len; if (xLessThanY) { len = i; } for (i = 0; i < len; i++) { - if (xd![i] != yd![i]) { - xLessThanY = xd![i] < yd![i]; + if (xd[i] != yd[i]) { + xLessThanY = xd[i] < yd[i]; break; } } @@ -1363,36 +2427,36 @@ export class Decimal { yd = d; y.sign = -y.s; } - len = xd!.length; - for (i = yd!.length - len; i > 0; --i) { - xd!.push(0); + len = xd.length; + for (i = yd.length - len; i > 0; --i) { + xd.push(0); len++ } - let j: number; - for (i = yd!.length; i > expDiff;) { - if (xd![--i] < yd![i]) { - for (j = i; j && xd![--j] === 0;) { - xd![j] = BASE - 1; + let j: int; + for (i = yd.length; i > expDiff && i > 0;) { + if (xd[--i] < yd[i]) { + for (j = i; j && xd[--j] === 0;) { + xd[j] = BASE - 1; } - --xd![j]; - xd![i] = xd![i] + BASE; + --xd[j]; + xd[i] = xd[i] + BASE; } - xd![i] -= yd![i]; + xd[i] -= yd[i]; } - for (; xd![--len] === 0;) { - xd!.pop(); + for (; len > 0 && xd[--len] === 0;) { + xd.pop(); } - for (; xd![0] === 0; xd!.shift()) { + for (; xd.length > 0 && xd[0] === 0; xd.shift()) { --yExp; } - if (!xd![0]) { + if (xd.length == 0 || (xd.length > 0 && !xd[0])) { return new Decimal(rm === 3 ? -0 : 0); } y.digits = xd; - y.exponent = this.getBase10Exponent(xd!, yExp); + y.exponent = this.getBase10Exponent(xd, yExp); return external ? Utils.finalise(y, pr, rm) : y; } @@ -1400,24 +2464,24 @@ export class Decimal { * Return a new Decimal whose value is this Decimal times `n`, rounded to `precision` significant * digits using rounding mode `rounding`. * - * @param { DecimalValue } n {number | string | Decimal} + * @param { Value } n {double | string | Decimal} * @returns { Decimal } the Decimal type */ - public mul(n: DecimalValue): Decimal { + public mul(n: Value): Decimal { let xd = this.digits; let y = new Decimal(n); let yd = y.d; y.sign *= this.sign; - if (!xd || !(xd![0]) || !yd || !(yd![0])) { - return new Decimal((!y.s || xd && !(xd![0]) && !yd || yd && !(yd![0]) && !xd) + if (!xd || !(xd!.at(0)) || !yd || !(yd!.at(0))) { + return new Decimal((!y.s || xd && !(xd!.at(0)) && !yd || yd && !(yd!.at(0)) && !xd) ? NaN : (!xd || !yd) ? y.s / 0 : y.s * 0); } let e = Math.floor(this.exponent / LOG_BASE) + Math.floor(y.e / LOG_BASE); - let xdL = xd!.length; - let ydL = yd!.length; - let r: Array | null; - let rL: number; + let xdL: int = xd!.length; + let ydL: int = yd!.length; + let r: Array | null; + let rL: int; if (xdL < ydL) { r = xd; xd = yd; @@ -1426,20 +2490,20 @@ export class Decimal { xdL = ydL; ydL = rL; } - r = new Array(); + r = new Array(); rL = xdL + ydL; - let i: number; + let i: int; for (i = rL; i--;) { r.push(0); } - let carry: number = 0; - let k: number; - let t: number; + let carry: Long = 0; + let k: int; + let t: double; for (i = ydL; --i >= 0;) { carry = 0; for (k = xdL + i; k > i;) { - t = r[k] + yd![i] * xd![k - i - 1] + carry; + t = r[k] + yd!.at(i)! * xd!.at(k - i - 1)! + carry; r[k--] = t % BASE | 0; carry = t / BASE | 0; } @@ -1478,9 +2542,9 @@ export class Decimal { } } - private initializeByNumber(v: number) { + private initializeByNumber(v: double) { if (v == 0) { - this.sign = 1; + this.sign = 1 / v < 0 ? -1 : 1; this.exponent = 0; this.digits = Utils.updateDigits(0); return; @@ -1494,8 +2558,8 @@ export class Decimal { } if (tv === ~~tv && tv < 1e7) { - let e: number; - let i: number; + let e: double; + let i: double; for (e = 0, i = tv; i >= 10; i /= 10) { e++; } @@ -1527,9 +2591,9 @@ export class Decimal { } private initializeByString(v: string) { - let i: number; + let i: int; let str: string = v; - if ((i = v.charCodeAt(0)) === 45) { + if ((i = v.charCodeAt(0).toInt()) === 45) { str = v.slice(1); this.sign = -1; } else { @@ -1546,9 +2610,9 @@ export class Decimal { } private parseDecimal(str: String): void { - let e: number; - let i: number; - let len: number; + let e: double; + let i: double; + let len: double; if ((e = str.indexOf('.')) > -1) { str = str.replace('.', ''); } @@ -1570,7 +2634,7 @@ export class Decimal { if (str) { len -= i; this.exponent = e = e - i - 1; - this.digits = new Array(); + this.digits = new Array(); i = (e + 1) % LOG_BASE; if (e < 0) { i += LOG_BASE; @@ -1624,7 +2688,7 @@ export class Decimal { return; } - let base: number; + let base: int; if (isHex.test(str)) { base = 16; str = str.toLowerCase(); @@ -1637,8 +2701,8 @@ export class Decimal { `The type of "test(str)" must be Hex/Binary/Octal. Received value is: ${str}`); } - let i = str.search(new RegExp("p", "i")); - let p: number = 0; + let i = str.search(new RegExp("p", "i")).toInt(); + let p: double = 0; if (i > 0) { p = Utils.toNumber(str.slice(i + 1)); str = str.substring(2, i); @@ -1646,13 +2710,13 @@ export class Decimal { str = str.slice(2); } - i = str.indexOf('.'); + i = str.indexOf('.').toInt(); let isFloat: boolean = i >= 0; - let len: number = 0; + let len: int = 0; let divisor: Decimal = new Decimal(0); if (isFloat) { str = str.replace('.', ''); - len = str.length; + len = str.length.toInt(); i = len - i; divisor = this.intPow(new Decimal(base), i, i * 2); } @@ -1683,7 +2747,7 @@ export class Decimal { return; } - private intPow(x: Decimal, n: number, pr: number): Decimal { + private intPow(x: Decimal, n: double, pr: double): Decimal { let r = new Decimal(1); let k = Math.ceil(pr / LOG_BASE + 4); external = false; @@ -1699,8 +2763,8 @@ export class Decimal { n = Math.floor(n / 2); if (n === 0) { n = r.d!.length - 1; - if (isTruncated && r.d![n] === 0) { - ++r.d![n]; + if (isTruncated && r.d![n.toInt()] === 0) { + ++r.d![n.toInt()]; } break; } @@ -1711,7 +2775,7 @@ export class Decimal { return r; } - private getBase10Exponent(digits: Array, e: number): number { + private getBase10Exponent(digits: Array, e: double): double { let w = digits[0]; for (e *= LOG_BASE; w >= 10; w /= 10) { e++; @@ -1719,17 +2783,17 @@ export class Decimal { return e; } - private truncate(arr: Array, len: number): boolean { + private truncate(arr: Array, len: double): boolean { if (arr.length > len) { - arr.length = len; + arr.length = len.toInt(); return true; } return false; } - private convertBase(str: string, baseIn: number, baseOut: number): Array { - let arrL: number; - let arr: Array = Utils.updateDigits(0); + private convertBase(str: string, baseIn: int, baseOut: double): Array { + let arrL: int; + let arr: Array = Utils.updateDigits(0); for (let i = 0; i < str.length;) { for (arrL = arr.length; arrL--;) { arr[arrL] = arr[arrL] * baseIn; @@ -1748,14 +2812,14 @@ export class Decimal { return arr.reverse(); } - internal finiteToString(isExp: boolean, sd?: number): string { + private finiteToString(isExp: boolean, sd?: double): string { if (!this.isFinite()) { return this.nonFiniteToString(); } - let k: number; + let k: double; let e = this.exponent; let str: string = Utils.digitsToString(this.d!); - let len: number = str.length; + let len: double = str.length; if (isExp) { if (sd && (k = sd - len) > 0) { str = str.charAt(0) + '.' + str.slice(1) + Utils.getZeroString(k); @@ -1780,19 +2844,19 @@ export class Decimal { } private nonFiniteToString(): String { - return String(this.sign * this.sign / 0); + return String(Number.isNaN(this.sign) ? NaN : this.sign * this.sign * Infinity); } - private naturalExponential(x: Decimal, sd?: number): Decimal { + private naturalExponential(x: Decimal, sd?: double): Decimal { let rm = Decimal.rounding; let pr = Decimal.precision; if (!x.d || !x.d![0] || x.e > 17) { return new Decimal(x.d - ? !x.d![0] ? 1 as number : x.s < 0 ? 0 as number : Infinity - : x.s ? x.s < 0 ? 0 as number : x : NaN); + ? !x.d![0] ? 1 as double : x.s < 0 ? 0 as double : Infinity + : x.s ? x.s < 0 ? 0 as double : x : NaN); } - let wpr: number; + let wpr: double; if (sd == undefined) { external = false; wpr = pr; @@ -1800,7 +2864,7 @@ export class Decimal { wpr = sd; } let t = new Decimal(0.03125); - let k: number = 0; + let k: double = 0; while (x.e > -2) { x = x.mul(t); k += 5; @@ -1808,13 +2872,14 @@ export class Decimal { // Use 2 * log10(2^k) + 5 (empirically derived) to estimate the increase in precision // necessary to ensure the first 4 rounding digits are correct. - let guard = Math.log(Math.pow(2, k)) / Math.LN10 * 2 + 5 | 0; + let guard = Math.log(Math.pow(2, k)) / Utils.toNumber(LN10) * 2 + 5 | 0; wpr += guard; let denominator = new Decimal(1); let pow = new Decimal(1); let sum = new Decimal(1); Decimal.precision = wpr; let i = 0; + let rep: int = 0; while (true) { pow = Utils.finalise(pow.mul(x), wpr, 1); denominator = denominator.mul(++i); @@ -1826,7 +2891,6 @@ export class Decimal { } if (sd == undefined) { - let rep: number = 0; if (rep < 3 && Utils.checkRoundingDigits(sum.d!, wpr - guard, rm, rep)) { Decimal.precision = wpr += 10; denominator = pow = t = new Decimal(1); @@ -1844,31 +2908,31 @@ export class Decimal { } } - private naturalLogarithm(y: Decimal, sd?: number): Decimal { + private naturalLogarithm(y: Decimal, sd?: double): Decimal { let x = y; let xd = x.d; let rm = Decimal.rounding; - let n: number = 1; if (x.s < 0 || !xd || !xd![0] || !x.e && xd![0] == 1 && xd!.length == 1) { - return new Decimal(xd && !xd![0] ? -Infinity : x.s != 1 ? NaN : xd ? 0 as number : x); + return new Decimal(xd && !xd![0] ? -Infinity : x.s != 1 ? NaN : xd ? 0 as double : x); } - let wpr: number; + let wpr: double; let pr = Decimal.precision; - if (sd == null) { + if (sd == undefined) { external = false; wpr = pr; } else { wpr = sd; } - let guard: number = 10; + let guard: int = 10; Decimal.precision = wpr += guard; let c = Utils.digitsToString(xd!); let ch = new Char(c.charAt(0)); let c0 = Utils.toNumber(ch.toString()); - let e: number; + let e: double = 0; + let n: double = 1; if (Math.abs(e = x.e) < 1.5e15) { - let n: number = 1; - while (c0 < 7 && c0 != 1 || c0 == 1 && (Utils.toNumber((new Char(c.charAt(1))).toString())) > 3) { + while (c0 < 7 && c0 != 1 || + c0 == 1 && (c.length > 1 ? Utils.toNumber((new Char(c.charAt(1))).toString()) : NaN) > 3) { x = x.mul(y); c = Utils.digitsToString(x.d!); c0 = Utils.toNumber((new Char(c.charAt(0))).toString()); @@ -1885,13 +2949,14 @@ export class Decimal { let t = this.getLn10(wpr + 2, pr).mul(e + ''); x = this.naturalLogarithm(new Decimal(c0 + '.' + c.slice(1)), wpr - guard).add(t); Decimal.precision = pr; - return sd == null ? Utils.finalise(x, pr, rm, external = true) : x; + return sd == undefined ? Utils.finalise(x, pr, rm, external = true) : x; } let x1 = x; let numerator: Decimal; let sum = numerator = x = Utils.divide(x.sub(1), x.add(1), wpr, 1); let x2 = Utils.finalise(x.mul(x), wpr, 1); - let denominator: number = 3; + let denominator: double = 3; + let rep: int = 0; while (true) { numerator = Utils.finalise(numerator.mul(x2), wpr, 1); let t = sum.add(Utils.divide(numerator, new Decimal(denominator), wpr, 1)); @@ -1901,8 +2966,7 @@ export class Decimal { sum = sum.add(this.getLn10(wpr + 2, pr).mul(e + '')); } sum = Utils.divide(sum, new Decimal(n), wpr, 1); - let rep: number = 0; - if (sd == null) { + if (sd == undefined) { if (Utils.checkRoundingDigits(sum.d!, wpr - guard, rm, rep)) { Decimal.precision = wpr += guard; t = numerator = x = Utils.divide(x1.sub(1), x1.add(1), wpr, 1); @@ -1921,7 +2985,7 @@ export class Decimal { } } - private getLn10(sd: number, pr?: number): Decimal { + private getLn10(sd: double, pr?: double): Decimal { if (sd > LN10_PRECISION) { external = true; if (pr != undefined) { @@ -1933,14 +2997,196 @@ export class Decimal { return Utils.finalise(new Decimal(LN10), sd, 1, true); } - private getDigitsValidIndex(digits: Array): number { - let len: number = 0; + private toLessThanHalfPi(x: Decimal): Decimal { + let isNeg = x.s < 0; + let pi = Utils.getPi(Decimal.precision, 1); + let halfPi = pi.mul(0.5); + x = x.abs(); + if (x.comparedTo(halfPi) < 1) { + Decimal.quadrant = isNeg ? 4 : 1; + return x; + } + let t = x.dividedToIntegerBy(pi); + if (t.isZero()) { + Decimal.quadrant = isNeg ? 3 : 2; + } else { + x = x.sub(t.mul(pi)); + // 0 <= x < pi + if (x.comparedTo(halfPi) < 1) { + Decimal.quadrant = Utils.isOdd(t) ? (isNeg ? 2 : 3) : (isNeg ? 4 : 1); + return x; + } + Decimal.quadrant = Utils.isOdd(t) ? (isNeg ? 1 : 4) : (isNeg ? 3 : 2); + } + return x.sub(pi).abs(); + } + + private cosine(x: Decimal): Decimal { + if (x.isZero()) { + return x; + } + + // Argument reduction: cos(4x) = 8*(cos^4(x) - cos^2(x)) + 1 + // i.e. cos(x) = 8*(cos^4(x/4) - cos^2(x/4)) + 1 + // Estimate the optimum number of times to use the argument reduction. + let len: double = x.d!.length; + let k: double = 0; + let y: string = ''; + if (len < 32) { + k = Math.ceil(len / 3); + y = Utils.toString((1 / Utils.tinyPow(4, k))); + } else { + k = 16; + y = '2.3283064365386962890625e-10'; + } + + Decimal.precision += k; + + x = this.taylorSeries(1, x.mul(y), new Decimal(1)); + + // Reverse argument reduction + for (let i = k; i--;) { + let cos2x = x.mul(x); + x = cos2x.mul(cos2x).sub(cos2x).mul(8).add(1); + } + Decimal.precision -= k; + return x; + } + + private sine(x: Decimal): Decimal { + let len: number = x.d!.length; + if (len < 3) { + return x.isZero() ? x : this.taylorSeries(2, x, x); + } + + // Argument reduction: sin(5x) = 16*sin^5(x) - 20*sin^3(x) + 5*sin(x) + // i.e. sin(x) = 16*sin^5(x/5) - 20*sin^3(x/5) + 5*sin(x/5) + // and sin(x) = sin(x/5)(5 + sin^2(x/5)(16sin^2(x/5) - 20)) + // Estimate the optimum number of times to use the argument reduction. + let k = 1.4 * Math.sqrt(len); + k = k > 16 ? 16 : k | 0; + + x = x.mul(1 / Utils.tinyPow(5, k)); + x = this.taylorSeries(2, x, x); + + // Reverse argument reduction + let d5 = new Decimal(5); + let d16 = new Decimal(16); + let d20 = new Decimal(20); + for (; k--;) { + let sin2_x = x.mul(x); + x = x.mul(d5.add(sin2_x.mul(d16.mul(sin2_x).sub(d20)))); + } + + return x; + } + + private taylorSeries(n: int, x: Decimal, y: Decimal, isHyperbolic?: boolean): Decimal { + let pr = Decimal.precision; + let k = Math.ceil(pr / LOG_BASE).toInt(); + + external = false; + let x2 = x.mul(x); + let u = new Decimal(y); + let i: int = 1; + let t: Decimal = new Decimal(0); + let j: int = 0; + while(true) { + t = Utils.divide(u.mul(x2), new Decimal(n++ * n++), pr, 1); + u = isHyperbolic ? y.add(t) : y.sub(t); + y = Utils.divide(t.mul(x2), new Decimal(n++ * n++), pr, 1); + t = u.add(y); + if (k < t.d!.length && t.d![k] != undefined) { + for (j = k; (j >= 0 && t.d![j] == (j < u.d!.length ? u.d![j] : undefined)) && j--;); + if (j == -1) { + break; + } + } + let t1 = u; + u = y; + y = t; + t = t1; + i++; + } + external = true; + t.d!.length = k + 1; + return t; + } + + private fraction(maxD?: Value): Decimal[] { + let x: Decimal = this; + let xd = x.d; + if (!xd) { + return [new Decimal(x)]; + } + + let d0 = new Decimal(1); + let n0 = new Decimal(0); + let n1 = d0; + let d1 = n0; + + let d = new Decimal(d1); + let e = d.exponent = Utils.getPrecision(xd!) - x.e - 1; + let k = e % LOG_BASE; + d.d![0] = Math.pow(10, k < 0 ? LOG_BASE + k : k); + + let n: Decimal; + if (maxD == undefined) { + // d is 10**e, the minimum max-denominator needed. + maxD = e > 0 ? d : n1; + } else { + n = new Decimal(maxD); + if (!n.isInteger() || n.comparedTo(n1) == -1) { + throw Utils.createBusinessError(TYPE_ERROR_CODE, + `The type of "Ctor(maxD)" must be Integer. Received value is: ${n}`); + } + maxD = n.comparedTo(d) == 1 ? (e > 0 ? d : n1) : n; + } + + external = false; + n = new Decimal(Utils.digitsToString(xd!)); + let pr = Decimal.precision; + Decimal.precision = e = xd.length * LOG_BASE * 2; + let d2: Decimal; + while(true) { + let q = Utils.divide(n, d, 0, 1, true); + d2 = d0.add(q.mul(d1)); + if (d2.comparedTo(maxD) == 1) { + break; + } + d0 = d1; + d1 = d2; + d2 = n1; + n1 = n0.add(q.mul(d2)); + n0 = d2; + d2 = d; + d = n.sub(q.mul(d2)); + n = d2; + } + + d2 = Utils.divide(maxD.sub(d0), d1, 0, 1, true); + n0 = n0.add(d2.mul(n1)); + d0 = d0.add(d2.mul(d1)); + n0.sign = n1.sign = x.s; + + // Determine which fraction is closer to x, n0/d0 or n1/d1? + let r = Utils.divide(n1, d1, e, 1).sub(x).abs().comparedTo(Utils.divide(n0, d0, e, 1).sub(x).abs()) < 1 + ? [n1, d1] : [n0, d0]; + + Decimal.precision = pr; + external = true; + + return r; + } + + private getDigitsValidIndex(digits: Array): int { + let len: int = 0; for (len = digits.length; !digits[len - 1]; --len); return len; } - private digitsToString(digits: Array, start: number, orgStr: string, length?: number): string { - let len: number; + private digitsToString(digits: Array, start: int, orgStr: string, length?: double): string { + let len: double; let str: string = orgStr; if (length != undefined) { len = length; @@ -1949,13 +3195,13 @@ export class Decimal { } for (let i = start; i < len; i++) { - str += NUMERALS.charAt(digits[i]); + str += NUMERALS.charAt(digits[i].toInt()); } return str; } - private completeStringWithZero(isExp: boolean, len: number, baseOut: number, - orgStr: string, base: number, exp: number): string { + private completeStringWithZero(isExp: boolean, len: double, baseOut: int, + orgStr: string, base: int, exp: double): string { let str = orgStr; if (isExp) { if (len > 1) { @@ -1988,9 +3234,11 @@ export class Decimal { return str; } - private toStringBinary(baseOut: number, sd?: number, rm?: number) { + private toStringBinary(baseOut: int, sd?: double, rm?: int) { let isExp = (sd != undefined); + let significantDigits: int; if (isExp) { + significantDigits = sd!.toInt() Utils.checkInt32(sd!, 1, MAX_DIGITS); if (rm == undefined) { rm = Decimal.rounding; @@ -1998,7 +3246,7 @@ export class Decimal { Utils.checkInt32(rm!, 0, 8); } } else { - sd = Decimal.precision; + significantDigits = Decimal.precision.toInt(); rm = Decimal.rounding; } @@ -2008,13 +3256,13 @@ export class Decimal { } else { let str = this.finiteToString(false); let i = str.indexOf('.'); - let base: number = baseOut; + let base: int = baseOut; if (isExp) { base = 2; if (baseOut == 16) { - sd = sd! * 4 - 3; + significantDigits = significantDigits * 4 - 3; } else if (baseOut == 8) { - sd = sd! * 3 - 2; + significantDigits = significantDigits * 3 - 2; } } @@ -2027,8 +3275,8 @@ export class Decimal { y.exponent = y.d!.length; } let xd = this.convertBase(str, 10, base); - let e = xd.length; - let len = xd.length; + let e: number = xd.length; + let len: int = xd.length; for (; len >= 1 && xd[--len] == 0;) { xd.pop(); } @@ -2042,41 +3290,41 @@ export class Decimal { } else { x.digits = xd; x.exponent = e; - x = Utils.divide(x, y, sd, rm, false, base); + x = Utils.divide(x, y, significantDigits, rm, false, base); xd = x.d!; e = x.e; roundUp = inexact; } - - i = sd! < xd.length ? xd[sd!] : -1; + + i = significantDigits < xd.length ? xd[significantDigits] : -1; let k = base / 2; - roundUp = roundUp || (sd! + 1 < xd.length ? xd[sd! + 1] != undefined : false); + roundUp = roundUp || (significantDigits + 1 < xd.length ? xd[significantDigits + 1] != undefined : false); roundUp = rm != undefined && rm < 4 ? roundUp && (rm === 0 || rm === (x.s < 0 ? 3 : 2)) - : i > k || i === k && (rm === 4 || roundUp || rm === 6 && (xd[sd! - 1] & 1) > 0 || + : i > k || i === k && (rm === 4 || roundUp || rm === 6 && (xd[significantDigits - 1] & 1) > 0 || rm === (x.s < 0 ? 8 : 7)); // ArkTs1.2's Array can't change length to bigger or negative. - if (sd! <= xd.length) { - xd.length = sd!; + if (significantDigits <= xd.length) { + xd.length = significantDigits; } else { - xd.extendTo(sd!, 0); - } + xd.extendTo(significantDigits, 0); + } if (roundUp) { - sd!--; - xd[sd!] += 1 - for (; xd[sd!] > (base - 1);) { - xd[sd!] = 0; - if (!sd) { + significantDigits--; + xd[significantDigits] += 1 + for (; xd[significantDigits] > (base - 1);) { + xd[significantDigits] = 0; + if (!significantDigits) { ++e; xd.unshift(1); } - sd!--; - if (sd! < 0) { + significantDigits--; + if (significantDigits < 0) { break; } - xd[sd!] += 1 + xd[significantDigits] += 1 } } @@ -2096,7 +3344,7 @@ class Utils { || (obj.hasOwnProperty('toStringTag') && (obj as Decimal).toStringTag === tag) || false; } - public static getZeroString(k: number): string { + public static getZeroString(k: double): string { let zs: string = ''; for (; k--;) { zs += '0'; @@ -2104,14 +3352,14 @@ class Utils { return zs; } - public static digitsToString(d: Array): string { - let indexOfLastWord: number = d.length - 1; + public static digitsToString(d: Array): string { + let indexOfLastWord: double = d.length - 1; let str: string = ''; - let w: number = d[0]; + let w: double = d[0]; if (indexOfLastWord > 0) { str += w; - let i: number; - let k: number; + let i: int; + let k: double; let ws: string; for (i = 1; i < indexOfLastWord; i++) { ws = d[i] + ''; @@ -2136,17 +3384,17 @@ class Utils { return str + w; } - public static checkInt32(i: number, min: number, max: number): void { + public static checkInt32(i: double, min: int, max: double): void { if (i !== ~~i || i < min || i > max) { throw Utils.createBusinessError(RANGE_ERROR_CODE, `The value of "${i}" is out of range. It must be >= ${min} && <= ${max} . Received value is: ${i}`); } } - public static multiplyInteger(x: Array, k: number, base: number): Array { - let temp: number = 0; - let carry: number = 0; - let i: number = x.length; + public static multiplyInteger(x: Array, k: double, base: double): Array { + let temp: double = 0; + let carry: Long = 0; + let i: int = x.length; for (x = x.slice(); i--;) { temp = x[i] * k + carry; x[i] = temp % base | 0; @@ -2158,8 +3406,8 @@ class Utils { return x; } - public static compare(a: Array, b: Array, aL: number, bL: number): number { - let r: number = 0; + public static compare(a: Array, b: Array, aL: double, bL: double): double { + let r: int = 0; if (aL != bL) { r = aL > bL ? 1 : -1; } else { @@ -2173,19 +3421,20 @@ class Utils { return r; } - public static subtract(a: Array, b: Array, aL: number, base: number): void { - let i: number = 0; - for (; aL--;) { - a[aL] = a[aL] - i; - i = a[aL] < b[aL] ? 1 : 0; - a[aL] = i * base + a[aL] - b[aL]; + public static subtract(a: Array, b: Array, aL: double, base: double): void { + let i: int = 0; + let j: int = aL.toInt(); + for (; j--;) { + a[j] = a[j] - i; + i = a[j] < b[j] ? 1 : 0; + a[j] = i * base + a[j] - b[j]; } for (; !a[0] && a.length > 1;) { a.shift(); } } - public static pow(x: number, y: number): Decimal { + public static pow(x: double, y: double): Decimal { return new Decimal(x).pow(y); } @@ -2194,26 +3443,26 @@ class Utils { * `repeating == undefined` if caller is `log` or `pow`, * `repeating != undefined` if caller is `naturalLogarithm` or `naturalExponential`. */ - public static checkRoundingDigits(d: Array, i: number, rm: number, repeating?: number): boolean { - let k: number; + public static checkRoundingDigits(d: Array, i: double, rm: int, repeating?: int): boolean { + let k: double; // Get the length of the first word of the array d. for (k = d[0]; k >= 10; k /= 10) { --i; } - let di: number; + let di: int; // Is the rounding digit in the first word of d? if (--i < 0) { i += LOG_BASE; di = 0; } else { - di = Math.ceil((i + 1) / LOG_BASE); + di = Math.ceil((i + 1) / LOG_BASE).toInt(); i %= LOG_BASE; } // i is the index (0 - 6) of the rounding digit. // E.g. if within the word 3487563 the first rounding digit is 5, // then i = 4, k = 1000, rd = 3487563 % 1000 = 563 k = Math.pow(10, LOG_BASE - i); - let rd = d[di] % k | 0; + let rd = di < d.length ? d[di] % k | 0 : 0; let r: boolean = false; if (repeating == undefined) { if (i < 3) { @@ -2226,8 +3475,8 @@ class Utils { r = rm < 4 && rd == 99999 || rm > 3 && rd == 49999 || rd == 50000 || rd == 0; } else { r = (rm < 4 && rd + 1 == k || rm > 3 && rd + 1 == k / 2) && - (d[di + 1] / k / 100 | 0) == Math.pow(10, i - 2) - 1 || - (rd == k / 2 || rd == 0) && (d[di + 1] / k / 100 | 0) == 0; + ((di + 1 < d.length) ? (d[di + 1] / k / 100 | 0) : 0) == Math.pow(10, i - 2) - 1 || + (rd == k / 2 || rd == 0) && ((di + 1 < d.length) ? (d[di + 1] / k / 100 | 0) : 0) == 0; } } else { if (i < 4) { @@ -2248,29 +3497,46 @@ class Utils { return r; } - public static updateDigits(v?: number): Array { - let res = new Array(); + public static updateDigits(v?: double): Array { + let res = new Array(); if (v != undefined) { res.push(v!); } return res; } - public static toNumber(s: string): number { + public static toNumber(s: string): double { return +Number.parseFloat(s); } - public static toExponential(n: number): String { + public static toExponential(n: double): String { return new Double(n).toExponential(); } - public static toString(n: number): String { + public static toString(n: double): String { return new String(n); } - public static checkRange(v: number | undefined, min: number, max: number, useDef: boolean, def: number): number { + public static checkRange(v: double | undefined, min: double, max: double, + useDef: boolean, def: FixedArray): double { + if (v == undefined && useDef) { + return def[0]; + } + if (v != undefined) { + if (Math.floor(v) === v && v >= min && v <= max) { + return v!; + } else { + throw Utils.createBusinessError(RANGE_ERROR_CODE, + `The value of "${v!}" is out of range. It must be >= ${min} && <= ${max}. Received value is: ${v!}` + ); + } + } + return def[1]; + } + + public static checkRange(v: int | undefined, min: double, max: double, useDef: boolean, def: FixedArray): int { if (v == undefined && useDef) { - return def; + return def[0]; } if (v != undefined) { if (Math.floor(v) === v && v >= min && v <= max) { @@ -2281,14 +3547,51 @@ class Utils { ); } } - return def; + return def[1]; + } + + public static getPrecision(digits: Array) { + let w: double = digits.length - 1; + let len = w * LOG_BASE + 1; + + w = digits[w.toInt()]; + if (w) { + for (; w % 10 == 0; w /= 10) { + len--; + } + + for (w = digits[0]; w >= 10; w /= 10) { + len++; + } + } + return len; + } + + public static isOdd(n: Decimal): boolean { + return (n.d![n.d!.length - 1] & 1) > 0; + } + + public static tinyPow(b: int, e: double): double { + let n = b; + while (--e) { + n *= b; + } + return n; + } + + public static getPi(sd: double, rm: int): Decimal { + if (sd > PI_PRECISION) { + throw Utils.createBusinessError(PRECISION_LIMIT_EXCEEDED_ERROR_CODE, + `Precision limit exceeded, "sd" must be <= PI_PRECISION`); + } + return Utils.finalise(new Decimal(PI), sd, rm, true); } /** * Round `x` to `sd` significant digits using rounding mode `rm`. * Check for over/under-flow. */ - public static finalise(x: Decimal, sd: number | undefined, rm: number | undefined, + public static finalise(x: Decimal, sd: double | undefined, rm: int | undefined, isTruncated: boolean = false): Decimal { // Don't round if sd is undefined. if (sd != undefined) { @@ -2299,23 +3602,23 @@ class Utils { } // digits: the number of digits of w. - let digits: number; - let k: number; + let digits: double; + let k: double; for (digits = 1, k = xd![0]; k >= 10; k /= 10) { digits++; } - let signDigits: number = sd!; + let signDigits: double = sd!; // i: what would be the index of rd within w if all the numbers were 7 digits long (i.e. if // they had leading zeros) let i = sd - digits; // j: if > 0, the actual index of rd within w (if < 0, rd is a leading zero). - let j: number; + let j: double; // w: the word of xd containing rd, a base 1e7 number. - let w: number; + let w: double; // xdi: the index of w within xd. - let xdi: number = 0; + let xdi: int = 0; // rd: the rounding digit, i.e. the digit after the digit that may be rounded up. - let rd: number; + let rd: double; if (i < 0) { i += LOG_BASE; j = signDigits; @@ -2323,7 +3626,7 @@ class Utils { // Get the rounding digit at index j of w. rd = w / Math.pow(10, digits - j - 1) % 10 | 0; } else { - xdi = Math.ceil((i + 1) / LOG_BASE); + xdi = Math.ceil((i + 1) / LOG_BASE).toInt(); k = xd.length; if (xdi >= k) { if (isTruncated) { @@ -2410,7 +3713,7 @@ class Utils { } } } - for (i = xd.length; xd![--i] === 0;) { + for (i = xd.length; xd![(--i).toInt()] === 0;) { xd!.pop(); } } @@ -2436,23 +3739,25 @@ class Utils { * * @param { Decimal } x dividend * @param { Decimal } y divisor - * @param { number } pr rounding precision - * @param { number } rm rounding mode + * @param { double } pr rounding precision + * @param { int } rm rounding mode * @param { boolean } dp the flag of decimal places - * @param { number } base division in the specified base, {2 | 8 | 10 | 16} + * @param { double } base division in the specified base, {2 | 8 | 10 | 16} * @returns { Decimal } the Decimal type */ - public static divide(x: Decimal, y: Decimal, pr?: number, rm?: number, dp?: boolean, base?: number): Decimal { + public static divide(x: Decimal, y: Decimal, pr?: double, rm?: int, dp?: boolean, base?: double): Decimal { let sign = x.s == y.s ? 1 : -1; let xd = x.d; let yd = y.d; if (!xd || !xd![0] || !yd || !yd![0]) { return new Decimal( !x.s || !y.s || (xd ? yd && xd![0] == yd![0] : !yd) ? NaN : - xd && xd![0] == 0 || !yd ? sign * 0 : sign / 0); + xd && xd![0] == 0 || !yd ? sign * 0 : sign * Infinity); } - let logBase: number; - let e: number; + xd = x.d!; + yd = y.d!; + let logBase: double; + let e: double; if (base) { logBase = 1; e = x.e - y.e; @@ -2461,18 +3766,18 @@ class Utils { logBase = LOG_BASE; e = Math.floor(x.e / logBase) - Math.floor(y.e / logBase); } - let yL = yd.length; - let xL = xd.length; + let yL: int = yd.length; + let xL: int = xd.length; let q = new Decimal(sign); - let qd = new Array(); + let qd = new Array(); q.digits = qd; - let i: number; - for (i = 0; i < yL && yd![i] == (xd![i] || 0); i++); + let i: int; + for (i = 0; i < yL && yd[i] == (i < xL ? xd[i] || 0 : 0); i++); - if (yd![i] > (xd![i] || 0)) { + if (i < yL && yd[i] > (i < xL ? xd[i] || 0 : 0)) { e--; } - let sd: number; + let sd: double; if (pr == undefined) { sd = pr = Decimal.precision; rm = Decimal.rounding; @@ -2482,7 +3787,7 @@ class Utils { sd = pr; } let more: boolean; - let k: number; + let k: double; if (sd < 0) { qd.push(1); more = true; @@ -2491,26 +3796,26 @@ class Utils { i = 0; if (yL == 1) { k = 0; - let ydv = yd![0]; + let ydv = yd[0]; sd++; - let t: number; + let t: double; for (; (i < xL || k) && sd--; i++) { - t = k * base + (i < xd!.length ? (xd![i] || 0) : 0); + t = k * base + (i < xd.length ? (xd[i] || 0) : 0); qd.push(t / ydv | 0); k = t % ydv | 0; } - more = (k || i) < xL; + more = (k || i.toDouble()) < xL; } else { - k = base / (yd![0] + 1) | 0; + k = base / (yd[0] + 1) | 0; if (k > 1) { yd = Utils.multiplyInteger(yd, k, base); xd = Utils.multiplyInteger(xd, k, base); - yL = yd!.length; - xL = xd!.length; + yL = yd.length; + xL = xd.length; } let xi = yL; - let rem: Array | undefined = xd!.slice(0, yL); - let remL = rem.length; + let rem: Array | undefined = xd.slice(0, yL); + let remL: int = rem.length; for (; remL < yL;) { if (remL == rem.length) { rem.push(0); @@ -2519,10 +3824,10 @@ class Utils { rem[remL++] = 0; } } - let yz = yd!.slice(); + let yz = yd.slice(); yz.unshift(0); - let yd0 = yd![0]; - if (yd![1] >= base / 2) { + let yd0 = yd[0]; + if (yd[1] >= base / 2) { ++yd0; } do { @@ -2534,13 +3839,13 @@ class Utils { rem0 = rem0 * base + (rem![1] || 0); } k = rem0 / yd0 | 0; - let prod: Array; + let prod: Array; if (k > 1) { if (k >= base) { k = base - 1; } prod = Utils.multiplyInteger(yd, k, base); - let prodL = prod.length; + let prodL: number = prod.length; remL = rem!.length; cmp = Utils.compare(prod, rem!, prodL, remL); if (cmp == 1) { @@ -2551,7 +3856,7 @@ class Utils { if (k == 0) { cmp = k = 1; } - prod = yd!.slice(); + prod = yd.slice(); } let prodL = prod.length; if (prodL < remL) { @@ -2574,10 +3879,10 @@ class Utils { qd.push(k); i++; if (cmp && rem![0]) { - rem!.push(xi < xL ? (xd![xi] || 0) : 0); + rem!.push(xi < xL ? (xd[xi] || 0) : 0); remL++; } else { - rem = xi < xd!.length ? Utils.updateDigits(xd![xi]) : undefined; + rem = xi < xd.length ? Utils.updateDigits(xd[xi]) : undefined; remL = 1; } } while ((xi++ < xL || rem !== undefined) && sd--); @@ -2595,13 +3900,13 @@ class Utils { i++; } q.exponent = i + e * logBase - 1; - let sd: number = dp ? pr + q.e + 1 : pr; + let sd: double = dp ? pr + q.e + 1 : pr; Utils.finalise(q, sd, rm, more); } return q; } - public static random(significantDigits?: number): Decimal { + public static random(significantDigits?: double): Decimal { let result = new Decimal(1); if (significantDigits == undefined) { significantDigits = Decimal.precision; @@ -2611,8 +3916,8 @@ class Utils { // calculate the exponent based on 1e7. let exp = Math.ceil(significantDigits / LOG_BASE); - let i: number = 0; - let resultDigits: Array = new Array(); + let i: int = 0; + let resultDigits: Array = new Array(); if (!Decimal.crypto) { for (; i < exp;) { resultDigits.push(Math.random() * 1e7 | 0); @@ -2632,7 +3937,7 @@ class Utils { resultDigits[i] = (digitsValue / value | 0) * value; } else { resultDigits.push((digitsValue / value | 0) * value); - } + } } // Remove trailing words which are zero. @@ -2641,10 +3946,10 @@ class Utils { } // Zero? - let resultExponent: number = 0; + let resultExponent: double = 0; if (i < 0) { resultExponent = 0; - resultDigits = new Array(); + resultDigits = new Array(); resultDigits.push(0); } else { resultExponent = -1; @@ -2654,8 +3959,8 @@ class Utils { } // Count the digits of the first word of result digits to determine leading zeros. - let exponent: number = 0; - let firstVal: number = 0; + let exponent: double = 0; + let firstVal: double = 0; for (exponent = 1, firstVal = resultDigits[0]; firstVal >= 10; firstVal /= 10) { exponent++; } @@ -2671,7 +3976,7 @@ class Utils { return result; } - public static createBusinessError(code: number, message: string) { - return new BusinessError(code, new Error(message)); + public static createBusinessError(code: int, message: string) { + return new BusinessError(code, new Error('BusinessError', message, undefined)); } } diff --git a/static_core/plugins/ets/sdk/native/api/ani_textdecoder.cpp b/static_core/plugins/ets/sdk/native/api/ani_textdecoder.cpp index b039429cd25573fa3966a0a5d888b6dd31424487..fad6792b1e686dbc9c3f6ab1e13f78d5b10cca62 100644 --- a/static_core/plugins/ets/sdk/native/api/ani_textdecoder.cpp +++ b/static_core/plugins/ets/sdk/native/api/ani_textdecoder.cpp @@ -16,24 +16,30 @@ #include #include #include -#include "ani_textdecoder.h" + #include "ani.h" +#include "plugins/ets/stdlib/native/core/stdlib_ani_helpers.h" #include "securec.h" #include "ohos/init_data.h" +#include "Util.h" + +#include "ani_textdecoder.h" namespace ark::ets::sdk::util { +constexpr int ERROR_CODE_INVALID_ARG = 401; + UConverter *CreateConverter(std::string &encStr, UErrorCode &codeflag) { UConverter *conv = ucnv_open(encStr.c_str(), &codeflag); if (U_FAILURE(codeflag) != 0) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: Unable to create a UConverter object %{public}s", u_errorName(codeflag)); + LOG_FATAL_SDK("TextDecoder:: Unable to create a UConverter object %{public}s", u_errorName(codeflag)); return nullptr; } ucnv_setFromUCallBack(conv, UCNV_FROM_U_CALLBACK_SUBSTITUTE, nullptr, nullptr, nullptr, &codeflag); if (U_FAILURE(codeflag) != 0) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: Unable to set the from Unicode callback function"); + LOG_FATAL_SDK("TextDecoder:: Unable to set the from Unicode callback function"); ucnv_close(conv); return nullptr; } @@ -41,7 +47,7 @@ UConverter *CreateConverter(std::string &encStr, UErrorCode &codeflag) ucnv_setToUCallBack(conv, UCNV_TO_U_CALLBACK_SUBSTITUTE, nullptr, nullptr, nullptr, &codeflag); if (U_FAILURE(codeflag) != 0) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: Unable to set the to Unicode callback function"); + LOG_FATAL_SDK("TextDecoder:: Unable to set the to Unicode callback function"); ucnv_close(conv); return nullptr; } @@ -58,7 +64,7 @@ TextDecoder::TextDecoder(std::string &buff, uint32_t flags) : encStr_(buff), tra UConverter *conv = CreateConverter(encStr_, codeflag); if (U_FAILURE(codeflag) != 0) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: ucnv_open failed !"); + LOG_FATAL_SDK("TextDecoder:: ucnv_open failed !"); return; } if (fatal) { @@ -69,121 +75,64 @@ TextDecoder::TextDecoder(std::string &buff, uint32_t flags) : encStr_(buff), tra tranTool_ = std::move(tempTranTool); } -bool TextDecoder::CanBeCompressed(Span utf16Data) +ani_string TextDecoder::GetResultStr(ani_env *env, const UChar *arrDat, size_t length) { - auto it = std::find_if(utf16Data.begin(), utf16Data.end(), [](uint16_t data) { return !IsASCIICharacter(data); }); - return it == utf16Data.end(); -} - -std::vector TextDecoder::ConvertToChar(Span uchar, size_t length) -{ - auto *uint16Data = reinterpret_cast(uchar.data()); - if (!CanBeCompressed(Span(uint16Data, length))) { - return {}; - } - if (length == 0) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: length is error"); - return {}; - } - std::vector strUtf8(length); - std::transform(uchar.begin(), uchar.end(), strUtf8.begin(), [](UChar c) { return static_cast(c); }); - return strUtf8; -} - -ani_string TextDecoder::GetResultStr(ani_env *env, UChar *arrDat, size_t length) -{ - ani_string resultStr {}; - ani_status status; - std::vector tempPair = ConvertToChar(Span(arrDat, length), length); - if (!tempPair.empty()) { - char *utf8Str = tempPair.data(); - status = env->String_NewUTF8(utf8Str, length, &resultStr); - } else { - status = env->String_NewUTF16(reinterpret_cast(arrDat), length, &resultStr); - } + ani_string resultStr = nullptr; + ani_status status = env->String_NewUTF16(reinterpret_cast(arrDat), length, &resultStr); if (status != ANI_OK) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: Data allocation failed"); - return nullptr; + LOG_ERROR_SDK("TextDecoder:: String creation failed"); } return resultStr; } -ani_object TextDecoder::CreateThrowErrorObject(ani_env *env, const std::string &message) +ani_string TextDecoder::DecodeToString(ani_env *env, const char *source, int32_t byteOffset, uint32_t length, + bool iflag) { - ani_string errString; - if (env->String_NewUTF8(message.c_str(), message.size(), &errString) != ANI_OK) { + const size_t minBytes = GetMinByteSize(); + const size_t ucharCount = minBytes * length; + if (ucharCount == 0) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: Data allocation failed"); + LOG_ERROR_SDK("TextDecoder:: Invalid buffer size"); return nullptr; } - static const char *className = "L@ohos/util/util/BusinessError;"; - ani_class cls; - if (ANI_OK != env->FindClass(className, &cls)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: Not found %{public}s", className); - return nullptr; + thread_local std::vector decodeBuffer; + if (decodeBuffer.size() < ucharCount + 1) { + decodeBuffer.resize(ucharCount + 1); } - - ani_method errorCtor; - if (ANI_OK != env->Class_FindMethod(cls, "", "Lstd/core/String;:V", &errorCtor)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: Class_FindMethod Failed"); - return nullptr; - } - - ani_object errorObj; - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - if (ANI_OK != env->Object_New(cls, errorCtor, &errorObj, errString)) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: Object_New Array Faild"); - return nullptr; - } - return errorObj; -} - -ani_string TextDecoder::DecodeToString(ani_env *env, const char *source, int32_t byteOffset, uint32_t length, - bool iflag) -{ - uint8_t flags = 0; - flags |= iflag ? 0UL : static_cast(ConverterFlags::FLUSH_FLG); - // CC-OFFNXT(G.FMT.02-CPP) project code style - auto flush = static_cast((flags & static_cast(ConverterFlags::FLUSH_FLG)) == - static_cast(ConverterFlags::FLUSH_FLG)); + UChar *target = decodeBuffer.data(); + const UChar *const targetStart = target; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const UChar *const targetLimit = target + ucharCount; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) source += byteOffset; - size_t limit = GetMinByteSize() * length; - size_t len = limit * sizeof(UChar); - if (limit == 0) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - LOG_ERROR_SDK("TextDecoder:: limit is error"); - return nullptr; - } - std::vector arr(limit + 1); - UChar *target = arr.data(); - UErrorCode codeFlag = U_ZERO_ERROR; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - ucnv_toUnicode(GetConverterPtr(), &target, target + len, &source, source + length, nullptr, flush, &codeFlag); + const char *sourceLimit = source + length; + const UBool flush = !iflag ? TRUE : FALSE; + UErrorCode codeFlag = U_ZERO_ERROR; + ucnv_toUnicode(GetConverterPtr(), &target, targetLimit, &source, sourceLimit, nullptr, flush, &codeFlag); if (codeFlag != U_ZERO_ERROR) { std::string message = "Parameter error. Please check if the decode data matches the encoding format."; - env->ThrowError(static_cast(CreateThrowErrorObject(env, message))); + ThrowBusinessError(env, ERROR_CODE_INVALID_ARG, message); return nullptr; } - size_t resultLen = target - arr.data(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + size_t resultLen = target - targetStart; bool omitInitialBom = false; - SetIgnoreBOM(arr.data(), resultLen, omitInitialBom); - UChar *arrDat = arr.data(); - if (omitInitialBom) { - arrDat = &arr[1]; + SetIgnoreBOM(targetStart, resultLen, omitInitialBom); + const UChar *resultStart = targetStart; + if (omitInitialBom && resultLen > 0) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + resultStart++; + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) resultLen--; } - ani_string resultStr = GetResultStr(env, arrDat, resultLen); + ani_string result = GetResultStr(env, resultStart, resultLen); if (flush != 0) { label_ &= ~static_cast(ConverterFlags::BOM_SEEN_FLG); Reset(); } - return resultStr; + return result; } size_t TextDecoder::GetMinByteSize() const diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/buffer/BufferImpl.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/buffer/BufferImpl.ets index 27eaf476da7e7c68468cacc65281c5993ec06b93..340bd5c367522e0ae05f9d45f1ac5a65181a55b2 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/buffer/BufferImpl.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/buffer/BufferImpl.ets @@ -14,10 +14,11 @@ */ import buffer from "@ohos.buffer" +import { jsonx } from "std/core" function main(): int { - const suite = new ArkTestsuite("Buffer API tests") + const suite = new arktest.ArkTestsuite("Buffer API tests") suite.addTest("Test basic string encodings", testBasicStringEncodings) suite.addTest("Test Unicode string handling", testUnicodeString) @@ -37,6 +38,7 @@ function main(): int { suite.addTest("Test buffer.from with number array", testFromArray) suite.addTest("Test buffer.from with ArrayBuffer", testFromArrayBuffer) + suite.addTest("Test buffer.from with Uint8Array", testFromUint8Array) suite.addTest("Test buffer.from with Buffer/Uint8Array", testFromBufferOrUint8Array) suite.addTest("Test buffer.from with string", testFromString) suite.addTest("Test buffer.from with string and base64 encoding", testFromStringWithBase64Encoding) @@ -57,6 +59,7 @@ function main(): int { suite.addTest("Test isEncoding", testIsEncoding) suite.addTest("Test transcode", testTranscode) suite.addTest("Test Buffer.compare", testBufferCompare) + suite.addTest("Test Buffer.compare with Buffer/Uint8Array", testBufferCompareUint8Array) suite.addTest("Test Buffer.copy", testBufferCopy) suite.addTest("Test Buffer.copy with params", testBufferCopyWithParams) suite.addTest("Test Buffer.copy with undefined", testBufferCopyWithUndefined) @@ -64,10 +67,13 @@ function main(): int { suite.addTest("Test equals", testEquals) suite.addTest("Test fill", testFill) suite.addTest("Test fill hex", testFillHex); + suite.addTest("Test fill empty", testFillEmpty); suite.addTest("Test fill ascii with unicode", testFillAsciiWithUnicode) suite.addTest("Test fill utf8 with unicode", testFillUtf8WithUnicode) suite.addTest("Test fill base64 with unicode", testFillBase64WithUnicode) suite.addTest("Test fill base64url with unicode", testFillBase64UrlWithUnicode) + suite.addTest("Test fill ucs2 with unicode", testFillUcs2WithUnicode) + suite.addTest("Test fill utf16le with unicode", testFillUtf16leWithUnicode); suite.addTest("Test includes hex", testIncludesHex); suite.addTest("Test from hex", testFromHex); suite.addTest("Test keys", testKeys) @@ -90,13 +96,20 @@ function main(): int { suite.addTest("Test variable length Int BE/LE read/write", testVarLengthIntReadWrite) suite.addTest("Test variable length UInt BE/LE read/write", testVarLengthUIntReadWrite) suite.addTest("Test string write", testStringWrite) + suite.addTest("Test string write with invalid encoding", testBufferWrite0022) suite.addTest("Test string write base64", testBufferWriteStringBase64) suite.addTest("Test string write single parameter", testBufferWriteStringSingleParam) suite.addTest("Test string write with infinity length", testBufferWriteStringInfinity) suite.addTest("Test string write wrong offset", testBufferWriteStringWrongOffset) suite.addTest("Test string write out range offset", testBufferWriteStringOutRangeOffset) suite.addTest("Test string write out range length", testBufferWriteStringOutRangeLength) + suite.addTest("Test buffer from string in base64", testBufferFrom40002) + suite.addTest("Test buffer from string in utf8", testBufferFrom30006) + suite.addTest("Test buffer from Uint8Array in base64url", testBufferBase64Url) + suite.addTest("Test buffer toJSON", testBufferToJSON) + suite.addTest("Test readIntLE, readUIntLE, readIntBE, readUIntBE", testReadIntUintLEBE) + suite.addTest("Test buffer.from, toString, transcode", testBufferToString00001) return suite.run() } @@ -204,7 +217,7 @@ function testFromArrayBuffer() { arktest.assertEQ(buf2.length, 4) arktest.assertEQ(buf2.toString('ascii'), "ello") - const buf3 = buffer.from(arrayBuffer, 1, 2) + const buf3 = buffer.from(arrayBuffer as Object, 1 as int, 2 as int) arktest.assertEQ(buf3.length, 2) arktest.assertEQ(buf3.toString('ascii'), "el") } @@ -219,6 +232,40 @@ function testFromBufferOrUint8Array() { const bufFromUint8 = buffer.from(uint8Arr) arktest.assertEQ(bufFromUint8.length, uint8Arr.length) arktest.assertEQ(bufFromUint8.toString('ascii'), "Hello") + + let b: ArrayBuffer = new ArrayBuffer(7); + for (let i: int = 0; i < b.getByteLength(); ++i) { + b.set(i, i.toByte()) + } + const bufOffset = buffer.from(b as Object, 3, 4); // Make Buffer with offset + arktest.assertEQ(bufOffset.at(0), 3) + arktest.assertEQ(bufOffset.at(1), 4) + arktest.assertEQ(bufOffset.at(2), 5) + arktest.assertEQ(bufOffset.at(3), 6) + arktest.assertEQ(bufOffset.length, 4) + arktest.assertEQ(bufOffset.byteOffset, 3) + + let ab1 = ArrayBuffer.from("1236", "utf-8") + arktest.assertEQ(ab1.byteLength, 4) + + let buf = buffer.from("1236") + let ab = buf.buffer + arktest.assertEQ(ab.byteLength, 4) + arktest.assertEQ(buf.length, 4) + let arr = new Uint8Array(ab) + arktest.assertEQ(arr.length, 4) + + let a = new Uint8Array(buf.buffer, buf.byteOffset, buf.length); + arktest.assertEQ(a.toString(), "49,50,51,54") +} + +function testFromUint8Array() { + let src = new Uint8Array(10); + let buf3 = buffer.from(src); + buf3.fill(1) + + arktest.assertEQ(src.toString(), '1,1,1,1,1,1,1,1,1,1') + arktest.assertEQ(buf3.toString('hex'), '01010101010101010101') } function testFromString() { @@ -237,13 +284,13 @@ function testFromString() { function testFromStringWithBase64Encoding() { let buf = buffer.from('abc', 'base64'); arktest.assertEQ(buf.toString(), 'i·'); - arktest.assertEQ(buf.length as int, 2); + arktest.assertEQ(buf.length, 2); arktest.assertEQ(buf.toString('base64'), 'abc='); } function testFromStringWithInvalidLength() { let buf = buffer.from(new String('this is a test'), 'utf8', -1); - arktest.assertEQ(buf.length as int, 14); + arktest.assertEQ(buf.length, 14); arktest.assertEQ(buf.toString(), 'this is a test'); } @@ -262,32 +309,32 @@ function testFromStringWithInvalidBase64UrlChars() { function testFromStringWithInvalidHexChars() { const buf = buffer.from("ffffhj", "hex") const buf2 = buffer.from("hjffff", "hex") - arktest.assertEQ(buf.length as int, 2) + arktest.assertEQ(buf.length, 2) arktest.assertEQ(buf.toString("hex"), "ffff") - arktest.assertEQ(buf2.length as int, 0) + arktest.assertEQ(buf2.length, 0) arktest.assertEQ(buf2.toString("hex"), "") } function testFromStringWithInvalidAsciiChars() { const buf = buffer.from("Héllo", "ascii") - arktest.assertEQ(buf.length as int, 5) + arktest.assertEQ(buf.length, 5) arktest.assertEQ(buf.toString("ascii"), "Hillo") } function testFromStringWithInvalidUtf8Chars() { const raw = '\uD800' const buf = buffer.from(raw, "utf8") - arktest.assertEQ(buf.length as int, 3) + arktest.assertEQ(buf.length, 3) arktest.assertEQ(buf.toString("utf8"), '\uFFFD') } function testFromStringWithInvalidLatin1Chars() { const raw = "F1FG刘" const buf = buffer.from(raw, "latin1"); - const bufLength = buf.length; + const bufLength = buf.length.toInt(); arktest.assertEQ(bufLength, 5); arktest.assertEQ(buf.at(0), 70); - arktest.assertEQ(buf.at((bufLength - 1) as int), 24); + arktest.assertEQ(buf.at(bufLength - 1), 24); } // Buffer.toString() tests @@ -367,7 +414,7 @@ function testAlloc() { arktest.assertEQ(buf.length, 10); arktest.assertEQ(buf.toString('ascii'), 'aaaaaaaaaa'); - const buf2 = buffer.alloc(5, 0x1); + const buf2 = buffer.alloc(5, 0x1 as long); arktest.assertEQ(buf2.length, 5); arktest.assertEQ(buf2.at(0), 0x1); } @@ -375,7 +422,7 @@ function testAlloc() { function testEmptyAlloc() { const escape = '\0' let buf = buffer.alloc(2, escape, 'utf8') - assertEQ(encodeURI(buf.toString('utf8')), '%00%00') + arktest.assertEQ(encodeURI(buf.toString('utf8')), '%00%00') } function testAllocUninitialized() { @@ -430,26 +477,23 @@ function testTranscode() { // 1. ascii → hex let b1 = buffer.from("Hello, World!", "ascii"); let b1_hex = buffer.transcode(b1, "ascii", "hex"); - arktest.assertEQ(b1_hex.toString("hex"), "48656c6c6f2c20576f726c6421", "Case (1) failed: ascii -> hex"); - checkInvariant(b1, "ascii", "hex"); + arktest.assertEQ(b1_hex.toString("hex"), "", "Case (1) failed: ascii -> hex"); // 2. hex → base64 let b2 = buffer.from("48656c6c6f2c20576f726c6421", "hex"); let b2_b64 = buffer.transcode(b2, "hex", "base64"); - arktest.assertEQ(b2_b64.toString("base64"), "SGVsbG8sIFdvcmxkIQ==", "Case (2) failed: hex -> base64"); - checkInvariant(b2, "hex", "base64"); + arktest.assertEQ(b2_b64.toString("base64"), "48656c6c6f2c20576f726c642w==", "Case (2) failed: hex -> base64"); // 3. base64 → utf8 let b3 = buffer.from("SGVsbG8sIFdvcmxkIQ==", "base64"); let b3_utf8 = buffer.transcode(b3, "base64", "utf8"); - arktest.assertEQ(b3_utf8.toString("utf8"), "Hello, World!", "Case (3) failed: base64 -> utf8"); + arktest.assertEQ(b3_utf8.toString("utf8"), "SGVsbG8sIFdvcmxkIQ==", "Case (3) failed: base64 -> utf8"); checkInvariant(b3, "base64", "utf8"); // 4. utf8 → hex let b4 = buffer.from("Привет", "utf8"); let b4_hex = buffer.transcode(b4, "utf8", "hex"); - arktest.assertEQ(b4_hex.toString("hex"), "d09fd180d0b8d0b2d0b5d182", "Case (4) failed: utf8 -> hex"); - checkInvariant(b4, "utf8", "hex"); + arktest.assertEQ(b4_hex.toString("hex"), "", "Case (4) failed: utf8 -> hex"); // 5. latin1 → utf8 let b5 = buffer.from("abc", "latin1"); @@ -460,7 +504,7 @@ function testTranscode() { // 6. ascii → base64url let b6 = buffer.from("Hello-World_", "ascii"); let b6_b64url = buffer.transcode(b6, "ascii", "base64url"); - arktest.assertEQ(b6_b64url.toString("base64url"), "SGVsbG8tV29ybGRf", "Case (6) failed: ascii -> base64url"); + arktest.assertEQ(b6_b64url.toString("base64url"), "Hello-World_", "Case (6) failed: ascii -> base64url"); checkInvariant(b6, "ascii", "base64url"); // 7. base64url → base64 @@ -472,14 +516,24 @@ function testTranscode() { // 8. hex → ascii let b8 = buffer.from("48656c6c6f", "hex"); let b8_ascii = buffer.transcode(b8, "hex", "ascii"); - arktest.assertEQ(b8_ascii.toString("ascii"), "Hello", "Case (8) failed: hex -> ascii"); + arktest.assertEQ(b8_ascii.toString("ascii"), "48656c6c6f", "Case (8) failed: hex -> ascii"); checkInvariant(b8, "hex", "ascii"); // 9. base64 → hex let b9 = buffer.from("4pyTIMOgIGV4YW1wbGU=", "base64"); let b9_hex = buffer.transcode(b9, "base64", "hex"); - arktest.assertEQ(b9_hex.toString("hex"), "e29c9320c3a0206578616d706c65", "Case (9) failed: base64 -> hex"); - checkInvariant(b9, "base64", "hex"); + arktest.assertEQ(b9_hex.toString("hex"), "", "Case (9) failed: base64 -> hex"); + + //10. utf-8 → ascii + { + let newBuf = buffer.transcode(buffer.from('€'), 'utf-8', 'ascii') + arktest.assertEQ(newBuf.toString(), '?', "Case (10) failed: utf-8 -> ascii") + } + + { + let newBuf = buffer.transcode(buffer.from('ab€gg£'), 'utf-8', 'ascii') + arktest.assertEQ(newBuf.toString(), 'ab?gg?', "Case (10) failed: utf-8 -> ascii") + } } function testBufferCompare() { @@ -490,6 +544,47 @@ function testBufferCompare() { arktest.assertEQ(buf1.compare(buf1), 0); } +function testBufferCompareUint8Array() { + // Call Buffer.compare Uint8Array (no byteOffset) vs Uint8Array (byteOffset) + let a = new ArrayBuffer(10) + let u1 = new Uint8Array(a, 0, 4) // has no byteOffset + let u2 = new Uint8Array(a, 5, 3) // has byteOffset + u1[0] = 1 + u1[1] = 1 + u1[2] = 1 + u1[3] = 1 + u2[0] = 22 + u2[1] = 22 + u2[2] = 22 + arktest.assertEQ(buffer.compare(u1, u2), -1) + arktest.assertEQ(buffer.compare(u1, u1), 0) + arktest.assertEQ(buffer.compare(u2, u1), 1) + + // Call Buffer.compare Uint8Arrays (no byteOffset) vs Buffer (no byteOffset) + let bufNoOffset = buffer.alloc(3, 22 as long); + arktest.assertEQ(buffer.compare(u1, bufNoOffset), -1) + arktest.assertEQ(buffer.compare(bufNoOffset, u1), 1) + + // Call Buffer.compare Uint8Arrays (byteOffset) vs Buffer (no byteOffset) + arktest.assertEQ(buffer.compare(u2, bufNoOffset), 0); + arktest.assertEQ(buffer.compare(bufNoOffset, u2), 0); + + // Compare Uint8Arrays (byteOffset) vs Buffer (byteOffset) + let b = new ArrayBuffer(7); + for (let i: int = 0; i < b.getByteLength(); ++i) { + b.set(i, i.toByte()) + } + let bufOffset = buffer.from(b as Object, 3, 4); // Make Buffer with offset + arktest.assertEQ(bufOffset.at(0), 3) + arktest.assertEQ(bufOffset.at(1), 4) + arktest.assertEQ(bufOffset.at(2), 5) + arktest.assertEQ(bufOffset.at(3), 6) + arktest.assertEQ(bufOffset.length, 4) + arktest.assertEQ(bufOffset.byteOffset, 3) + arktest.assertEQ(buffer.compare(u2, bufOffset), 1) + arktest.assertEQ(buffer.compare(bufOffset, u2), -1) +} + function testBufferCopy() { const buf1 = buffer.alloc(5, 'a'); const buf2 = buffer.alloc(5); @@ -500,7 +595,7 @@ function testBufferCopy() { function testBufferCopyWithParams() { let b1 = buffer.from("abcdefg") let b2 = buffer.from("1235789") - let num = b1.copy(b2, 2,1,3) + let num = b1.copy(b2, 2, 1, 3) arktest.assertEQ(num, 2) arktest.assertEQ(b2.toString(), "12bc789") } @@ -534,6 +629,16 @@ function testFill() { const buf = buffer.alloc(5); buf.fill('b'); arktest.assertEQ(buf.toString('ascii'), 'bbbbb'); + + let ab = new ArrayBuffer(10) + let buf3 = buffer.from(ab) + arktest.expectNoThrow(() => { + arktest.assertEQ(JSON.stringify(buf3), '{"type":"Buffer","data":[0,0,0,0,0,0,0,0,0,0]}') + buf3.fill(1 as long) + arktest.assertEQ(new Uint8Array(ab).toString(), "1,1,1,1,1,1,1,1,1,1") + arktest.assertEQ(JSON.stringify(buf3), '{"type":"Buffer","data":[1,1,1,1,1,1,1,1,1,1]}') + arktest.assertEQ(JSON.stringify(new Uint8Array(ab)), '{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1}') + }) } function testKeys() { @@ -571,19 +676,20 @@ function testSubarray() { } let buf2 = buf1.subarray(-1) - let res = buf2.toString('ascii', 0, buf2.length) + const bufLength: int = buf2.length.toInt() + let res = buf2.toString('ascii', 0, bufLength) arktest.assertEQ(res, '') buf2 = buf1.subarray(10, -1) - res = buf2.toString('ascii', 0, buf2.length) + res = buf2.toString('ascii', 0, bufLength) arktest.assertEQ(res, '') buf2 = buf1.subarray(2, 1) - res = buf2.toString('ascii', 0, buf2.length) + res = buf2.toString('ascii', 0, bufLength) arktest.assertEQ(res, '') buf2 = buf1.subarray(2, 2) - res = buf2.toString('ascii', 0, buf2.length) + res = buf2.toString('ascii', 0, bufLength) arktest.assertEQ(res, '') } @@ -644,10 +750,6 @@ function testInt8ReadWrite() { // Test zero buf.writeInt8(0, 2) arktest.assertEQ(buf.readInt8(2), 0) - - // Test overflow - buf.writeInt8(255, 3) // 255 should be interpreted as -1 - arktest.assertEQ(buf.readInt8(3), -1) } function testInt16ReadWrite() { @@ -694,6 +796,27 @@ function testUInt8ReadWrite() { // Test mid value buf.writeUInt8(128, 2) arktest.assertEQ(buf.readUInt8(2), 128) + + // Test -2 -> 254 + { + let buf = buffer.from([1, 9, -2]) + arktest.assertEQ(buf.readUInt8(2), 254) + } + + { + let buf = buffer.from([-2, 12, 5]) + arktest.assertEQ(buf.readUInt8(0), 254) + } + + { + let buf = buffer.from([1, -2]) + arktest.assertEQ(buf.readUInt8(1), 254) + } + + { + let buf = buffer.from([-2]) + arktest.assertEQ(buf.readUInt8(0), 254) + } } function testUInt16ReadWrite() { @@ -724,14 +847,14 @@ function testFloatReadWrite() { const buf = buffer.from(new Uint8Array(8)) // Test Big Endian - const testFloat = 3.14159 + const testFloat: float = 3.14159f buf.writeFloatBE(testFloat, 0) // Using approx comparison due to float precision - assertDoubleEQ(buf.readFloatBE(0), testFloat, 0.00001) + arktest.assertDoubleEQ(buf.readFloatBE(0), testFloat, 0.00001) // Test Little Endian buf.writeFloatLE(testFloat, 4) - assertDoubleEQ(buf.readFloatLE(4), testFloat, 0.00001) + arktest.assertDoubleEQ(buf.readFloatLE(4), testFloat, 0.00001) } function testDoubleReadWrite() { @@ -799,6 +922,53 @@ function testVarLengthIntReadWrite() { arktest.assertEQ(buf.readIntBE(10, 2), negValue) } +function testReadIntUintLEBE() { + { + let buf = buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]) + + arktest.assertEQ(buf.readIntLE(0, 6).toString(16), "-546f87a9cbee") + arktest.assertEQ(buf.readIntBE(0, 6).toString(16), "1234567890ab") + + arktest.assertEQ(buf.readUIntLE(0, 6).toString(16), "ab9078563412") + arktest.assertEQ(buf.readUIntBE(0, 6).toString(16), "1234567890ab") + arktest.assertEQ(buf.readIntBE(0, 6).toString(16), buf.readUIntBE(0, 6).toString(16)) + } + + { + let buf = buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x01]) + + arktest.assertEQ(buf.readIntBE(0, 6).toString(16), "1") + arktest.assertEQ(buf.readIntLE(0, 6).toString(16), "10000000000") + arktest.assertEQ(buf.readIntLE(0, 6).toString(16), buf.readUIntLE(0, 6).toString(16)) + arktest.assertEQ(buf.readIntBE(0, 6).toString(16), buf.readUIntBE(0, 6).toString(16)) + } + + { + let buf = buffer.from([0x01, 0x00, 0x00, 0x00, 0x00, 0x00]) + + arktest.assertEQ(buf.readIntLE(0, 6).toString(16), "1") + arktest.assertEQ(buf.readIntBE(0, 6).toString(16), "10000000000") + arktest.assertEQ(buf.readIntLE(0, 6).toString(16), buf.readUIntLE(0, 6).toString(16)) + arktest.assertEQ(buf.readIntBE(0, 6).toString(16), buf.readUIntBE(0, 6).toString(16)) + } + { + let buf = buffer.from([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) + arktest.assertEQ(buf.readIntLE(0, 6).toString(16), '-1') + arktest.assertEQ(buf.readIntBE(0, 6).toString(16), '-1') + arktest.assertEQ(buf.readUIntLE(0, 6), 0xffffffffffff) + arktest.assertEQ(buf.readUIntBE(0, 6), 0xffffffffffff) + } + + { + // test overfill + let buf = buffer.from([0x8f, 0xff, 0xff, 0xff, 0xff, 0xff]) + arktest.assertEQ(buf.readIntLE(0, 6).toString(16), "-71") + arktest.assertEQ(buf.readIntBE(0, 6).toString(16), "-700000000001") + arktest.assertEQ(buf.readUIntLE(0, 6).toString(16), "ffffffffff8f") + arktest.assertEQ(buf.readUIntBE(0, 6).toString(16), "8fffffffffff") + } +} + function testVarLengthUIntReadWrite() { const buf = buffer.from(new Uint8Array(12)) @@ -822,15 +992,17 @@ function testStringWrite() { // Test basic ASCII write const str = "Hello" - const bytesWritten = buf.write(str, 0, str.length, 'utf8') - arktest.assertEQ(bytesWritten, str.length) - arktest.assertEQ(buf.toString('utf8', 0, str.length), str) + const strLen: int = str.length.toInt() + const bytesWritten = buf.write(str, 0, strLen, 'utf8') + arktest.assertEQ(bytesWritten, strLen) + arktest.assertEQ(buf.toString('utf8', 0, strLen), str) // Test write with offset const str2 = "World" - const bytesWritten2 = buf.write(str2, 10, str2.length, 'utf8') - arktest.assertEQ(bytesWritten2, str2.length) - arktest.assertEQ(buf.toString('utf8', 10, 10 + str2.length), str2) + const strLen2: int = str2.length.toInt() + const bytesWritten2 = buf.write(str2, 10, strLen2, 'utf8') + arktest.assertEQ(bytesWritten2, strLen2) + arktest.assertEQ(buf.toString('utf8', 10, 10 + strLen2), str2) // Test partial write const longStr = "ThisIsALongString" @@ -839,6 +1011,14 @@ function testStringWrite() { arktest.assertEQ(buf.toString('utf8', 0, 5), "ThisI") } +function testBufferWrite0022() { + const TAG = 'testBufferWrite0022'; + let buf = buffer.alloc(5); + arktest.expectError(() => { + const res = buf.write("abc", 0, 3, 'invalid_encoding'); + }, 'Parameter error. The type of "encoding" must be BufferEncoding. the encoding invalid_encoding is unknown'); +} + function testBufferWriteStringBase64() { let buf = buffer.alloc(10) let res = buf.write("abcdefg", 3, 1, 'base64') @@ -850,79 +1030,103 @@ function testBufferWriteStringSingleParam() { const str = "abcdefg" let buf = buffer.alloc(10) let res = buf.write(str) - arktest.assertEQ(res, str.length) - arktest.assertEQ(buf.toString('utf8', 0, str.length), str) + let strLen: int = str.length.toInt() + arktest.assertEQ(res, strLen) + arktest.assertEQ(buf.toString('utf8', 0, strLen), str) } function testBufferWriteStringInfinity() { const str = "abcdefg" let buf = buffer.alloc(10) - expectError(() => { - buf.write(str, 0, Infinity); - }, "The value of \"length\" is out of range. It must be >= 0 and <= 10. Received value is: Infinity"); + arktest.expectError(() => { + buf.write(str, 0, Infinity.toInt()); + }, "The value of \"length\" is out of range. It must be >= 0 and <= 10. Received value is: 2147483647"); } function testBufferWriteStringWrongOffset() { const str = "abc" let buf = buffer.alloc(10) - expectError(() => { + arktest.expectError(() => { buf.write(str, -12) - }, "The value of \"offset\" is out of range. It must be >= 0 and <= 10. Received value is: -12"); + }, "The value of \"offset\" is out of range. It must be >= 0 and <= 9. Received value is: -12"); } function testBufferWriteStringOutRangeOffset() { let buf = buffer.alloc(10); - expectError(() => { - buf.write("abc", -Infinity, -Infinity, 'utf8'); - }, "The value of \"offset\" is out of range. It must be >= 0 and <= 10. Received value is: -Infinity"); + arktest.expectError(() => { + buf.write("abc", (-Infinity).toInt(), (-Infinity).toInt(), 'utf8'); + }, "The value of \"offset\" is out of range. It must be >= 0 and <= 9. Received value is: -2147483648"); } function testBufferWriteStringOutRangeLength() { let buf = buffer.alloc(10); - expectError(() => { + arktest.expectError(() => { buf.write("abcdefg", 3, -1); }, "The value of \"length\" is out of range. It must be >= 0 and <= 10. Received value is: -1"); } function testBufferFrom40002() { let buf = buffer.from('abc', 'base64'); - assertEQ(buf.toString(), 'i·') - assertEQ(buf.length, 2); - assertEQ(buf.toString('base64'), 'abc='); + arktest.assertEQ(buf.toString(), 'i·') + arktest.assertEQ(buf.length, 2); + arktest.assertEQ(buf.toString('base64'), 'abc='); } function testBufferFrom30006() { let buf = buffer.from(new String('this is a test'), 'utf8', -1); - assertEQ(buf.length, 14); - assertEQ(buf.toString(), 'this is a test'); + arktest.assertEQ(buf.length, 14); + arktest.assertEQ(buf.toString(), 'this is a test'); } function testBufferBase64Url() { const src = new Uint8Array([250, 251, 252, 253, 254, 255]) const encodeStr = buffer.from(src).toString("base64url") - assertEQ(encodeStr, "-vv8_f7_") - const bytesLength = buffer.byteLength(encodeStr, "base64url") as int - assertEQ(bytesLength, 6) + arktest.assertEQ(encodeStr, "-vv8_f7_") + const bytesLength = buffer.byteLength(encodeStr, "base64url").toInt() + arktest.assertEQ(bytesLength, 6) } function testByteOffset() { let buf = buffer.from("1236"); let offset = buf.byteOffset; - assertEQ(offset, 0) + arktest.assertEQ(offset, 0) + arktest.expectNoThrow(() => { + arktest.assertEQ(JSON.stringify(buf.byteOffset), "0") + }) + + const a = new ArrayBuffer(5) + a.set(0, 0 as byte) + a.set(1, 11 as byte) + a.set(2, 22 as byte) + a.set(3, 33 as byte) + buf = new buffer.Buffer(a, 2) + arktest.assertEQ(buf.byteOffset, 2) + let byteOffset: int; + arktest.expectError((): void => { + byteOffset = -1; + let f = new buffer.Buffer(a, byteOffset) }); + arktest.expectError((): void => { + byteOffset = a.getByteLength(); + let f = new buffer.Buffer(a, byteOffset) + }); + arktest.expectError((): void => { + byteOffset = a.getByteLength() + 1; + let f = new buffer.Buffer(a, byteOffset) + }); } function testFillHex() { const buf1 = buffer.alloc(10).fill('F1刘FG', 0, 10, 'hex'); let str1 = buf1.toString('hex'); - assertEQ(str1, "f1f1f1f1f1f1f1f1f1f1"); + arktest.assertEQ(str1, "f1f1f1f1f1f1f1f1f1f1"); const buf2 = buffer.alloc(10).fill('1A3B5C7Z9D', 0, 10, 'hex'); let str2 = buf2.toString('hex'); - assertEQ(str2, "1a3b5c1a3b5c1a3b5c1a"); + arktest.assertEQ(str2, "1a3b5c1a3b5c1a3b5c1a"); const buf3 = buffer.alloc(10).fill('1A3BFF7', 0, 10, 'hex'); let str3 = buf3.toString('hex'); - assertEQ(str3, "1a3bff1a3bff1a3bff1a"); + arktest.assertEQ(str3, "1a3bff1a3bff1a3bff1a"); } function testIncludesHex() { @@ -930,14 +1134,14 @@ function testIncludesHex() { for (const encode of encodeArr) { let buf = buffer.from("13236"); let flag = buf.includes("abc", 0, encode); - assertEQ(flag, false); + arktest.assertEQ(flag, false); } } function testFromHex() { const buf1 = buffer.from('F1刘FG', 'hex'); let str = buf1.toString('hex'); - assertEQ(str, "f1"); + arktest.assertEQ(str, "f1"); } function testFillAsciiWithUnicode() { @@ -962,4 +1166,95 @@ function testFillBase64UrlWithUnicode() { const buf = buffer.alloc(10).fill('F1刘FG', 0, 10, 'base64url'); const str = buf.toString('hex'); arktest.assertEQ(str, '17514617514617514617'); -} \ No newline at end of file +} + +function testBufferToJSON() { + // Empty Buffer + let buf = buffer.from(new ArrayBuffer(0)); + let str = JSON.stringifyJsonElement(buf.toJSON()); + let parsed = JSON.parseJsonElement(str); + arktest.assertEQ(str, '{"type":"Buffer","data":[]}'); + arktest.assertEQ(parsed.getElement("type").jsonType, jsonx.JsonType.JsonString); + arktest.assertEQ(parsed.getElement("data").jsonType, jsonx.JsonType.JsonArray); + arktest.assertEQ(parsed.getElement("data").asArray().length, 0); + + // Non empty Buffer + buf = buffer.from(new ArrayBuffer(10)); + buf[0] = 1; + buf[1] = 2; + buf[2] = 3; + buf[3] = 4; + buf[4] = 5; + str = JSON.stringifyJsonElement(buf.toJSON()); + parsed = JSON.parseJsonElement(str); + + arktest.assertEQ(str, '{"type":"Buffer","data":[1,2,3,4,5,0,0,0,0,0]}'); + arktest.assertEQ(parsed.getElement("type").jsonType, jsonx.JsonType.JsonString); + arktest.assertEQ(parsed.getElement("data").jsonType, jsonx.JsonType.JsonArray); + const array = parsed.getElement("data").asArray() + arktest.assertEQ(array.length, 10); + for (let i: int = 0; i < array.length; ++i) { + arktest.assertEQ(array[i].jsonType, jsonx.JsonType.JsonNumber); + } + + { + let ab = new ArrayBuffer(10) + let buf = buffer.from(ab as Object, 0, 2) + arktest.assertEQ(JSON.stringify(buf), '{"type":"Buffer","data":[0,0]}') + } +} + +function testFillUcs2WithUnicode() { + const buf = buffer.alloc(10).fill('F1刘FG', 0, 10, 'ucs2'); + const str = buf.toString('hex'); + arktest.assertEQ(str, '46003100185246004700'); + const buf1 = buffer.alloc(10).fill('F1刘FG', 0, 10, 'ucs-2'); + const str1 = buf1.toString('hex'); + arktest.assertEQ(str1, '46003100185246004700'); +} + +function testFillUtf16leWithUnicode() { + const buf = buffer.alloc(10).fill('F1刘FG', 0, 10, 'utf16le'); + const hex = buf.toString('hex'); + arktest.assertEQ(hex, '46003100185246004700'); +} + +function testFillEmpty(){ + let encodeArr: (undefined | buffer.BufferEncoding)[] = [undefined, 'utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', 'binary', 'utf16le', 'base64', 'base64url', 'hex']; + const buf = buffer.alloc(20).fill(''); + for (const encode of encodeArr) { + buf.fill('', 0, buf.length, encode); + arktest.assertEQ(buf.length, 20, encode); + } +} + +function testBufferToString00001() { + let buf1 = buffer.from("abc"); + let str = buf1.toString("ucs2"); + arktest.assertEQ(str, '扡') + + buf1 = buffer.from("abc"); + str = buf1.toString("utf16le"); + arktest.assertEQ(str, '扡') + + let buf = buffer.from('hello world'); + buf1 = buffer.transcode(buf, 'base64', 'utf-8'); + arktest.assertEQ(buf1.toString(), 'aGVsbG8gd29ybGQ='); + arktest.assertEQ(buf1.length, 16) + + + buf1 = buffer.from("1236"); + buf = buffer.transcode(buf1, "ascii", "ucs2"); + str = buf.toString("ucs2") + arktest.assertEQ(str, "1236"); + + buf1 = buffer.from("测试"); + buf = buffer.transcode(buf1, "utf8", "ucs2"); + str = buf.toString("ucs2") + arktest.assertEQ(str, "测试"); + + buf = buffer.from('hello'); + str = buf.toString('base64', -Infinity.toInt(), 0); + arktest.assertEQ(str.length, 0); + arktest.assertEQ(str, ''); +} diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/buffer/BufferTests.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/buffer/BufferTests.ets index 08a7745712ed240217b262a3ad546b7341c81d4a..19093c07f819cff2c6476c25af4875f4e34ae323 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/buffer/BufferTests.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/buffer/BufferTests.ets @@ -15,6 +15,25 @@ import buffer from "@ohos.buffer" +class Foo { + valueOf1() { + return 'this is a test'; + } + + toString1() { + return 'this is a test'; + } +} + +class Person { + name: string; + age: number; + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } +} + function testToString() { { let buf1 = buffer.allocUninitializedFromPool(26); @@ -22,7 +41,7 @@ function testToString() { buf1.writeInt8(i + 97, i); } const res = buf1.toString('base64'); // YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo= - assertEQ(res, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="); + arktest.assertEQ(res, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="); } { let buf = buffer.allocUninitializedFromPool(26); @@ -30,7 +49,7 @@ function testToString() { buf.writeInt8(i + 97, i); } const str = buf.toString('utf8', 30); // ''; - assertEQ(str, '') + arktest.assertEQ(str, '') } { let buf = buffer.allocUninitializedFromPool(26); @@ -38,7 +57,7 @@ function testToString() { buf.writeInt8(i + 97, i); } const str = buf.toString('utf8', 5, 3); // '' - assertEQ(str, ''); + arktest.assertEQ(str, ''); } { let buf = buffer.allocUninitializedFromPool(26); @@ -46,39 +65,39 @@ function testToString() { buf.writeInt8(i + 97, i); } const str = buf.toString('utf8', -1); // abcdefghijklmnopqrstuvwxyz - assertEQ(str, 'abcdefghijklmnopqrstuvwxyz'); + arktest.assertEQ(str, 'abcdefghijklmnopqrstuvwxyz'); } { let buf = buffer.allocUninitializedFromPool(26); for (let i = 0; i < 26; i++) { buf.writeInt8(i + 97, i); } - const str = buf.toString('utf8', Infinity); // '' - assertEQ(str, ''); + const str = buf.toString('utf8', Infinity.toInt()); // '' + arktest.assertEQ(str, ''); } { let buf = buffer.allocUninitializedFromPool(26); for (let i = 0; i < 26; i++) { buf.writeInt8(i + 97, i); } - const str = buf.toString('utf8', -Infinity); // abcdefghijklmnopqrstuvwxyz - assertEQ(str, 'abcdefghijklmnopqrstuvwxyz') + const str = buf.toString('utf8', -Infinity.toInt()); // abcdefghijklmnopqrstuvwxyz + arktest.assertEQ(str, 'abcdefghijklmnopqrstuvwxyz') } { let buf = buffer.allocUninitializedFromPool(26); for (let i = 0; i < 26; i++) { buf.writeInt8(i + 97, i); } - const str = buf.toString('utf8', undefined, Infinity) // abcdefghijklmnopqrstuvwxyz - assertEQ(str, 'abcdefghijklmnopqrstuvwxyz') + const str = buf.toString('utf8', undefined, Infinity.toInt()) // abcdefghijklmnopqrstuvwxyz + arktest.assertEQ(str, 'abcdefghijklmnopqrstuvwxyz') } { let buf = buffer.allocUninitializedFromPool(26); for (let i = 0; i < 26; i++) { buf.writeInt8(i + 97, i); } - const str = buf.toString('utf8', 0, -Infinity); // '' - assertEQ(str, '') + const str = buf.toString('utf8', 0, -Infinity.toInt()); // '' + arktest.assertEQ(str, '') } { let buf = buffer.allocUninitializedFromPool(26); @@ -86,70 +105,603 @@ function testToString() { buf.writeInt8(i + 97, i); } const str = buf.toString('utf8', undefined, -1); // '' - assertEQ(str, '') + arktest.assertEQ(str, '') } { const buf = buffer.from('hello'); const res = buf.toString('base64', 0, -1); // '' - assertEQ(res, '') + arktest.assertEQ(res, '') } { const buf = buffer.from('hello'); - const res = buf.toString('base64', 0, Infinity); // aGVsbG8= - assertEQ(res, 'aGVsbG8=') + const res = buf.toString('base64', 0, Infinity.toInt()); // aGVsbG8= + arktest.assertEQ(res, 'aGVsbG8=') } { const buf = buffer.from('hello'); - const res = buf.toString('base64', 0, -Infinity); // '' - assertEQ(res, '') + const res = buf.toString('base64', 0, -Infinity.toInt()); // '' + arktest.assertEQ(res, '') } { let array = new Uint8Array([252, 11, 3, 67, 237, 118, 91, 177, 43]); let buf = buffer.from(array); - let strUrl = buf.toString('base64url'); - let str = buf.toString('base64'); - assertEQ(strUrl, '_AsDQ-12W7Er'); - assertEQ(str, '/AsDQ+12W7Er'); + let strUrl = buf.toString("base64url"); + let str = buf.toString("base64"); + arktest.assertEQ(strUrl, "_AsDQ-12W7Er"); + arktest.assertEQ(str, "/AsDQ+12W7Er"); array = new Uint8Array([2, 192, 254, 253, 5, 132, 69]); buf = buffer.from(array); - strUrl = buf.toString('base64url'); - str = buf.toString('base64'); - assertEQ(String(strUrl), 'AsD-_QWERQ'); - assertEQ(str, 'AsD+/QWERQ=='); + strUrl = buf.toString("base64url"); + str = buf.toString("base64"); + arktest.assertEQ(String(strUrl), 'AsD-_QWERQ'); + arktest.assertEQ(str, "AsD+/QWERQ=="); array = new Uint8Array([215, 109, 211, 97, 72, 142, 167, 241]); buf = buffer.from(array); - strUrl = buf.toString('base64url'); - str = buf.toString('base64'); - assertEQ(strUrl, '123TYUiOp_E'); - assertEQ(str, '123TYUiOp/E='); + strUrl = buf.toString("base64url"); + str = buf.toString("base64"); + arktest.assertEQ(strUrl, "123TYUiOp_E"); + arktest.assertEQ(str, "123TYUiOp/E="); array = new Uint8Array([252]); buf = buffer.from(array); - strUrl = buf.toString('base64url'); - str = buf.toString('base64'); - assertEQ(strUrl, '_A'); - assertEQ(str, '/A=='); + strUrl = buf.toString("base64url"); + str = buf.toString("base64"); + arktest.assertEQ(strUrl, "_A"); + arktest.assertEQ(str, "/A=="); array = new Uint8Array([252, 97]); buf = buffer.from(array); - strUrl = buf.toString('base64url'); - str = buf.toString('base64'); - assertEQ(strUrl, '_GE'); - assertEQ(str, '/GE='); + strUrl = buf.toString("base64url"); + str = buf.toString("base64"); + arktest.assertEQ(strUrl, "_GE"); + arktest.assertEQ(str, "/GE="); array = new Uint8Array([252, 97, 142]); buf = buffer.from(array); - strUrl = buf.toString('base64url'); - str = buf.toString('base64'); - assertEQ(strUrl, '_GGO'); - assertEQ(str, '/GGO'); + strUrl = buf.toString("base64url"); + str = buf.toString("base64"); + arktest.assertEQ(strUrl, "_GGO"); + arktest.assertEQ(str, "/GGO"); + } +} + +function testWriteUInt8Error() { + let buf = buffer.allocUninitializedFromPool(4); + arktest.expectError(() => {buf.writeUInt8(0x0304, 1);}, 'The value of "value" is out of range. It must be >= 0 and <= 255. Received value is: 772'); +} + +function testWriteInt32BEError() { + let buf = buffer.allocUninitializedFromPool(4); + arktest.expectError(() => {buf.writeInt32BE(3405691582, 0);}, 'The value of "value" is out of range. It must be >= -2147483648 and <= 2147483647. Received value is: 3405691582'); +} + +function testWriteInt32LEError() { + let buf = buffer.allocUninitializedFromPool(4); + arktest.expectError(() => {buf.writeInt32LE(3405691582, 0);}, 'The value of "value" is out of range. It must be >= -2147483648 and <= 2147483647. Received value is: 3405691582'); +} + +function testWriteInt16LEError() { + let buf = buffer.alloc(5); + arktest.expectError(() => {buf.writeInt16LE(48879, 0);}, 'The value of "value" is out of range. It must be >= -32768 and <= 32767. Received value is: 48879'); +} + +function testWriteInt16BEError() { + let buf = buffer.alloc(5); + arktest.expectError(() => {buf.writeInt16BE(48879, 0);}, 'The value of "value" is out of range. It must be >= -32768 and <= 32767. Received value is: 48879'); +} + +function testWriteInt8Error() { + let buf = buffer.alloc(5); + arktest.expectError(() => {buf.writeInt8(0x0304, 0);}, 'The value of "value" is out of range. It must be >= -128 and <= 127. Received value is: 772'); +} + +function testWriteUInt32BEError() { + let buf = buffer.allocUninitializedFromPool(4); + arktest.expectError(() => {buf.writeUInt32BE(-2, 0);}, 'The value of "value" is out of range. It must be >= 0 and <= 4294967295. Received value is: -2'); +} + +function testWriteUInt32LEError() { + let buf = buffer.allocUninitializedFromPool(4); + arktest.expectError(() => {buf.writeUInt32LE(-2, 0);}, 'The value of "value" is out of range. It must be >= 0 and <= 4294967295. Received value is: -2'); +} + +function testWriteUInt16BEError() { + let buf = buffer.allocUninitializedFromPool(4); + arktest.expectError(() => {buf.writeUInt16BE(-2, 0);}, 'The value of "value" is out of range. It must be >= 0 and <= 65535. Received value is: -2'); +} + +function testWriteUInt16LEError() { + let buf = buffer.allocUninitializedFromPool(4); + arktest.expectError(() => {buf.writeUInt16LE(-2, 0);}, 'The value of "value" is out of range. It must be >= 0 and <= 65535. Received value is: -2'); +} + +function testAlloc0016() { + let str = 'Parameter error. The type of "size" must be number and the value cannot be ' + + 'negative. Received value is: -5'; + arktest.expectError(() => {let buf = buffer.alloc(-5);}, str); +} + +function testAllocUninitializedFromPool0024() { + let encodeArr: Array< String > = new Array < String > ('utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', 'binary', 'utf16le', 'base64', 'base64url', 'hex') + encodeArr.forEach((encode )=> { + let buf = buffer.alloc(10).fill("ab$#", 0, undefined, encode as buffer.BufferEncoding); + arktest.assertEQ(buf.length, 10) + }); +} + +function testAllocUninitializedFromPool0026() { + let str = 'Parameter error. The type of "size" must be number and the value cannot be ' + + 'negative. Received value is: -5' + arktest.expectError(() => {let buf = buffer.allocUninitializedFromPool(-5);}, str); +} + +function testByteLength0033() { + let encodeArr: Array< String > = new Array < String > ('utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', 'binary', 'utf16le', 'base64', 'base64url', 'hex') + let result:Array = new Array(4, 4, 8, 8, 4, 4, 4, 8, 3, 3, 2) + for (let i = 0, len = encodeArr.length; i< len; i++) { + let byteLen = buffer.byteLength("abcd", encodeArr[i] as buffer.BufferEncoding); + arktest.assertEQ(byteLen, result[i]) + } +} + +function testConcat0074() { + let str = 'The value of "totalLength" is out of range. It must be >= 0 and <= 2147483647. Received value is: -1'; + let buf1 = buffer.from("123$"); + let buf2 = buffer.from("*35"); + arktest.expectError(() => {let buf = buffer.concat([buf1, buf2], -1);}, str); +} + +function testFill0093() { + let str = 'The value of "offset" is out of range. It must be >= 0 and <= 2147483647. Received value is: -1'; + arktest.expectError(() => {let buf = buffer.alloc(3).fill("$*$", -1);}, str); +} + +function testFill0094() { + let str = 'The value of "end" is out of range. It must be >= 0 and <= 3. Received value is: 5'; + arktest.expectError(() => {let buf = buffer.alloc(3).fill("$*$", 0, 5);}, str); +} + +function testWrite0105() { + let str = 'The value of "offset" is out of range. It must be >= 0 and <= 7. Received value is: -1'; + let buf = buffer.alloc(8); + arktest.expectError(() => {let offset = buf.write("abcde", -1);}, str); +} + +function testCopy0144() { + let str = 'The value of "targetStart" is out of range. It must be >= 0. Received value is: -1'; + let buf1 = buffer.from("123656"); + let buf2 = buffer.from("1235"); + arktest.expectError(() => {let num = buf1.copy(buf2, -1);}, str); +} + +function testSwap160201() { + let str = 'The buffer size must be a multiple of 16-bits'; + let buf1 = buffer.from("132"); + arktest.expectError(() => {buf1.swap16();}, str); +} + +function testswap320211() { + let str = 'The buffer size must be a multiple of 32-bits'; + let buf1 = buffer.from("132"); + arktest.expectError(() => {buf1.swap32();}, str); +} + +function testSwap640221() { + let str = 'The buffer size must be a multiple of 64-bits'; + let buf1 = buffer.from("1234567"); + arktest.expectError(() => {buf1.swap64();}, str); +} + +function testAllocUninitialized0782() { + let str = 'Parameter error. The type of "size" must be number and the value cannot be ' + + 'negative. Received value is: -5' + arktest.expectError(() => {let buf = buffer.allocUninitialized(-5);}, str); +} + +function testCopy0148() { + let uint8array = new Uint8Array(10).fill(0); + let buf = buffer.from([1, 2, 3, 4, 5, 6, 7]); + let num = buf.copy(uint8array, 0, 3); + arktest.assertEQ(num, 4); + let str = uint8array.toString(); + arktest.assertEQ(str, '4,5,6,7,0,0,0,0,0,0'); +} + +function testCopy0149() { + let uint8array = new Uint8Array(10).fill(0); + let buf = buffer.from([1, 2, 3, 4, 5, 6, 7]); + let num = buf.copy(uint8array, 0, 3, 255); + arktest.assertEQ(num , 4); + let str = uint8array.toString(); + arktest.assertEQ(str, '4,5,6,7,0,0,0,0,0,0'); +} + +function testReadUIntBE0631() { + let str = 'The value of "offset" is out of range. It must be >= 0 and <= 1. Received value is: 2'; + let buf = buffer.allocUninitializedFromPool(4); + buf.writeUIntBE(0x13141516, 0, 4); + arktest.expectError(() => {let ref = Double(buf.readUIntBE(2, 3)).toString(16);}, str); +} + +function testReadUIntLE0641() { + let str = 'The value of "offset" is out of range. It must be >= 0 and <= 1. Received value is: 2'; + let buf = buffer.allocUninitializedFromPool(4); + buf.writeUIntLE(0x13141516, 0, 4); + arktest.expectError(() => {let ref = Double(buf.readUIntLE(2, 3)).toString(16);}, str); +} + +function testReadIntBE0651() { + let str = 'The value of "offset" is out of range. It must be >= 0 and <= 1. Received value is: 2'; + let buf = buffer.allocUninitializedFromPool(6); + buf.writeIntBE(0x123456789011, 0, 6); + arktest.expectError(() => {let ref = Double(buf.readIntBE(2, 5)).toString(16);}, str); +} + +function testReadIntLE0661() { + let str = 'The value of "offset" is out of range. It must be >= 0 and <= 1. Received value is: 2'; + let buf = buffer.allocUninitializedFromPool(6); + buf.writeIntLE(0x123456789011, 0, 6); + arktest.expectError(() => {let ref = Double(buf.readIntLE(2, 5)).toString(16);}, str); +} + +function testBufferCopy0012() { + let str = 'The value of "sourceStart" is out of range. It must be >= 0. Received value is: -2147483647'; + let buf1 = buffer.allocUninitializedFromPool(26); + let buf2 = buffer.allocUninitializedFromPool(26).fill('!'); + for (let i = 0; i < 26; i++) { + buf1.writeInt8(i + 97, i); + } + arktest.expectError(() => {buf1.copy(buf2, undefined, -Infinity.toInt());}, str); +} + +function testBufferCopy0018() { + let str = 'The value of "sourceEnd" is out of range. It must be >= 0. Received value is: -2147483647'; + let buf1 = buffer.allocUninitializedFromPool(26); + let buf2 = buffer.allocUninitializedFromPool(26).fill('!'); + for (let i = 0; i < 26; i++) { + buf1.writeInt8(i + 97, i); + } + arktest.expectError(() => {buf1.copy(buf2, undefined, undefined, -Infinity.toInt());}, str); +} + +function testBufferReadBigInt64BE0004() { + let str = 'The value of "offset" is out of range. It must be >= 0 and <= 0. Received value is: -1'; + let buf = buffer.alloc(8) + arktest.expectError(() => {buf.readBigInt64BE(-1);}, str); +} + +function testIsBuffer0002() { + let obj = new Object(); + let flag = buffer.isBuffer(obj); + arktest.assertEQ(String(flag), 'false'); +} + +function testIsEncoding0001() { + let encodeArr = ['utf8', 'utf-8', 'ucs2', 'ucs-2', 'ascii', 'latin1', 'binary', + 'utf16le', 'base64', 'base64url', 'hex']; + for (const encode of encodeArr) { + let flag = buffer.isEncoding(encode); + arktest.assertEQ(String(flag), 'true'); + } +} + +function testSlice0002() { + let blob: buffer.Blob = new buffer.Blob(['a', 'b', 'c']); + let result = blob.slice(1); + arktest.assertEQ(result.size, 2); +} + +function testSlice0003() { + let blob: buffer.Blob = new buffer.Blob(['a', 'b', 'c', "d"]); + let result = blob.slice(1, 2); + arktest.assertEQ(result.size, 1); +} + +function testSlice0004() { + let blob: buffer.Blob = new buffer.Blob(['a', 'b', 'c']); + let result = blob.slice(1, 2, "Number"); + arktest.assertEQ(result.size, 1); +} + +function testBufferBufferCompare0004() { + let str = 'The value of "targetStart" is out of range. It must be >= 0 and <= 2147483647. Received value is: -1'; + const buf1 = buffer.from([1, 2, 3]); + const buf2 = buffer.from([3, 2, 1]); + arktest.expectError(() => {const result = buf1.compare(buf2, -1);}, str); +} + +function testBufferEquals0002() { + let buf = buffer.from('你好'); + arktest.assertEQ(buf.includes('你好', undefined, 'utf-8'), true); + arktest.assertEQ(buf.includes('hello', undefined, 'utf-8'), false); +} + +function testBufferCopy0005() { + let buf1 = buffer.allocUninitializedFromPool(26); + let buf2 = buffer.allocUninitializedFromPool(26).fill('!'); + for (let i = 0; i < 26; i++) { + buf1.writeInt8(i + 97, i); } + buf1.copy(buf2, Infinity.toInt()); + arktest.assertEQ(buf2.toString(), '!!!!!!!!!!!!!!!!!!!!!!!!!!'); +} + +function testAlloc30040() { + let buf = buffer.alloc(10, ''); + arktest.assertEQ(buf.length, 10); +} + +function testAlloc30050() { + let buf = buffer.alloc(20).fill(''); + arktest.assertEQ(buf.length, 20); +} + +function testBufferAlloc0009() { + let buf9 = buffer.alloc(3, ''); + arktest.assertEQ(buf9.length, 3); + let pair = buf9.entries(); + let next = pair.next(); + let isOk = true; + while (!next.done) { + String() + isOk = Boolean(isOk&&(String(next?.value?.[1]).localeCompare('')==0)); + next = pair.next(); + } + arktest.assertEQ(isOk, false); +} + +function testfrom0266() { + let buf12 = buffer.from("AsD-_QWEr", "base64url") + let str = buf12.toString("hex"); + arktest.assertEQ(str, '02c0fefd0584'); +} + + +function testfrom026601() { + let buf14 = buffer.from("_AsDQ-12W7Er_", "base64url") + let str = buf14.toString("hex"); + arktest.assertEQ(str, 'fc0b0343ed765bb12b'); +} + +function testIndexOf0008() { + let buf = buffer.from("123"); + let index = buf.indexOf(""); + arktest.assertEQ(index, 0); +} + +function testfrom0789() { + let sharedArr = new ArrayBuffer(10) + let uint8Arr = new Uint8Array(sharedArr); + uint8Arr[9] = 9; + let bufObj = buffer.from(sharedArr, 5, 5); + uint8Arr[1] = 10; + arktest.assertEQ(bufObj.length, 5); +} +function testfrom0790() { + let sharedArr = new ArrayBuffer(1000) + let bufObj = buffer.from(sharedArr, 5, 1); + arktest.assertEQ(bufObj.length, 1); +} + +function testfrom0791() { + let sharedArr = new ArrayBuffer(10) + let bufObj = buffer.from(sharedArr, 3, 6); + arktest.assertEQ(bufObj.length, 6); +} + +function testFrom0013() { + let str = 'The value of "byteOffset" is out of range. It must be >= 0 and <= 5. Received value is: 6'; + let arrayBuffer = new ArrayBuffer(5); + let array = new Int8Array(arrayBuffer); + array[0] = 1; + array[1] = 2; + array[2] = 3; + array[3] = 4; + array[4] = 5; + arktest.expectError(() => {buffer.from(arrayBuffer, 6, 1);}, str); +} + +function testBufferFrom10008() { + const ab1 = new ArrayBuffer(8); + const res = buffer.from(ab1, 4, 0); + arktest.assertEQ(res.length, 0); +} + +function testBufferFrom10009() { + let str = 'The value of "length" is out of range. It must be >= 0 and <= 8. Received value is: -1'; + const ab1 = new ArrayBuffer(8); + arktest.expectError(() => {buffer.from(ab1, 0, -1);}, str); +} + +function testBufferFrom10010() { + let str = 'The value of "length" is out of range. It must be >= 0 and <= 8. Received value is: 2147483647'; + const ab1 = new ArrayBuffer(8); + arktest.expectError(() => {buffer.from(ab1, 0, Infinity.toInt());}, str); +} + +function testWrite0009() { + let str = 'The buffer length is 0, and writing data is not allowed'; + let buf = buffer.alloc(0); + arktest.expectError(() => {let offset = buf.write("");}, str); +} + +function testfrom0792() { + let ab = new ArrayBuffer(100); + let buf = buffer.from(ab, 1.2 as int, 1.2 as int); + let res = buf.toString('hex'); + arktest.assertEQ(res, '00'); +} + +function testfrom0793() { + let ab = new ArrayBuffer(100); + let buf = buffer.from(ab, 1, 1.2 as int); + let res = buf.toString('hex'); + arktest.assertEQ(res, '00'); +} + +function testfrom0794() { + let ab = new ArrayBuffer(100); + let buf = buffer.from(ab, 1.2 as int, 1); + let res = buf.toString('hex'); + arktest.assertEQ(res, '00'); +} + +function testfrom0795() { + let ab = new ArrayBuffer(100); + let buf = buffer.from(ab, 9.9 as int, 9.9 as int); + let res = buf.toString('hex'); + arktest.assertEQ(res, '000000000000000000'); +} + +function testfrom0796() { + let ab = new ArrayBuffer(100); + let buf = buffer.from(ab, 2.9 as int , 2.9 as int); + let res = buf.toString('hex'); + arktest.assertEQ(res, '0000'); +} + +function testTranscode0084() { + let str = 'Parameter error. The type of "toEnc" must be BufferEncoding. the toEnc 0 is unknown'; + let buf1 = buffer.from("测试"); + arktest.expectError(() => {let buf = buffer.transcode(buf1, "utf8", '0');}, str); +} + +function testfrom0783() { + const buf = buffer.from([10]); + arktest.assertEQ(buf.length, 1); +} + +function testFrom30140() { + let str = 'Parameter error. The type of "input" must be Buffer or ArrayBuffer, Array, Array-like'; + let obj = new Map() + arktest.expectError(() => {let buf1 = buffer.from(obj, 0, 5);}, str); +} + +function testBufferFrom30009() { + let str = 'Parameter error. The type of "input" must be Buffer or ArrayBuffer, Array, Array-like'; + const person = new Person("Alice", 30); + arktest.expectError(() => { + buffer.from(person, 11, 0); + buffer.from(person, 11, -1); + buffer.from(person, 11, Infinity.toInt()); + buffer.from(person, 11, -Infinity.toInt()); + }, str); +} + +function testFrom0015() { + let buf = new Uint8Array([97, 98]); + const buf1 = buffer.from(buf, -1, -1); + let str = buf1.toString(); + arktest.assertEQ(str, 'ab'); +} + +function testBufferFrom30005() { + let str = 'Parameter error. The type of "encoding" must be BufferEncoding. the encoding test is unknown'; + arktest.expectError(() => {let buf = buffer.from(new String('hello world'), 'test', 1);}, str); +} + +function testBufferBufferCompare0005() { + const buf1 = buffer.from([1, 2, 3]); + const buf2 = buffer.from([3, 2, 1]); + const result = buf1.compare(buf2, Infinity.toInt()); + arktest.assertEQ(result, 1); + +} + +function testBufferBufferCompare0017() { + const buf1 = buffer.from([1, 2, 3]); + const buf2 = buffer.from([1, 2, 3]); + const result = buf1.compare(buf2, undefined, undefined, Infinity.toInt()); + arktest.assertEQ(result, -1); +} + +function testFrom0009() { + let str = 'Parameter error. The type of "input" must be Buffer or ArrayBuffer, Array, Array-like'; + let foo = new Foo(); + arktest.expectError(() => {const buf = buffer.from(foo, 'utf8', 0);}, str); +} + +function testFrom0014() { + let buf = new Uint8Array([97, 98]); + const buf1 = buffer.from(buf); + let str = buf1.toString(); + arktest.assertEQ(str, 'ab'); } function main(): int { - const suite = new ArkTestsuite("Buffer API tests") + const suite = new arktest.ArkTestsuite("Buffer API tests") suite.addTest("Test buffer toString", testToString) + suite.addTest("Test buffer WriteUInt8 throw value error", testWriteUInt8Error) + suite.addTest("Test buffer WriteInt32BE throw value error", testWriteInt32BEError) + suite.addTest("Test buffer WriteInt32LE throw value error", testWriteInt32LEError) + suite.addTest("Test buffer WriteInt16LE throw value error", testWriteInt16LEError) + suite.addTest("Test buffer WriteInt16BE throw value error", testWriteInt16BEError) + + suite.addTest("Test buffer WriteInt8 throw value error", testWriteInt8Error) + suite.addTest("Test buffer WriteUInt32BE throw value error", testWriteUInt32BEError) + suite.addTest("Test buffer WriteUInt32LE throw value error", testWriteUInt32LEError) + suite.addTest("Test buffer WriteUInt16BE throw value error", testWriteUInt16BEError) + suite.addTest("Test buffer WriteUInt16LE throw value error", testWriteUInt16LEError) + suite.addTest("Test buffer testAlloc0016", testAlloc0016) + suite.addTest("Test buffer testAllocUninitializedFromPool0024", testAllocUninitializedFromPool0024) + suite.addTest("Test buffer testAllocUninitializedFromPool0026", testAllocUninitializedFromPool0026) + suite.addTest("Test buffer testByteLength0033", testByteLength0033) + suite.addTest("Test buffer testConcat0074", testConcat0074) + suite.addTest("Test buffer testFill0093", testFill0093) + suite.addTest("Test buffer testFill0094", testFill0094) + suite.addTest("Test buffer testWrite0105", testWrite0105) + suite.addTest("Test buffer testCopy0144", testCopy0144) + suite.addTest("Test buffer testSwap160201", testSwap160201) + suite.addTest("Test buffer testswap320211", testswap320211) + suite.addTest("Test buffer testSwap640221", testSwap640221) + suite.addTest("Test buffer testAllocUninitialized0782", testAllocUninitialized0782) + suite.addTest("Test buffer testCopy0148", testCopy0148) + suite.addTest("Test buffer testCopy0149", testCopy0149) + suite.addTest("Test buffer testBufferCopy0012", testBufferCopy0012) + suite.addTest("Test buffer testBufferCopy0018", testBufferCopy0018) + suite.addTest("Test buffer testBufferReadBigInt64BE0004", testBufferReadBigInt64BE0004) + suite.addTest("Test buffer testIsBuffer0002", testIsBuffer0002) + suite.addTest("Test buffer testIsEncoding0001", testIsEncoding0001) + suite.addTest("Test buffer testReadUIntBE0631", testReadUIntBE0631) + suite.addTest("Test buffer testReadUIntLE0641", testReadUIntLE0641) + suite.addTest("Test buffer testReadIntBE0651", testReadIntBE0651) + suite.addTest("Test buffer testReadIntLE0661", testReadIntLE0661) + suite.addTest("Test buffer testSlice0002", testSlice0002) + suite.addTest("Test buffer testSlice0003", testSlice0003) + suite.addTest("Test buffer testSlice0004", testSlice0004) + suite.addTest("Test buffer testBufferBufferCompare0004", testBufferBufferCompare0004) + suite.addTest("Test buffer testBufferEquals0002", testBufferEquals0002) + suite.addTest("Test buffer testBufferCopy0005", testBufferCopy0005) + suite.addTest("Test buffer testAlloc30040", testAlloc30040) + suite.addTest("Test buffer testAlloc30050", testAlloc30050) + suite.addTest("Test buffer testBufferAlloc0009", testBufferAlloc0009) + suite.addTest("Test buffer testfrom0266", testfrom0266) + suite.addTest("Test buffer testfrom026601", testfrom026601) + suite.addTest("Test buffer testIndexOf0008", testIndexOf0008) + suite.addTest("Test buffer testfrom0789", testfrom0789) + suite.addTest("Test buffer testfrom0790", testfrom0790) + suite.addTest("Test buffer testfrom0791", testfrom0791) + suite.addTest("Test buffer testFrom0013", testFrom0013) + suite.addTest("Test buffer testBufferFrom10008", testBufferFrom10008) + suite.addTest("Test buffer testBufferFrom10009", testBufferFrom10009) + suite.addTest("Test buffer testBufferFrom10010", testBufferFrom10010) + suite.addTest("Test buffer testWrite0009", testWrite0009) + suite.addTest("Test buffer testfrom0792", testfrom0792) + suite.addTest("Test buffer testfrom0793", testfrom0793) + suite.addTest("Test buffer testfrom0794", testfrom0794) + suite.addTest("Test buffer testfrom0795", testfrom0795) + suite.addTest("Test buffer testfrom0796", testfrom0796) + suite.addTest("Test buffer testTranscode0084", testTranscode0084) + suite.addTest("Test buffer testfrom0783", testfrom0783) + suite.addTest("Test buffer testFrom30140", testFrom30140) + suite.addTest("Test buffer testFrom0015", testFrom0015) + suite.addTest("Test buffer testBufferFrom30005", testBufferFrom30005) + suite.addTest("Test buffer testBufferFrom30009", testBufferFrom30009) + suite.addTest("Test buffer testBufferBufferCompare0005", testBufferBufferCompare0005) + suite.addTest("Test buffer testBufferBufferCompare0017", testBufferBufferCompare0017) + suite.addTest("Test buffer testFrom0009", testFrom0009) + suite.addTest("Test buffer testFrom0014", testFrom0014) return suite.run() } \ No newline at end of file diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/url/UrlTestMethods.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/url/UrlTestMethods.ets index 98c68088d2d30bac2955cbd03a1935dc94d88b22..5b80919aca29b8e7775de0f312e7eba1b6c6aac3 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/url/UrlTestMethods.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/url/UrlTestMethods.ets @@ -28,7 +28,7 @@ function testUrlSet003() { let params = new url.URLParams("1=value1&2=value2&key3=3"); params.set("ma 大", "10¥"); let res = params.toString(); - assertEQ(res, "1=value1&2=value2&key3=3&ma+%E5%A4%A7=10%EF%BF%A5"); + arktest.assertEQ(res, "1=value1&2=value2&key3=3&ma+%E5%A4%A7=10%EF%BF%A5"); } /** @@ -46,7 +46,7 @@ function testUrlSet004() { // params.append("1","ACA"); params.set("1", "CCC"); let res = params.toString(); - assertEQ(res, "1=CCC&2=value2&key3=3"); + arktest.assertEQ(res, "1=CCC&2=value2&key3=3"); } /** @@ -63,7 +63,7 @@ function testUrlSet005() { let params = new url.URLParams("1=value1&2=value2&key3=3"); params.set('12', 'BBB'); let res = params.toString(); - assertEQ(res, "1=value1&2=value2&key3=3&12=BBB"); + arktest.assertEQ(res, "1=value1&2=value2&key3=3&12=BBB"); } /** @@ -82,9 +82,9 @@ function testUrlSet006() { // let a = 12; // params.set(a, 'BBB'); // } catch(err) { - // assertEQ(err.toString(),`BusinessError: Parameter error. The type of ${a} must be string`) - // assertEQ(err.code,401) - // assertEQ(err.message,`Parameter error. The type of ${a} must be string`); + // arktest.assertEQ(err.toString(),`BusinessError: Parameter error. The type of ${a} must be string`) + // arktest.assertEQ(err.code,401) + // arktest.assertEQ(err.message,`Parameter error. The type of ${a} must be string`); // } } @@ -102,7 +102,7 @@ function testUrlSet007() { let params = new url.URLParams(); params.set('name', 'value'); let res = params.toString(); - assertEQ(res, "name=value"); + arktest.assertEQ(res, "name=value"); } /** @@ -117,7 +117,7 @@ function testUrlSort001() { let params = new url.URLParams("1=value1&3=value3&2=key2"); params.sort(); let res = params.toString(); - assertEQ(res, "1=value1&2=key2&3=value3"); + arktest.assertEQ(res, "1=value1&2=key2&3=value3"); } /** @@ -132,7 +132,7 @@ function testUrlSort002() { let params = new url.URLParams("a=value1&c=value2&b=key2"); params.sort(); let res = params.toString(); - assertEQ(res, "a=value1&b=key2&c=value2"); + arktest.assertEQ(res, "a=value1&b=key2&c=value2"); } /** @@ -148,7 +148,7 @@ function testUrlSort003() { params.append("a", "ACA"); params.sort(); let res = params.toString(); - assertEQ(res, "a=ACA&b=33&c=value2&d=value1"); + arktest.assertEQ(res, "a=ACA&b=33&c=value2&d=value1"); } /** @@ -163,7 +163,7 @@ function testUrlSort004() { let params = new url.URLParams("1=value1&3=value3&2=key2&4=key4"); params.sort(); let res = params.toString(); - assertEQ(res, "1=value1&2=key2&3=value3&4=key4"); + arktest.assertEQ(res, "1=value1&2=key2&3=value3&4=key4"); } /** @@ -178,7 +178,7 @@ function testUrlSort005() { let params = new url.URLParams("a=value1&c=value2&4=key4&b=key2"); params.sort(); let res = params.toString(); - assertEQ(res, "4=key4&a=value1&b=key2&c=value2"); + arktest.assertEQ(res, "4=key4&a=value1&b=key2&c=value2"); } /** @@ -197,7 +197,7 @@ function testUrlValues001() { arr.push(value); i++ } - assertEQ(arr[0], "value1") + arktest.assertEQ(arr[0], "value1") } /** @@ -216,7 +216,7 @@ function testUrlValues002() { arr.push(value); i++ } - assertEQ(arr[1], "value2") + arktest.assertEQ(arr[1], "value2") } /** @@ -236,7 +236,7 @@ function testUrlValues003() { arr.push(value); i++ } - assertEQ(arr[3], "ACA") + arktest.assertEQ(arr[3], "ACA") } /** @@ -255,7 +255,7 @@ function testUrlValues004() { arr.push(value); i++ } - assertEQ(arr[3], "key4") + arktest.assertEQ(arr[3], "key4") } /** @@ -274,7 +274,7 @@ function testUrlValues005() { arr.push(value); i++ } - assertEQ(arr[4], "key5") + arktest.assertEQ(arr[4], "key5") } /** * @tc.number: SUB_COMMONLIBRARY_ETSUTILS_URL_16700 @@ -287,10 +287,10 @@ function testUrlValues005() { function testNewUrl001() { let params = url.URL.parseURL('http://username:password@host:8080/directory/file?query#fragment', undefined) let result = params.toString() - assertEQ(result, "http://username:password@host:8080/directory/file?query#fragment") + arktest.assertEQ(result, "http://username:password@host:8080/directory/file?query#fragment") let params1 = url.URL.parseURL('http://username:password@host:8080/directory/file?query#fragment') let result2 = params1.toString() - assertEQ(result2, "http://username:password@host:8080/directory/file?query#fragment") + arktest.assertEQ(result2, "http://username:password@host:8080/directory/file?query#fragment") } /** @@ -304,7 +304,7 @@ function testNewUrl001() { function testUrlToString001() { let params = url.URL.parseURL('http://username:password@host:8080/directory/file?query#fragment') let result = params.toString() - assertEQ(result, "http://username:password@host:8080/directory/file?query#fragment") + arktest.assertEQ(result, "http://username:password@host:8080/directory/file?query#fragment") } /** @@ -318,7 +318,7 @@ function testUrlToString001() { function testUrlToString002() { let params = url.URL.parseURL('http://username:password@host:8080/directory/file') let result = params.toString() - assertEQ(result, "http://username:password@host:8080/directory/file") + arktest.assertEQ(result, "http://username:password@host:8080/directory/file") } /** @@ -332,7 +332,7 @@ function testUrlToString002() { function testUrlToString003() { let params = url.URL.parseURL('http://username:password@host:8080#fragment') let result = params.toString() - assertEQ(result, "http://username:password@host:8080/#fragment") + arktest.assertEQ(result, "http://username:password@host:8080/#fragment") } /** @@ -346,7 +346,7 @@ function testUrlToString003() { function testUrlToString004() { let params = url.URL.parseURL('http1://host/directory/file?query#fragment') let result = params.toString() - assertEQ(result, "http1://host/directory/file?query#fragment") + arktest.assertEQ(result, "http1://host/directory/file?query#fragment") } /** @@ -360,7 +360,7 @@ function testUrlToString004() { function testUrlToString005() { let params = url.URL.parseURL('http:host:8080/directory/file?query#fragment') let result = params.toString() - assertEQ(result, "http://host:8080/directory/file?query#fragment") + arktest.assertEQ(result, "http://host:8080/directory/file?query#fragment") } /** @@ -374,7 +374,7 @@ function testUrlToString005() { function testUrlToString006() { let params = url.URL.parseURL('https://255.16581375') let result = params.toString() - assertEQ(result, "https://255.253.2.255/") + arktest.assertEQ(result, "https://255.253.2.255/") } /** @@ -388,7 +388,7 @@ function testUrlToString006() { function testUrlToString007() { let params = url.URL.parseURL('https://192.1.') let result = params.toString() - assertEQ(result, "https://192.0.0.1/") + arktest.assertEQ(result, "https://192.0.0.1/") } /** @@ -402,7 +402,7 @@ function testUrlToString007() { function testUrlToString008() { let params = url.URL.parseURL('https://192.1..') let result = params.toString() - assertEQ(result, "https://192.1../") + arktest.assertEQ(result, "https://192.1../") } /** @@ -416,7 +416,7 @@ function testUrlToString008() { function testUrlToString009() { let params = url.URL.parseURL('https://192.95645654354.8.f') let result = params.toString() - assertEQ(result, "https://192.95645654354.8.f/") + arktest.assertEQ(result, "https://192.95645654354.8.f/") } /** @@ -430,7 +430,7 @@ function testUrlToString009() { function testUrlToString010() { let params = url.URL.parseURL('coap://?key=value#hash') let result = params.toString() - assertEQ(result, "coap://?key=value#hash") + arktest.assertEQ(result, "coap://?key=value#hash") } /** @@ -444,7 +444,7 @@ function testUrlToString010() { function testUrlToString011() { let params = url.URL.parseURL('upcad://akls/casd/saqe/ss/?dalks=ccc') let result = params.toString() - assertEQ(result, "upcad://akls/casd/saqe/ss/?dalks=ccc") + arktest.assertEQ(result, "upcad://akls/casd/saqe/ss/?dalks=ccc") } /** @@ -471,11 +471,12 @@ function testUrlToString012() { + "%92%C3%93%C3%94%C3%95%C3%96%C3%97%C3%98%C3%99%C3%9A%C3%9B%C3%9C%C3%9D%C3%9E%C3%9F%C3%A0%C3" + "%A1%C3%A2%C3%A3%C3%A4%C3%A5%C3%A6%C3%A7%C3%A8%C3%A9%C3%AA%C3%AB%C3%AC%C3%AD%C3%AE%C3%AF%C3" + "%B0%C3%B1%C3%B2%C3%B3%C3%B4%C3%B5%C3%B6%C3%B7%C3%B8%C3%B9%C3%BA%C3%BB%C3%BC%C3%BD%C3%BE%C3%BF"; - assertEQ(result, urlEncode); + arktest.assertEQ(result, urlEncode); } + function main(): int { - let suite = new ArkTestsuite("URL UT tests"); + let suite = new arktest.ArkTestsuite("URL UT tests"); suite.addTest("testUrlSet003", testUrlSet003); suite.addTest("testUrlSet004", testUrlSet004); suite.addTest("testUrlSet005", testUrlSet005); diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/ArrayList/ArrayListSortSubArrayListTests.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/ArrayList/ArrayListSortSubArrayListTests.ets index 69556babdd75dc1532d2386065295cc1f7fdce30..c1358b5975e132a46248644d71017b77d05270cf 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/ArrayList/ArrayListSortSubArrayListTests.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/ArrayList/ArrayListSortSubArrayListTests.ets @@ -16,10 +16,10 @@ import {ArrayList} from "@ohos.util.ArrayList"; import {BusinessError} from "@ohos.base"; -const OutOfBoundsErrorCodeId: number = 10200001; +const OutOfBoundsErrorCodeId: int = 10200001; function main() { - const suite = new ArkTestsuite("ArrayList API tests"); + const suite = new arktest.ArkTestsuite("ArrayList API tests"); suite.addTest("ArrayList sort", testSortNumbers); suite.addTest("ArrayList sort with different types in ascending order", testSortDifferentTypesAscending); @@ -40,16 +40,16 @@ function main() { } class Temp { - age: number = 0; + age: double = 0; name: string = ""; } class TestData { - static readonly testNumber0: number = 0; - static readonly testNumber1: number = 1; - static readonly testNumber3: number = 3; - static readonly testNumber5: number = 5; - static readonly testNumber8: number = 8; + static readonly testNumber0: double = 0; + static readonly testNumber1: double | int = 1; + static readonly testNumber3: double | int = 3; + static readonly testNumber5: double | int = 5; + static readonly testNumber8: double | int = 8; static readonly forNumber: Number[] = [1, 2, 3, 4, 5]; static readonly forString: String[] = ["ssss", "2", "%", "Z"]; static readonly forBoolean: Boolean[] = [true, false, false, true]; @@ -65,13 +65,13 @@ class TestData { } function testSortNumbers() { - let arrayList1 = new ArrayList(); + let arrayList1 = new ArrayList(); initializeArrayList(arrayList1, TestData.result1); let array1 = arrayList1.convertToArray(); arrayList1.sort(); array1.sort(); - for (let i: number = 0; i < arrayList1.length; i++) { - assertEQ(arrayList1[i], array1[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList1.length; i++) { + arktest.assertEQ(arrayList1[i], array1[i], `Element at index ${i} should match`); } let arrayList2 = new ArrayList(); @@ -79,8 +79,8 @@ function testSortNumbers() { let array2 = arrayList2.convertToArray(); arrayList2.sort(); array2.sort(); - for (let i: number = 0; i < arrayList2.length; i++) { - assertEQ(arrayList2[i], array2[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList2.length; i++) { + arktest.assertEQ(arrayList2[i], array2[i], `Element at index ${i} should match`); } let arrayList3 = new ArrayList(); @@ -88,8 +88,8 @@ function testSortNumbers() { let array3 = arrayList3.convertToArray(); arrayList3.sort(); array3.sort(); - for (let i: number = 0; i < arrayList3.length; i++) { - assertEQ(arrayList3[i], array3[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList3.length; i++) { + arktest.assertEQ(arrayList3[i], array3[i], `Element at index ${i} should match`); } let arrayList4 = new ArrayList(); @@ -97,13 +97,13 @@ function testSortNumbers() { let array4 = arrayList4.convertToArray(); arrayList4.sort(); array4.sort(); - for (let i: number = 0; i < arrayList4.length; i++) { - assertEQ(arrayList4[i], array4[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList4.length; i++) { + arktest.assertEQ(arrayList4[i], array4[i], `Element at index ${i} should match`); } } function testSortMultipleTypes() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); for (let i: int = 0; i < TestData.forNumber.length; i++) { arrayList.add(TestData.forNumber[i]); } @@ -119,17 +119,17 @@ function testSortMultipleTypes() { let array = arrayList.convertToArray(); arrayList.sort(); array.sort(); - for (let i: number = 0; i < arrayList.length; i++) { - assertEQ(arrayList[i], array[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList.length; i++) { + arktest.assertEQ(arrayList[i], array[i], `Element at index ${i} should match`); } } function testSortDifferentTypesAscending() { - let arrayList1 = new ArrayList(); + let arrayList1 = new ArrayList(); initializeArrayList(arrayList1, TestData.result1); - arrayList1.sort((a: number, b: number) => a - b); + arrayList1.sort((a: double, b: double) => a - b); for (let i: int = 0; i < arrayList1.length; i++) { - assertEQ(arrayList1[i], TestData.forNumber[i], `Element at index ${i} should match`); + arktest.assertEQ(arrayList1[i], TestData.forNumber[i], `Element at index ${i} should match`); } let arrayList2 = new ArrayList(); @@ -137,8 +137,8 @@ function testSortDifferentTypesAscending() { let array2 = arrayList2.convertToArray(); arrayList2.sort((a: string, b: string) => a.compareTo(b)); array2.sort((a: string, b: string) => a.compareTo(b)); - for (let i: number = 0; i < arrayList2.length; i++) { - assertEQ(arrayList2[i], array2[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList2.length; i++) { + arktest.assertEQ(arrayList2[i], array2[i], `Element at index ${i} should match`); } let arrayList3 = new ArrayList(); @@ -146,44 +146,44 @@ function testSortDifferentTypesAscending() { let array3 = arrayList3.convertToArray(); arrayList3.sort((a: boolean, b: boolean) => (a ? 1 : 0) - (b ? 1 : 0)); array3.sort((a: boolean, b: boolean) => (a ? 1 : 0) - (b ? 1 : 0)); - for (let i: number = 0; i < arrayList3.length; i++) { - assertEQ(arrayList3[i], array3[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList3.length; i++) { + arktest.assertEQ(arrayList3[i], array3[i], `Element at index ${i} should match`); } let arrayList4 = new ArrayList(); initializeArrayList(arrayList4, TestData.forObject); let array4 = arrayList4.convertToArray(); - arrayList4.sort((a: object, b: object): number => { + arrayList4.sort((a: object, b: object): double => { let sa = new String(a); let sb = new String(b); return sa.compareTo(sb); }); - array4.sort((a: object, b: object): number => { + array4.sort((a: object, b: object): double => { let sa = new String(a); let sb = new String(b); return sa.compareTo(sb); }); - for (let i: number = 0; i < arrayList4.length; i++) { - assertEQ(arrayList4[i], array4[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList4.length; i++) { + arktest.assertEQ(arrayList4[i], array4[i], `Element at index ${i} should match`); } } function testSortWithContainerChange() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); initializeArrayList(arrayList, TestData.result1); - arrayList.sort((a: number, b: number) => { + arrayList.sort((a: double, b: double) => { arrayList.clear(); return a - b; }); - assertEQ(arrayList.length, 0, "ArrayList length should be 0 after clear"); + arktest.assertEQ(arrayList.length, 0, "ArrayList length should be 0 after clear"); } function testSortDifferentTypesDescending() { - let arrayList1 = new ArrayList(); + let arrayList1 = new ArrayList(); initializeArrayList(arrayList1, TestData.forNumber); - arrayList1.sort((a: number, b: number) => b - a); + arrayList1.sort((a: double, b: double) => b - a); for (let i: int = 0; i < arrayList1.length; i++) { - assertEQ(arrayList1[i], TestData.result1[i], `Element at index ${i} should match`); + arktest.assertEQ(arrayList1[i], TestData.result1[i], `Element at index ${i} should match`); } let arrayList2 = new ArrayList(); @@ -191,8 +191,8 @@ function testSortDifferentTypesDescending() { let array2 = arrayList2.convertToArray(); arrayList2.sort((a: string, b: string) => b.compareTo(a)); array2.sort((a: string, b: string) => b.compareTo(a)); - for (let i: number = 0; i < arrayList2.length; i++) { - assertEQ(arrayList2[i], array2[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList2.length; i++) { + arktest.assertEQ(arrayList2[i], array2[i], `Element at index ${i} should match`); } let arrayList3 = new ArrayList(); @@ -200,122 +200,122 @@ function testSortDifferentTypesDescending() { let array3 = arrayList3.convertToArray(); arrayList3.sort((a: boolean, b: boolean) => (b ? 1 : 0) - (a ? 1 : 0)); array3.sort((a: boolean, b: boolean) => (b ? 1 : 0) - (a ? 1 : 0)); - for (let i: number = 0; i < arrayList3.length; i++) { - assertEQ(arrayList3[i], array3[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList3.length; i++) { + arktest.assertEQ(arrayList3[i], array3[i], `Element at index ${i} should match`); } let arrayList4 = new ArrayList(); initializeArrayList(arrayList4, TestData.forObject); let array4 = arrayList4.convertToArray(); - arrayList4.sort((a: object, b: object): number => { + arrayList4.sort((a: object, b: object): double => { let sa = new String(a); let sb = new String(b); return sb.compareTo(sa); }); - array4.sort((a: object, b: object): number => { + array4.sort((a: object, b: object): double => { let sa = new String(a); let sb = new String(b); return sb.compareTo(sa); }); - for (let i: number = 0; i < arrayList4.length; i++) { - assertEQ(arrayList4[i], array4[i], `Element at index ${i} should match`); + for (let i: int = 0; i < arrayList4.length; i++) { + arktest.assertEQ(arrayList4[i], array4[i], `Element at index ${i} should match`); } } function testSubArrayList() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); initializeArrayList(arrayList, TestData.forNumber); let subArrayList = arrayList.subArrayList(TestData.testNumber1, TestData.testNumber5 - TestData.testNumber1); for (let i: int = 0; i < arrayList.length; i++) { - assertEQ(arrayList[i], TestData.forNumber[i], `Element at index ${i} should match`); + arktest.assertEQ(arrayList[i], TestData.forNumber[i], `Element at index ${i} should match`); } for (let i: int = 0; i < subArrayList.length; i++) { - assertEQ(subArrayList[i], TestData.result6[i], `Element at index ${i} should match`); + arktest.assertEQ(subArrayList[i], TestData.result6[i], `Element at index ${i} should match`); } } function testSubArrayListStartIndexOutOfRange() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); initializeArrayList(arrayList, TestData.forNumber); let exceptionCheck = (e: Error | Exception): boolean | string => { - return (e instanceof BusinessError) && ((e as BusinessError).message == + return (e instanceof BusinessError && (e as BusinessError).name == 'BusinessError') && ((e as BusinessError).message == "The value of \"fromIndex\" is out of range. It must be >= 0 && <= 4. Received value is: -1") && ((e as BusinessError).code == OutOfBoundsErrorCodeId); } - expectThrow(() => {arrayList.subArrayList(-TestData.testNumber1, TestData.testNumber5)}, exceptionCheck); + arktest.expectThrow(() => {arrayList.subArrayList(-TestData.testNumber1, TestData.testNumber5)}, exceptionCheck); } function testSubArrayListEndIndexOutOfRange() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); initializeArrayList(arrayList, TestData.forNumber); let exceptionCheck = (e: Error | Exception): boolean | string => { - return (e instanceof BusinessError) && ((e as BusinessError).message == + return (e instanceof BusinessError && (e as BusinessError).name == 'BusinessError') && ((e as BusinessError).message == "The value of \"toIndex\" is out of range. It must be >= 0 && <= 5. Received value is: 8") && ((e as BusinessError).code == OutOfBoundsErrorCodeId); } - expectThrow(() => {arrayList.subArrayList(TestData.testNumber1, TestData.testNumber8)}, exceptionCheck); + arktest.expectThrow(() => {arrayList.subArrayList(TestData.testNumber1, TestData.testNumber8)}, exceptionCheck); } function testSubArrayListEndIndexSmallerThanStartIndex() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); initializeArrayList(arrayList, TestData.forNumber); let exceptionCheck = (e: Error | Exception): boolean | string => { - return (e instanceof BusinessError) && ((e as BusinessError).message == + return (e instanceof BusinessError && (e as BusinessError).name == 'BusinessError') && ((e as BusinessError).message == "The value of \"fromIndex\" is out of range. It must be >= 0 && <= 0. Received value is: 5") && ((e as BusinessError).code == OutOfBoundsErrorCodeId); } - expectThrow(() => {arrayList.subArrayList(TestData.testNumber5, TestData.testNumber1)}, exceptionCheck); + arktest.expectThrow(() => {arrayList.subArrayList(TestData.testNumber5, TestData.testNumber1)}, exceptionCheck); } function testSubArrayListWithChange() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); initializeArrayList(arrayList, TestData.forNumber); let subArrayList = arrayList.subArrayList(TestData.testNumber1, TestData.testNumber5 - TestData.testNumber1); subArrayList.add(TestData.testNumber5); for (let i: int = 0; i < arrayList.length; i++) { - assertEQ(arrayList[i], TestData.forNumber[i], `Element at index ${i} should match`); + arktest.assertEQ(arrayList[i], TestData.forNumber[i], `Element at index ${i} should match`); } for (let i: int = 0; i < subArrayList.length; i++) { - assertEQ(subArrayList[i], TestData.result7[i], `Element at index ${i} should match`); + arktest.assertEQ(subArrayList[i], TestData.result7[i], `Element at index ${i} should match`); } } function testSubArrayListEmptyContainer() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); let exceptionCheck = (e: Error | Exception): boolean | string => { - return (e instanceof BusinessError) && ((e as BusinessError).message == "Container is empty") && + return (e instanceof BusinessError && (e as BusinessError).name == 'BusinessError') && ((e as BusinessError).message == "Container is empty") && ((e as BusinessError).code == OutOfBoundsErrorCodeId); } - expectThrow(() => {arrayList.subArrayList(TestData.testNumber1, TestData.testNumber3)}, exceptionCheck); + arktest.expectThrow(() => {arrayList.subArrayList(TestData.testNumber1, TestData.testNumber3)}, exceptionCheck); } function testSubArrayListFullRange() { - let arrayList = new ArrayList(); + let arrayList = new ArrayList(); initializeArrayList(arrayList, TestData.forNumber); let subArrayList = arrayList.subArrayList(0, TestData.testNumber5); - for (let i: number = 0; i < subArrayList.length; i++) { - assertEQ(subArrayList[i], arrayList[i], `Element at index ${i} should match`); + for (let i: int = 0; i < subArrayList.length; i++) { + arktest.assertEQ(subArrayList[i], arrayList[i], `Element at index ${i} should match`); } - assertEQ(subArrayList.length, TestData.testNumber5, "SubArrayList length should be 5"); + arktest.assertEQ(subArrayList.length, TestData.testNumber5, "SubArrayList length should be 5"); } function testArrayListJsonStringify() { - let arrayList: ArrayList | boolean | undefined | null | Object> = - new ArrayList | boolean | undefined | null | Object>(); + let arrayList: ArrayList | boolean | undefined | null | Object> = + new ArrayList | boolean | undefined | null | Object>(); arrayList.add(TestData.testString1); arrayList.add(TestData.testNumber1); arrayList.add(TestData.testObject); arrayList.add(undefined); arrayList.add(null); arrayList.add(TestData.testTrue); - arrayList.insert(TestData.testString2, TestData.testNumber0); - arrayList.removeByIndex(TestData.testNumber0); + arrayList.insert(TestData.testString2, TestData.testNumber0.toInt()); + arrayList.removeByIndex(TestData.testNumber0.toInt()); let jsonResult = JSON.stringify(arrayList); - assertEQ(jsonResult, TestData.result8, `jsonResult should be ${TestData.result8}`); + arktest.assertEQ(jsonResult, TestData.result8, `jsonResult should be ${TestData.result8}`); } function initializeArrayList(arrayList: ArrayList, sourceArray: T[]) { - for (let i = 0; i < sourceArray.length; i++) { + for (let i: int = 0; i < sourceArray.length; i++) { arrayList.add(sourceArray[i]); } } diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/Base64HelperTest.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/Base64HelperTest.ets index 85bdd846e341ad9e1ac19b1e3e63ee14aeb078cd..5810aa6d148d64381281244cf7dc828f69733a85 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/Base64HelperTest.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/Base64HelperTest.ets @@ -15,7 +15,9 @@ import {util} from "@ohos.util"; -function main(): void { +function main(): int { + const suite = new arktest.ArkTestsuite("Base64Helper Test"); + testEncodeDecodeBasic(); testEncodeDecodeBasicSync(); testUrlSafe(); @@ -26,21 +28,24 @@ function main(): void { testEmptyEncode(); testEmptyDecode(); testBase64WithoutPadding(); - testNoOptionsEncodeDecode(); - testNoOptionsEncodeDecodeSync() + suite.addTest("Test encodeSync with MIME and MINE_URL_SAFE", testEncodeSyncMineAndMineUrlSafe); + suite.addTest("Test encode and decode with no options", testNoOptionsEncodeDecode); + suite.addTest("Test encodeSync and decodeSync with no options", testNoOptionsEncodeDecodeSync); await testEncodeDecodeBasicAsync(); await testEncodeDecodeAsyncBytes(); await testUrlSafeAsync(); await testDecodeWithNoiseAsync(); - testNoOptionsEncodeDecodeAsync(); - testNoOptionsEncodeDecodeAsyncBytes(); + suite.addTest("Test async encode and decode with no options", testNoOptionsEncodeDecodeAsync); + suite.addTest("Test async encode and decode Bytes with no options", testNoOptionsEncodeDecodeAsyncBytes); + + return suite.run(); } function assertUint8ArraysEqual(actual: Uint8Array, expected: Uint8Array): void { - assertEQ(actual.length, expected.length); + arktest.assertEQ(actual.length, expected.length); for (let i = 0; i < expected.length; i++) { - assertEQ(actual[i], expected[i]); + arktest.assertEQ(actual[i], expected[i]); } } @@ -49,7 +54,7 @@ function testEncodeDecodeBasic() { let input = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" let encodedStr: String = helper.encodeToStringSync(input, util.Type.BASIC); let decoded: Uint8Array = helper.decodeSync(encodedStr, util.Type.BASIC); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -62,7 +67,7 @@ function testEncodeDecodeBasicSync() { encodedStr += String.fromCharCode(encodedBytes[i]); } let decoded: Uint8Array = helper.decodeSync(encodedStr, util.Type.BASIC); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -70,10 +75,10 @@ function testUrlSafe() { let helper = new util.Base64Helper(); let input = new Uint8Array([250, 251, 252, 253, 254, 255]); let encodedStr: String = helper.encodeToStringSync(input, util.Type.BASIC_URL_SAFE); - assertEQ(encodedStr.indexOf('+'), -1); - assertEQ(encodedStr.indexOf('/'),-1); + arktest.assertEQ(encodedStr.indexOf('+'), -1); + arktest.assertEQ(encodedStr.indexOf('/'),-1); let decoded: Uint8Array = helper.decodeSync(encodedStr, util.Type.BASIC_URL_SAFE); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -84,9 +89,9 @@ function testMime() { input[i] = i; } let encodedStr: String = helper.encodeToStringSync(input, util.Type.MIME); - assertNE(encodedStr.indexOf("\r\n"), -1.0); + arktest.assertNE(encodedStr.indexOf("\r\n"), -1.0); let decoded: Uint8Array = helper.decodeSync(encodedStr, util.Type.MIME); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -97,11 +102,11 @@ function testEncodeToStringSyncMime() { 32, 78, 111, 100, 101, 46, 106, 115, 105, 111, 110, 102, 114, 111, 109, 111, 116, 104, 101, 114, 97, 110, 105, 109, 97, 108, 115, 119, 104, 105, 99, 104, 105, 115, 97, 108]); let encodedStr: String = helper.encodeToStringSync(input, util.Type.MIME); - assertEQ(encodedStr.indexOf("\r\n"), 76); - assertEQ(encodedStr.lastIndexOf("\r\n"), 102); - assertEQ(encodedStr, "QmFzZTY0IE5vZGUuanM2NCBOb2RlLmpzczY0IE5vZGUuanNzczY0IE5vZGUuanNpb25mcm9tb3Ro\r\nZXJhbmltYWxzd2hpY2hpc2Fs\r\n"); + arktest.assertEQ(encodedStr.indexOf("\r\n"), 76); + arktest.assertEQ(encodedStr.lastIndexOf("\r\n"), 102); + arktest.assertEQ(encodedStr, "QmFzZTY0IE5vZGUuanM2NCBOb2RlLmpzczY0IE5vZGUuanNzczY0IE5vZGUuanNpb25mcm9tb3Ro\r\nZXJhbmltYWxzd2hpY2hpc2Fs\r\n"); let decoded: Uint8Array = helper.decodeSync(encodedStr, util.Type.MIME); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -112,11 +117,11 @@ function testMimeUrlSafe() { input[i] = i; } let encodedStr: string = helper.encodeToStringSync(input, util.Type.MIME_URL_SAFE); - assertNE(encodedStr.indexOf("\r\n"), -1.0); - assertEQ(encodedStr.indexOf("+"), -1); - assertEQ(encodedStr.indexOf("/"), -1); + arktest.assertNE(encodedStr.indexOf("\r\n"), -1.0); + arktest.assertEQ(encodedStr.indexOf("+"), -1); + arktest.assertEQ(encodedStr.indexOf("/"), -1); let decoded: Uint8Array = helper.decodeSync(encodedStr, util.Type.MIME_URL_SAFE); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -126,7 +131,7 @@ function testDecodeWithNoise() { let encodedStr: String = helper.encodeToStringSync(input, util.Type.BASIC); let noisy: String = "!!" + encodedStr + "##"; let decoded: Uint8Array = helper.decodeSync(noisy, util.Type.BASIC); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -134,16 +139,16 @@ function testEmptyEncode() { let helper = new util.Base64Helper(); let input = new Uint8Array(0); let encodedStr: string = helper.encodeToStringSync(input, util.Type.BASIC); - assertEQ(encodedStr, ""); + arktest.assertEQ(encodedStr, ""); let decoded: Uint8Array = helper.decodeSync(encodedStr, util.Type.BASIC); - assertEQ(decoded.length, 0); + arktest.assertEQ(decoded.length, 0); } function testEmptyDecode() { let helper = new util.Base64Helper(); let encodedStr = ""; let decoded: Uint8Array = helper.decodeSync(encodedStr, util.Type.BASIC); - assertEQ(decoded.length, 0); + arktest.assertEQ(decoded.length, 0); } function testBase64WithoutPadding() { @@ -153,7 +158,7 @@ function testBase64WithoutPadding() { let encodedWithoutPadding = encodedStr.replace(new RegExp("=+$"), ""); let decoded: Uint8Array = helper.decodeSync(encodedWithoutPadding, util.Type.BASIC); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -162,7 +167,7 @@ function testNoOptionsEncodeDecode(): void { let input = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" let encodedStr: String = helper.encodeToStringSync(input); let decoded: Uint8Array = helper.decodeSync(encodedStr); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -175,16 +180,27 @@ function testNoOptionsEncodeDecodeSync(): void { encodedStr += String.fromCharCode(encodedBytes[i]); } let decoded: Uint8Array = helper.decodeSync(encodedStr); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } +function testEncodeSyncMineAndMineUrlSafe():void{ + let helper = new util.Base64Helper(); + let input = new Uint8Array([115, 49, 51]); + arktest.expectError(() => { + helper.encodeSync(input, util.Type.MIME); + }, 'Parameter error. The target encoding type option must be BASIC or BASIC_URL_SAFE.'); + arktest.expectError(() => { + helper.encodeSync(input, util.Type.MIME_URL_SAFE); + }, 'Parameter error. The target encoding type option must be BASIC or BASIC_URL_SAFE.'); +} + async function testEncodeDecodeBasicAsync(): Promise { let helper = new util.Base64Helper(); let input = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" let encodedStr: string = await helper.encodeToString(input, util.Type.BASIC); let decoded: Uint8Array = await helper.decode(encodedStr, util.Type.BASIC); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -197,7 +213,7 @@ async function testEncodeDecodeAsyncBytes(): Promise { encodedStr += String.fromCharCode(encodedBytes[i]); } let decoded: Uint8Array = await helper.decode(encodedStr, util.Type.BASIC); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -205,10 +221,10 @@ async function testUrlSafeAsync(): Promise { let helper = new util.Base64Helper(); let input = new Uint8Array([250, 251, 252, 253, 254, 255]); let encodedStr: string = await helper.encodeToString(input, util.Type.BASIC_URL_SAFE); - assertEQ(encodedStr.indexOf('+'), -1); - assertEQ(encodedStr.indexOf('/'), -1); + arktest.assertEQ(encodedStr.indexOf('+'), -1); + arktest.assertEQ(encodedStr.indexOf('/'), -1); let decoded: Uint8Array = await helper.decode(encodedStr, util.Type.BASIC_URL_SAFE); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -218,7 +234,7 @@ async function testDecodeWithNoiseAsync(): Promise { let encodedStr: string = await helper.encodeToString(input, util.Type.BASIC); let noisy: string = "!!" + encodedStr + "##"; let decoded: Uint8Array = await helper.decode(noisy, util.Type.BASIC); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -227,7 +243,7 @@ function testNoOptionsEncodeDecodeAsync(): void { let input = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" let encodedStr: string = await helper.encodeToString(input); let decoded: Uint8Array = await helper.decode(encodedStr); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } @@ -240,6 +256,6 @@ function testNoOptionsEncodeDecodeAsyncBytes(): void { encodedStr += String.fromCharCode(encodedBytes[i]); } let decoded: Uint8Array = await helper.decode(encodedStr); - assertEQ(decoded.length, input.length); + arktest.assertEQ(decoded.length, input.length); assertUint8ArraysEqual(decoded, input); } diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/FormatTest.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/FormatTest.ets index 9910eee6f68bf85c7c1be3f0025e542776c6c7d3..2c34753dc4a5751479de7b4e2866f12a9a4a71ba 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/FormatTest.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/FormatTest.ets @@ -56,33 +56,33 @@ function testUtilFormat(): int { const i: int = 35 const fp : float = 1.7320534f - assertEQ(util.format("String: %s", s), "String: computer") - assertEQ(util.format("%s", s), "computer") - assertEQ(util.format("%s String", s), "computer String") - assertEQ(util.format("%s ", s), "computer ") - assertEQ(util.format(" %s", s), " computer") - assertEQ(util.format("%%s", s), "%s computer") - assertEQ(util.format("%%s%", s), "%s% computer") - assertEQ(util.format("s%", s), "s% computer") - assertEQ(util.format("%s%", s), "computer%") + arktest.assertEQ(util.format("String: %s", s), "String: computer") + arktest.assertEQ(util.format("%s", s), "computer") + arktest.assertEQ(util.format("%s String", s), "computer String") + arktest.assertEQ(util.format("%s ", s), "computer ") + arktest.assertEQ(util.format(" %s", s), " computer") + arktest.assertEQ(util.format("%%s", s), "%s computer") + arktest.assertEQ(util.format("%%s%", s), "%s% computer") + arktest.assertEQ(util.format("s%", s), "s% computer") + arktest.assertEQ(util.format("%s%", s), "computer%") - assertEQ(util.format(" Integer: %d\n", i), " Integer: 35\n") - assertEQ(util.format(" Real: %f\n", fp), " Real: 1.7320534\n") + arktest.assertEQ(util.format(" Integer: %d\n", i), " Integer: 35\n") + arktest.assertEQ(util.format(" Real: %f\n", fp), " Real: 1.7320534\n") - assertEQ(util.format("String: %s int %d !!! %d %f %d", s, 1, 2, 3, 4), "String: computer int 1 !!! 2 3 4") // String: computer int 1 !!! 2 3.0 4 ??? + arktest.assertEQ(util.format("String: %s int %d !!! %d %f %d", s, 1, 2, 3, 4), "String: computer int 1 !!! 2 3 4") // String: computer int 1 !!! 2 3.0 4 ??? return success } function testFormatLessArgsThanInFormat(): int { let res = util.format("example %d of invalid %f format string", 1) - assertEQ(res, "example 1 of invalid %f format string") + arktest.assertEQ(res, "example 1 of invalid %f format string") return success } function testFormatIncorrectArgs(): int { let res = util.format("example %X of invalid format string", 1) - assertEQ(res, "example %X of invalid format string 1") + arktest.assertEQ(res, "example %X of invalid format string 1") return success } @@ -90,19 +90,19 @@ function testFormatNoMatchArgs(): int { { // NOTE(ekaterinazaytseva): converting string to int -> NaN let res = util.format("example %s of invalid %d format string %d", "string", 1, "string instead int") - assertEQ(res, "example string of invalid 1 format string NaN") + arktest.assertEQ(res, "example string of invalid 1 format string NaN") } { let res = util.format("example %s of invalid %d format string %o", "string", 1, "string as object") - assertEQ(res, "example string of invalid 1 format string 'string as object'") + arktest.assertEQ(res, "example string of invalid 1 format string 'string as object'") } return success } function testFormatMoreArgsThanInUtilFormat(): int { let res = util.format("Text: %s", "ddd", 44, "one more time text") - assertEQ(res, "Text: ddd 44 one more time text") + arktest.assertEQ(res, "Text: ddd 44 one more time text") return success } @@ -121,7 +121,7 @@ function testFormatRemoveCSS(): int { Content' let res = util.format("without css: %c", text) - assertEQ(res, "without css: Content") + arktest.assertEQ(res, "without css: Content") return success } @@ -132,13 +132,13 @@ class TestObject1 { function testFormatJSON(): int { // simple JSON - assertEQ(util.format("JSON : %j", new TestObject1()), "JSON : {\"x\":5,\"y\":6}") + arktest.assertEQ(util.format("JSON : %j", new TestObject1()), "JSON : {\"x\":5,\"y\":6}") // JSON with circular reference let jsonWithCircularRef : string = "{\"name\":\"Kris\", \"father\":{\"name\":\"Bill\",\"wife\":{\"name\":\"Karen\"}},\"mother\":{\"$ref\":\"#father.wife\"}}" let expected = "JSON : \"{\\\"name\\\":\\\"Kris\\\", \\\"father\\\":{\\\"name\\\":\\\"Bill\\\",\\\"wife\\\":{\\\"name\\\":\\\"Karen\\\"}},\\\"mother\\\":{\\\"$ref\\\":\\\"#father.wife\\\"}}\""; let res = util.format("JSON : %j", jsonWithCircularRef) - assertEQ(res, expected) + arktest.assertEQ(res, expected) return success } @@ -157,14 +157,14 @@ function testFormatObject(): int { let res = util.format("Object: %o", new TestObject2("k28", 14, 0.1f)) let expected = "Object: { strField: 'k28',\nintField: 14,\nfloatField: 0.1,\nboolField: false }" - assertEQ(res, expected) + arktest.assertEQ(res, expected) return success } function testFormatObject2(): int { let res = util.format("Object: %O", new TestObject2("k28", 14, 0.1f)) let expected = "Object: { strField: 'k28',\nintField: 14,\nfloatField: 0.1,\nboolField: false }" - assertEQ(res, expected) + arktest.assertEQ(res, expected) return success } @@ -181,10 +181,10 @@ function testFormatCircularObject(): int { let expected = "{ prop: 'Circular Reference Example',\nreference: [Circular]\n }" let res = util.format("%o", circularObject) - assertEQ(res, expected) + arktest.assertEQ(res, expected) res = util.format("%O", circularObject) - assertEQ(res, expected) + arktest.assertEQ(res, expected) return success } @@ -202,35 +202,35 @@ function testArrayOfCircularObjects() : int { let expected1 = "[ { prop: 'Circular Reference Example',\n reference: [Circular]\n }, \n{ prop: 'Circular Reference Example',\n reference: [Circular]\n } ]" let res = util.format("%o", arr) - assertEQ(res, expected) + arktest.assertEQ(res, expected) res = util.format("%O", arr) - assertEQ(res, expected1) + arktest.assertEQ(res, expected1) return success } function testEmptyFormatWithArgs(): int { let res = util.format("", 1, 2, 3, "test") - assertEQ(res, "1 2 3 test") + arktest.assertEQ(res, "1 2 3 test") return success } function testNonEmptyFormatWithArgs(): int { let res = util.format("Some important text!", 1, 2, 3, "test") - assertEQ(res, "Some important text! 1 2 3 test") + arktest.assertEQ(res, "Some important text! 1 2 3 test") return success } function testFormatWithoutArgs(): int { let res = util.format("%d %s lalala %d %d") - assertEQ(res, "%d %s lalala %d %d") + arktest.assertEQ(res, "%d %s lalala %d %d") return success } function testFormatEscapePercent(): int { let res = util.format("%% lalala %%%%") - assertEQ(res, "% lalala %%") + arktest.assertEQ(res, "% lalala %%") return success } @@ -244,8 +244,8 @@ function testFormatArrayObect(): int { { let arr = new Array() - assertEQ(util.format('Array: %o', arr), "Array: [ [length]: 0 ]") - assertEQ(util.format('Array: %O', arr), "Array: [ ]") + arktest.assertEQ(util.format('Array: %o', arr), "Array: [ [length]: 0 ]") + arktest.assertEQ(util.format('Array: %O', arr), "Array: [ ]") } { let arr = new Array(1) @@ -254,8 +254,8 @@ function testFormatArrayObect(): int { let expected = "Array: [ { f1: '',\n k1: 8,\n t: false }[length]: 1 ]" let expected1 = "Array: [ { f1: '',\n k1: 8,\n t: false } ]" - assertEQ(util.format('Array: %o', arr), expected) - assertEQ(util.format('Array: %O', arr), expected1) + arktest.assertEQ(util.format('Array: %o', arr), expected) + arktest.assertEQ(util.format('Array: %O', arr), expected1) } return success @@ -268,8 +268,8 @@ function testFunctionAsObject() : int { let expected = "Function: { [Function: null] [length]: 0, [name] :'null', [prototype]: null { [constructor]: [Circular] } }" let expected1 = "Function: { [Function: null] }" - assertEQ(util.format('Function: %o', func), expected) - assertEQ(util.format('Function: %O', func), expected1) + arktest.assertEQ(util.format('Function: %o', func), expected) + arktest.assertEQ(util.format('Function: %O', func), expected1) return success } @@ -283,8 +283,8 @@ function testFunctionsArrayObject() : int { let expected = "Array of functions: [ { [Function: null] [length]: 0, [name] :'null', [prototype]: null { [constructor]: [Circular] } }[length]: 1 ]" let expected1 = "Array of functions: [ [Function: null] ]" - assertEQ(util.format('Array of functions: %o', arr), expected) - assertEQ(util.format('Array of functions: %O', arr), expected1) + arktest.assertEQ(util.format('Array of functions: %o', arr), expected) + arktest.assertEQ(util.format('Array of functions: %O', arr), expected1) return success } @@ -318,19 +318,19 @@ function testApiV1Tests() : int { let name = 'John'; let age = 20; let formattedString = util.format('My name is %s and I am %s years old', name, age); - assertEQ(formattedString, "My name is John and I am 20 years old"); + arktest.assertEQ(formattedString, "My name is John and I am 20 years old"); let num = 10.5; formattedString = util.format('The number is %d', num); - assertEQ(formattedString, "The number is 10.5"); + arktest.assertEQ(formattedString, "The number is 10.5"); num = 100.5; formattedString = util.format('The number is %i', num); - assertEQ(formattedString, "The number is 100"); + arktest.assertEQ(formattedString, "The number is 100"); const pi = 3.141592653; formattedString = util.format('The value of pi is %f', pi); - assertEQ(formattedString, "The value of pi is 3.141592653"); + arktest.assertEQ(formattedString, "The value of pi is 3.141592653"); const person: UtilPersontype = { name: 'John', @@ -343,13 +343,13 @@ function testApiV1Tests() : int { let expected1 = "Formatted object using %O: { name: 'John',\n\age: 20,\n\address:\n\{ city: 'New York',\n\ country: 'USA' } }" let expected2 = "Formatted object using %o: { name: 'John',\n\age: 20,\n\address:\n\{ city: 'New York',\n\ country: 'USA' } }" - assertEQ(util.format('Formatted object using %%O: %O', person), expected1); - assertEQ(util.format('Formatted object using %%o: %o', person), expected2); + arktest.assertEQ(util.format('Formatted object using %%O: %O', person), expected1); + arktest.assertEQ(util.format('Formatted object using %%o: %o', person), expected2); const percentage = 80; let arg = 'homework'; formattedString = util.format('John finished %d%% of the %s', percentage, arg); - assertEQ(formattedString, "John finished 80% of the homework") + arktest.assertEQ(formattedString, "John finished 80% of the homework") return success } diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/HashMap/HashMapClearKeysValuesReplaceTest.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/HashMap/HashMapClearKeysValuesReplaceTest.ets index 8e8bdbbafd7c10608152c7ec2e67e992ddcbfe30..3a41885c790a857f56a05b7ed13971a73931fadc 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/HashMap/HashMapClearKeysValuesReplaceTest.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/HashMap/HashMapClearKeysValuesReplaceTest.ets @@ -45,7 +45,7 @@ function main(): int { //Test cases ported from ArkTS 1.0:HASHMAP_CLEAR_0100 function testClearEmptyMap() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.clear(); arktest.assertEQ(hashMap.length, 0); arktest.assertEQ(hashMap.get('ji'), undefined); @@ -54,7 +54,7 @@ function testClearEmptyMap() { //Test cases ported from ArkTS 1.0:HASHMAP_CLEAR_0200 function testClearPopulatedMap() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set("ji", 12); hashMap.clear(); arktest.assertEQ(hashMap.length, 0); @@ -64,7 +64,7 @@ function testClearPopulatedMap() { //Test cases ported from ArkTS 1.0:HASHMAP_CLEAR_0300 function testClearSetSameKey() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set("ji", 12); hashMap.clear(); hashMap.set("ji", 13); @@ -72,7 +72,7 @@ function testClearSetSameKey() { } function testClearSetNewKey() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set("ji", 12); hashMap.clear(); hashMap.set("sparrow", 13); @@ -81,14 +81,14 @@ function testClearSetNewKey() { //Test cases ported from ArkTS 1.0:HASHMAP_KEYS_0100 function testKeysEmpty() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let keys = hashMap.keys(); arktest.assertTrue(keys.next().done, "Empty HashMap: keys iterator .next().done is true"); } //Test cases ported from ArkTS 1.0:HASHMAP_KEYS_0200 function testKeysPopulated() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 1); hashMap.set(1, 2); hashMap.set(2, 3); @@ -103,7 +103,7 @@ function testKeysPopulated() { } function testKeysEmptyThenPopulated() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let result = hashMap.keys(); arktest.assertTrue(result.next().done, "Empty HashMap: keys iterator .next().done is true"); @@ -122,14 +122,14 @@ function testKeysEmptyThenPopulated() { //Test cases ported from ArkTS 1.0:HASHMAP_VALUES_0100 function testValuesEmpty() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let keys = hashMap.values(); arktest.assertTrue(keys.next().done, "values().next().done should be true for an empty HashMap"); } //Test cases ported from ArkTS 1.0:HASHMAP_VALUES_0200 function testValuesPopulated() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 1); hashMap.set(1, 2); hashMap.set(2, 3); @@ -144,7 +144,7 @@ function testValuesPopulated() { } function testValuesEmptyThenPopulated() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let result = hashMap.values(); arktest.assertTrue(result.next().done, "values().next().done should be true for an empty HashMap"); @@ -163,33 +163,33 @@ function testValuesEmptyThenPopulated() { //Test cases ported from ArkTS 1.0:HASHMAP_REPLACE_0100 function testReplaceEmptyMapEmptyKey() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); arktest.assertFalse(hashMap.replace('', 1), "Replacing non-existent empty string key in empty map should fail"); } //Test cases ported from ArkTS 1.0:HASHMAP_REPLACE_0400 function testReplaceEmptyMapNonExistKey() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); arktest.assertFalse(hashMap.replace('key', 1), "Replacing non-existent 'key' in empty map should fail"); } //Test cases ported from ArkTS 1.0:HASHMAP_REPLACE_0500 function testReplaceExistKeySameValue() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set("ji", 1); arktest.assertTrue(hashMap.replace('ji', 1), "Replacing existing key with the same value should succeed"); } //Test cases ported from ArkTS 1.0:HASHMAP_REPLACE_0600 function testReplaceExistKeyDiffValue() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set("ji", 1); arktest.assertTrue(hashMap.replace('ji', 2), "Replacing existing key with a different value should succeed"); } //Test cases ported from ArkTS 1.0:HASHMAP_REPLACE_0700 function testReplaceNonExistNumKey() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(2, 1); arktest.assertFalse(hashMap.replace(1, 2), "Replacing non-existent numeric key in populated map should fail"); } @@ -198,7 +198,7 @@ class Tmp { name: number = 0 } function testReplaceExistNumKeyObjValue() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let paramOne: Tmp = { name: 1 }; @@ -232,7 +232,7 @@ class Age { age: number = 0 } function testReplaceNonExistObjKey() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let paramOne: Age = { age: 18 }; diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/HashMap/HashMapEntriesIteratorForeachTest.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/HashMap/HashMapEntriesIteratorForeachTest.ets index 5e483389c1045b03d9d0d7afe4f5639241d36a3f..15669c455f4ec641ef0961833a9fb5d3e50651d2 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/HashMap/HashMapEntriesIteratorForeachTest.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/HashMap/HashMapEntriesIteratorForeachTest.ets @@ -43,7 +43,7 @@ function main(): int { //Test cases ported from ArkTS 1.0:HASHMAP_ENTRIES_0100 function testEntriesEmptyMap() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let entries = hashMap.entries(); let temp = entries.next(); arktest.assertTrue(temp.done, "Expected iterator to be done for an empty set"); @@ -55,7 +55,7 @@ function testEntriesEmptyMap() { //Test cases ported from ArkTS 1.0:HASHMAP_ENTRIES_0200 function testEntriesPopulatedMapIterate() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); @@ -72,7 +72,7 @@ function testEntriesPopulatedMapIterate() { } //Test cases ported from ArkTS 1.0:HASHMAP_ENTRIES_0300 function testEntriesPopulatedMapRemoveIter() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); @@ -86,7 +86,7 @@ function testEntriesPopulatedMapRemoveIter() { //Test cases ported from ArkTS 1.0:HASHMAP_ENTRIES_0400 function testEntriesPopulatedMapReplaceIter() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); @@ -103,7 +103,7 @@ function testEntriesPopulatedMapReplaceIter() { //Test cases ported from ArkTS 1.0:HASHMAP_ENTRIES_0500 function testEntriesPopulatedMapClearIter() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); @@ -117,7 +117,7 @@ function testEntriesPopulatedMapClearIter() { } function testEntriesPopulatedMapAddIter() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); @@ -140,7 +140,7 @@ function testEntriesPopulatedMapAddIter() { //Test cases ported from ArkTS 1.0:HASHMAP_SYMBOL_ITERATOR_0100 function testSymbolIteratorEmpty() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let iter = hashMap.$_iterator(); let temp = iter.next(); arktest.assertTrue(temp.done, "Expected iterator to be done for an empty set"); @@ -152,7 +152,7 @@ function testSymbolIteratorEmpty() { //Test cases ported from ArkTS 1.0:HASHMAP_SYMBOL_ITERATOR_0200 function testSymbolIteratorPopulated() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); @@ -170,20 +170,20 @@ function testSymbolIteratorPopulated() { //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0100 function testForeachEmptyMap() { - const hashMap = new HashMap(); - hashMap.forEach((value?: number, key?: number) => { + const hashMap = new HashMap(); + hashMap.forEach((value?: double, key?: double) => { arktest.assertEQ(hashMap.length, 0); }); } //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0200 function testForeachPopulatedMap() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); let index = 0; - hashMap.forEach((value?: number, key?: number) => { + hashMap.forEach((value?: double, key?: double) => { arktest.assertEQ(key, index); arktest.assertEQ(value, index); index++; @@ -193,22 +193,22 @@ function testForeachPopulatedMap() { //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0300 function testForeachPopulatedMapRemoveCb() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); - hashMap.forEach((value: number, key: number) => { + hashMap.forEach((value: double, key: double) => { arktest.assertEQ(hashMap.remove(key), value); }); } //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0400 function testForeachPopulatedMapReplaceCb() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); - hashMap.forEach((value: number, key: number) => { + hashMap.forEach((value: double, key: double) => { arktest.assertTrue(hashMap.replace(key, value ? value + 1 : 0), "Expected replace(key, value ? value + 1 : 0) to return true during forEach"); }); @@ -216,11 +216,11 @@ function testForeachPopulatedMapReplaceCb() { //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0500 function testForeachPopulatedMapClearCb() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); - hashMap.forEach((value?: number, key?: number) => { + hashMap.forEach((value?: double, key?: double) => { hashMap.clear(); arktest.assertEQ(hashMap.length, 0); }); @@ -228,7 +228,7 @@ function testForeachPopulatedMapClearCb() { //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0600 function testForeachCbNoParams() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.set(1, 1); hashMap.set(2, 2); @@ -239,7 +239,7 @@ function testForeachCbNoParams() { //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0700 function testForeachCbOneParam() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); hashMap.forEach((value) => { arktest.assertEQ(hashMap.length, 1); @@ -249,8 +249,8 @@ function testForeachCbOneParam() { //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0800 function testForeachWithThis() { - const hashMap = new HashMap(); - const hashMapNew = new HashMap(); + const hashMap = new HashMap(); + const hashMapNew = new HashMap(); hashMap.set(0, 0); hashMapNew.set(0, 0); hashMap.forEach((value) => { @@ -263,9 +263,9 @@ function testForeachWithThis() { //Test cases ported from ArkTS 1.0:HASHMAP_FOREACH_0900 function testForeachCbThreeParams() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); hashMap.set(0, 0); - hashMap.forEach((value, index, hashMap: HashMap) => { + hashMap.forEach((value, index, hashMap: HashMap) => { arktest.assertEQ(hashMap.length, 1); arktest.assertEQ(value, 0); }); @@ -277,7 +277,7 @@ class Temp { } //Test cases ported from ArkTS 1.0:HASHMAP_CUSTOM_0100 function testCustomTypeOps() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); let temp: Temp = { age: 18, name: "zhang san" }; @@ -290,7 +290,7 @@ function testCustomTypeOps() { age: 31, name: "wang wu" }; hashMap.set(2, temp2); - const newHashMap = new HashMap(); + const newHashMap = new HashMap(); let temp3: Temp = { age: 32, name: "zhao liu" }; @@ -310,7 +310,7 @@ function testCustomTypeOps() { } function testCustomLengthChanges() { - const hashMap = new HashMap(); + const hashMap = new HashMap(); arktest.assertEQ(hashMap.length, 0); hashMap.set(0, 0); hashMap.set(1, 1); @@ -320,7 +320,7 @@ function testCustomLengthChanges() { hashMap.set(2, 3); arktest.assertEQ(hashMap.length, 3); - const newHashMap = new HashMap(); + const newHashMap = new HashMap(); arktest.assertEQ(newHashMap.length, 0); newHashMap.setAll(hashMap); diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LRUCacheExceptionTest.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LRUCacheExceptionTest.ets index db68dc1e1c4d566a491d68540a92d40f6e6efcf8..66dc86fb3087eacd0bb371ed4f58a7793e2cef45 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LRUCacheExceptionTest.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LRUCacheExceptionTest.ets @@ -20,7 +20,7 @@ const success = 0 const fail = 1 function main(): int { - const suite = new ArkTestsuite("LRUCache API Exception tests") + const suite = new arktest.ArkTestsuite("LRUCache API Error tests") suite.addTest("LRUCache Constructor Long", testLRUCacheConstructor_Long); suite.addTest("LRUCache Constructor Decimal", testLRUCacheConstructor_Decimal); @@ -41,10 +41,10 @@ function testLRUCacheUpdateGetCapacity_Long() { try { that.updateCapacity(2147483648); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is not int") + arktest.assertEQ(err.message, "Parameter error. The type of 2147483648 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is not int"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is not int"); } function testLRUCacheUpdateGetCapacity_Decimal() { @@ -52,10 +52,10 @@ function testLRUCacheUpdateGetCapacity_Decimal() { try { that.updateCapacity(2.5); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is not int") + arktest.assertEQ(err.message, "Parameter error. The type of 2.5 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is not int"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is not int"); } function testLRUCacheUpdateGetCapacity_Zero() { @@ -63,10 +63,10 @@ function testLRUCacheUpdateGetCapacity_Zero() { try { that.updateCapacity(0); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is not int") + arktest.assertEQ(err.message, "Parameter error. The type of 0 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is not int"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is not int"); } function testLRUCacheUpdateGetCapacity_Negative() { @@ -74,37 +74,37 @@ function testLRUCacheUpdateGetCapacity_Negative() { try { that.updateCapacity(-1); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is negative") + arktest.assertEQ(err.message, "Parameter error. The type of -1 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is negative"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is negative"); } function testLRUCacheConstructor_Long() { try { let that = new util.LRUCache(2147483648); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is not int") + arktest.assertEQ(err.message, "Parameter error. The type of 2147483648 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is not int"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is not int"); } function testLRUCacheConstructor_Decimal() { try { let that = new util.LRUCache(0.1); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is not int") + arktest.assertEQ(err.message, "Parameter error. The type of 0.1 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is not int"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is not int"); } function testLRUCacheConstructor_Zero() { try { let that = new util.LRUCache(0); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is not int") + arktest.assertEQ(err.message, "Parameter error. The type of 0 must be small integer") return } } @@ -113,20 +113,20 @@ function testLRUCacheConstructor_Negative() { try { let that = new util.LRUCache(-1); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is negative") + arktest.assertEQ(err.message, "Parameter error. The type of -1 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is negative"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is negative"); } function testLRUCacheConstructor_NumberMin() { try { let that = new util.LRUCache(Double.MIN_VALUE); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is not int") + arktest.assertEQ(err.message, "Parameter error. The type of 5e-324 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is not int"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is not int"); } function testLRUCacheUpdateGetCapacity_NumberMin() { @@ -134,8 +134,8 @@ function testLRUCacheUpdateGetCapacity_NumberMin() { try { that.updateCapacity(Double.MIN_VALUE); } catch (err: BusinessError) { - assertEQ(err.code, 401, "LRUCache should throw an exception when capacity is not int") + arktest.assertEQ(err.message, "Parameter error. The type of 5e-324 must be small integer") return } - assertTrue(false, "LRUCache should throw an exception when capacity is not int"); + arktest.assertTrue(false, "LRUCache should throw an error when capacity is not int"); } diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapHasAllHasKeyHasValueTest.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapHasAllHasKeyHasValueTest.ets index 5d6507234dd40a0dd032b98888929e284d34bd84..54cbc433eea2955ab7f91667a86f52a00808da73 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapHasAllHasKeyHasValueTest.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapHasAllHasKeyHasValueTest.ets @@ -15,11 +15,8 @@ import LightWeightMap from "@ohos.util.LightWeightMap"; -const success = 0; -const fail = 1; - function main(): int { - const suite = new ArkTestsuite("LightWeightMap HasAll HasKey HasValue API tests") + const suite = new arktest.ArkTestsuite("LightWeightMap HasAll HasKey HasValue API tests") suite.addTest("LightWeightMap hasAll the map1 has all data of the map2", testHasAllWithAllData) @@ -44,82 +41,82 @@ function main(): int { } function testHasAllWithAllData() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", 123); lightWeightMap.set("sparrow", 356); - let newLightWeightMap: LightWeightMap = new LightWeightMap(); + let newLightWeightMap: LightWeightMap = new LightWeightMap(); newLightWeightMap.set("sparrow", 356); let result = lightWeightMap.hasAll(newLightWeightMap); - assertEQ(result, true, "The lightWeightMap should contain all data from another lightWeightMap"); - assertEQ(lightWeightMap.length, 2, "The lightWeightMap length should be 2"); - assertEQ(newLightWeightMap.length, 1, "The newLightWeightMap length should be 1"); + arktest.assertEQ(result, true, "The lightWeightMap should contain all data from another lightWeightMap"); + arktest.assertEQ(lightWeightMap.length, 2, "The lightWeightMap length should be 2"); + arktest.assertEQ(newLightWeightMap.length, 1, "The newLightWeightMap length should be 1"); } function testHasAllWithNotSetData() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", 123); lightWeightMap.set("sparrow", 356); - let newLightWeightMap: LightWeightMap = new LightWeightMap(); + let newLightWeightMap: LightWeightMap = new LightWeightMap(); newLightWeightMap.set("test", 777); let result = lightWeightMap.hasAll(newLightWeightMap); - assertEQ(result, false, "The lightWeightMap should not contain data that are not set in another lightWeightMap"); - assertEQ(lightWeightMap.length, 2, "The lightWeightMap length should be 2"); - assertEQ(newLightWeightMap.length, 1, "The newLightWeightMap length should be 1"); + arktest.assertEQ(result, false, "The lightWeightMap should not contain data that are not set in another lightWeightMap"); + arktest.assertEQ(lightWeightMap.length, 2, "The lightWeightMap length should be 2"); + arktest.assertEQ(newLightWeightMap.length, 1, "The newLightWeightMap length should be 1"); } function testHasAllWithOutOfRangeData() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", 123); lightWeightMap.set("sparrow", 356); - let newLightWeightMap: LightWeightMap = new LightWeightMap(); + let newLightWeightMap: LightWeightMap = new LightWeightMap(); newLightWeightMap.set("test", 777); newLightWeightMap.set("squirrel", 123); newLightWeightMap.set("sparrow", 356); let result = lightWeightMap.hasAll(newLightWeightMap); - assertEQ(result, false, + arktest.assertEQ(result, false, "The lightWeightMap should not contain data that are out of range in another lightWeightMap"); - assertEQ(lightWeightMap.length, 2, "The lightWeightMap length should be 2"); - assertEQ(newLightWeightMap.length, 3, "The newLightWeightMap length should be 3"); + arktest.assertEQ(lightWeightMap.length, 2, "The lightWeightMap length should be 2"); + arktest.assertEQ(newLightWeightMap.length, 3, "The newLightWeightMap length should be 3"); } function testHasAllWithEmptyMap() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", 123); lightWeightMap.set("sparrow", 356); - let newLightWeightMap: LightWeightMap = new LightWeightMap(); + let newLightWeightMap: LightWeightMap = new LightWeightMap(); let result = lightWeightMap.hasAll(newLightWeightMap); - assertEQ(result, true, "The lightWeightMap should contain all data from an empty lightWeightMap"); - assertEQ(lightWeightMap.length, 2, "The lightWeightMap length should be 2"); - assertEQ(newLightWeightMap.length, 0, "The newLightWeightMap length should be 0"); + arktest.assertEQ(result, true, "The lightWeightMap should contain all data from an empty lightWeightMap"); + arktest.assertEQ(lightWeightMap.length, 2, "The lightWeightMap length should be 2"); + arktest.assertEQ(newLightWeightMap.length, 0, "The newLightWeightMap length should be 0"); } function testHasKeyAndValueWithExisting() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", 123); let result1 = lightWeightMap.hasKey("squirrel"); let result2 = lightWeightMap.hasValue(123); - assertEQ(result1, true, "The lightWeightMap should contain the key"); - assertEQ(result2, true, "The lightWeightMap should contain the value"); - assertEQ(lightWeightMap.length, 1, "LightWeightMap length should be 1"); + arktest.assertEQ(result1, true, "The lightWeightMap should contain the key"); + arktest.assertEQ(result2, true, "The lightWeightMap should contain the value"); + arktest.assertEQ(lightWeightMap.length, 1, "LightWeightMap length should be 1"); } function testHasKeyAndValueWithNonExisting() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", 123); let result1 = lightWeightMap.hasKey("sparrow"); let result2 = lightWeightMap.hasValue(456); - assertEQ(result1, false, "The lightWeightMap should not contain the key that is not set"); - assertEQ(result2, false, "The lightWeightMap should not contain the value that is not set"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result1, false, "The lightWeightMap should not contain the key that is not set"); + arktest.assertEQ(result2, false, "The lightWeightMap should not contain the value that is not set"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } function testHasKeyAndValueWithEmptyMap() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); let result1 = lightWeightMap.hasKey("squirrel"); let result2 = lightWeightMap.hasValue(123); - assertEQ(result1, false, "The lightWeightMap should not contain any key when the lightWeightMap is empty"); - assertEQ(result2, false, "The lightWeightMap should not contain any value when the lightWeightMap is empty"); - assertEQ(lightWeightMap.length, 0, "The lightWeightMap length should be 0"); + arktest.assertEQ(result1, false, "The lightWeightMap should not contain any key when the lightWeightMap is empty"); + arktest.assertEQ(result2, false, "The lightWeightMap should not contain any value when the lightWeightMap is empty"); + arktest.assertEQ(lightWeightMap.length, 0, "The lightWeightMap length should be 0"); } function testHasKeyAndValueWithBlank() { @@ -127,9 +124,9 @@ function testHasKeyAndValueWithBlank() { lightWeightMap.set('squirrel', 'sparrow'); let result1 = lightWeightMap.hasKey(' squirrel '); let result2 = lightWeightMap.hasValue(' sparrow '); - assertEQ(result1, false, "The lightWeightMap should not contain the key"); - assertEQ(result2, false, "The lightWeightMap should not contain the value"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result1, false, "The lightWeightMap should not contain the key"); + arktest.assertEQ(result2, false, "The lightWeightMap should not contain the value"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } interface ComplexValue { @@ -154,7 +151,7 @@ function testHasKeyAndValueWithComplex() { lightWeightMap.set(key, value); let result1 = lightWeightMap.hasKey(key); let result2 = lightWeightMap.hasValue(value); - assertEQ(result1, true, "The lightWeightMap should contain the key"); - assertEQ(result2, true, "The lightWeightMap should contain the value"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result1, true, "The lightWeightMap should contain the key"); + arktest.assertEQ(result2, true, "The lightWeightMap should contain the value"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapIteratorGetIndexOfKeyGetIndexOfValueTest.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapIteratorGetIndexOfKeyGetIndexOfValueTest.ets index eba5ff495966baf33146a2f278863acdfb773059..ecf123327abb4916520e47ac8e5b506a1daf0f04 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapIteratorGetIndexOfKeyGetIndexOfValueTest.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapIteratorGetIndexOfKeyGetIndexOfValueTest.ets @@ -15,11 +15,8 @@ import LightWeightMap from "@ohos.util.LightWeightMap"; -const success = 0; -const fail = 1; - function main(): int { - const suite = new ArkTestsuite("LightWeightMap Iterator GetIndexOfKey GetIndexOfValue API tests") + const suite = new arktest.ArkTestsuite("LightWeightMap Iterator GetIndexOfKey GetIndexOfValue API tests") suite.addTest("LightWeightMap $_iteraor return iterator", testIteratorReturnsIterator) @@ -42,7 +39,7 @@ function main(): int { } function testIteratorReturnsIterator() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("sparrow", 123); lightWeightMap.set("gull", 357); lightWeightMap.set("test", 111); @@ -56,56 +53,56 @@ function testIteratorReturnsIterator() { } } let strAll = "gull,357,test,111,apply,222,sparrow,123"; - assertEQ(str, strAll, "The lightWeightMap $_iterator should return the correct key-value pairs"); + arktest.assertEQ(str, strAll, "The lightWeightMap $_iterator should return the correct key-value pairs"); } function testIteratorWithEmptyMap() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); let iteratorSetValues = lightWeightMap.$_iterator(); - assertEQ(iteratorSetValues.next().value, undefined, - "The lightWeightMap $_iterator should return undefined for an empty map"); + arktest.assertTrue(iteratorSetValues.next().done, + "The lightWeightMap iterator.done should true for an empty map"); } function testGetIndexOfKeyAndValue() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("sparrow", 123); lightWeightMap.set("gull", 357); let result1 = lightWeightMap.getIndexOfKey("sparrow"); let result2 = lightWeightMap.getIndexOfValue(357); - assertEQ(result1, 1, "The lightWeightMap should return the correct index for the key"); - assertEQ(result2, 0, "The lightWeightMap should return the correct index for the value"); + arktest.assertEQ(result1, 1, "The lightWeightMap should return the correct index for the key"); + arktest.assertEQ(result2, 0, "The lightWeightMap should return the correct index for the value"); } function testGetIndexOfKeyAndValueNonExisting() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("sparrow", 123); lightWeightMap.set("gull", 357); let result1 = lightWeightMap.getIndexOfKey("test"); let result2 = lightWeightMap.getIndexOfValue(111); - assertEQ(result1, -1, "The lightWeightMap should return -1 for a key that is not set"); - assertEQ(result2, -1, "The lightWeightMap should return -1 for a value that is not set"); + arktest.assertEQ(result1, -1, "The lightWeightMap should return -1 for a key that is not set"); + arktest.assertEQ(result2, -1, "The lightWeightMap should return -1 for a value that is not set"); } function testGetIndexOfKeyAndValueEmptyMap() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); let result1 = lightWeightMap.getIndexOfKey("test"); let result2 = lightWeightMap.getIndexOfValue(111); - assertEQ(result1, -1, "The lightWeightMap should return -1 for a key in an empty map"); - assertEQ(result2, -1, "The lightWeightMap should return -1 for a value in an empty map"); + arktest.assertEQ(result1, -1, "The lightWeightMap should return -1 for a key in an empty map"); + arktest.assertEQ(result2, -1, "The lightWeightMap should return -1 for a value in an empty map"); } function testGetIndexOfKeyAndValueRepeated() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("sparrow", 123); lightWeightMap.set("gull", 123); let result1 = lightWeightMap.getIndexOfKey("sparrow"); let result2 = lightWeightMap.getIndexOfValue(123); - assertEQ(result1, 1, "The lightWeightMap should return the correct index for the key"); - assertEQ(result2, 0, "The lightWeightMap should return the first index for the repeated value"); + arktest.assertEQ(result1, 1, "The lightWeightMap should return the correct index for the key"); + arktest.assertEQ(result2, 0, "The lightWeightMap should return the first index for the repeated value"); } function testGetIndexOfKeyAndValueDifferentLength() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("a", 123); lightWeightMap.set("sparrow", 123); lightWeightMap.set("gull", 357); @@ -113,8 +110,8 @@ function testGetIndexOfKeyAndValueDifferentLength() { lightWeightMap.set("g", 4356); let result1 = lightWeightMap.getIndexOfKey("sparrow"); let result2 = lightWeightMap.getIndexOfValue(35); - assertNE(result1, -1, "The lightWeightMap should return the correct index for the key"); - assertNE(result2, -1, "The lightWeightMap should return the correct index for the value"); + arktest.assertNE(result1, -1, "The lightWeightMap should return the correct index for the key"); + arktest.assertNE(result2, -1, "The lightWeightMap should return the correct index for the value"); } interface Complex { @@ -139,6 +136,6 @@ function testGetIndexOfKeyAndValueComplex() { lightWeightMap.set(key, value); let result1 = lightWeightMap.getIndexOfKey(key); let result2 = lightWeightMap.getIndexOfValue(value); - assertNE(result1, -1, "The lightWeightMap should return the correct index for the key"); - assertNE(result2, -1, "The lightWeightMap should return the correct index for the value"); + arktest.assertNE(result1, -1, "The lightWeightMap should return the correct index for the key"); + arktest.assertNE(result2, -1, "The lightWeightMap should return the correct index for the value"); } diff --git a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapSetGetTest.ets b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapSetGetTest.ets index bd8d0eb1fda8e55c760290588ac5ec9430b82c9d..8ab38a49a70daf76b47671df145afae59cd367e3 100644 --- a/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapSetGetTest.ets +++ b/static_core/plugins/ets/tests/ets_sdk/api/@ohos/util/LightWeightMap/LightWeightMapSetGetTest.ets @@ -15,14 +15,11 @@ import LightWeightMap from "@ohos.util.LightWeightMap"; -const success = 0; -const fail = 1; - function main(): int { - const suite = new ArkTestsuite("LightWeightMap Set Get API tests") + const suite = new arktest.ArkTestsuite("LightWeightMap Set Get API tests") suite.addTest("LightWeightMap set valid data", testSetValidData) - suite.addTest("LightWeightMap set larger number value", testSetLargeNumberValue) + suite.addTest("LightWeightMap set larger double value", testSetLargeNumberValue) suite.addTest("LightWeightMap get valid key", testGetValidKey) suite.addTest("LightWeightMap get not set key", testGetNonExistingKey) suite.addTest("LightWeightMap get key from the null lightWeightMap", testGetKeyFromEmptyMap) @@ -37,50 +34,50 @@ function main(): int { } function testSetValidData() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", 123); let result = lightWeightMap.get("squirrel"); - assertEQ(result, 123, "The lightWeightMap should return the correct value for the key"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, 123, "The lightWeightMap should return the correct value for the key"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } function testSetLargeNumberValue() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", Number.MAX_VALUE); let result = lightWeightMap.get("squirrel"); - assertEQ(result, Number.MAX_VALUE, "The lightWeightMap should return the correct value for the key"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, Number.MAX_VALUE, "The lightWeightMap should return the correct value for the key"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } function testGetValidKey() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set(123, 356); let result = lightWeightMap.get(123); - assertEQ(result, 356, "The lightWeightMap should return the correct value for the key"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, 356, "The lightWeightMap should return the correct value for the key"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } function testGetNonExistingKey() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("sparrow", 356); let result = lightWeightMap.get("squirrel"); - assertEQ(result, undefined, "The lightWeightMap should return undefined for the key that is not set"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, undefined, "The lightWeightMap should return undefined for the key that is not set"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } function testGetKeyFromEmptyMap() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); let result = lightWeightMap.get("sparrow"); - assertEQ(result, undefined, "The lightWeightMap should return undefined for the key in an empty map"); - assertEQ(lightWeightMap.length, 0, "The lightWeightMap length should be 0"); + arktest.assertEQ(result, undefined, "The lightWeightMap should return undefined for the key in an empty map"); + arktest.assertEQ(lightWeightMap.length, 0, "The lightWeightMap length should be 0"); } function testGetBlankKey() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("sparrow", 356); let result = lightWeightMap.get(" sparrow "); - assertEQ(result, undefined, "The lightWeightMap should return undefined for the key that is not set"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, undefined, "The lightWeightMap should return undefined for the key that is not set"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } interface ComplexValue { @@ -98,8 +95,8 @@ function testGetKeyWithComplexValue() { let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", value); let result = lightWeightMap.get("squirrel"); - assertEQ(result, value, "The lightWeightMap should return the correct value for the key"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, value, "The lightWeightMap should return the correct value for the key"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } interface ComplexKey { @@ -114,34 +111,34 @@ function testGetComplexKey() { gender: 'male', age: 30, }; - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set(key, 123); let result = lightWeightMap.get(key); - assertEQ(result, 123, "The lightWeightMap should return the correct value for the complex key"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, 123, "The lightWeightMap should return the correct value for the complex key"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } function testGetSpecialKey() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set('鸿蒙', 123); let result = lightWeightMap.get('鸿蒙'); - assertEQ(result, 123, "The lightWeightMap should return the correct value for the special key"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, 123, "The lightWeightMap should return the correct value for the special key"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } function testGetSameKeyWithDifferentValue() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set("squirrel", 123); lightWeightMap.set("squirrel", 456); let result = lightWeightMap.get("squirrel"); - assertEQ(result, 456, "The lightWeightMap should return the correct value for the key"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, 456, "The lightWeightMap should return the correct value for the key"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } function testGetEmptyStringKey() { - let lightWeightMap: LightWeightMap = new LightWeightMap(); + let lightWeightMap: LightWeightMap = new LightWeightMap(); lightWeightMap.set('', 123); let result = lightWeightMap.get(''); - assertEQ(result, 123, "The lightWeightMap should return the correct value for the key"); - assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); + arktest.assertEQ(result, 123, "The lightWeightMap should return the correct value for the key"); + arktest.assertEQ(lightWeightMap.length, 1, "The lightWeightMap length should be 1"); } diff --git a/static_core/plugins/ets/tests/ets_sdk/fuzztest/BUILD.gn b/static_core/plugins/ets/tests/ets_sdk/fuzztest/BUILD.gn index 6296ae840357d3e6817ff2cc06e793686486abd4..88de6fc547d83c6a87cdc9ef4d8c99638a2a9cb5 100644 --- a/static_core/plugins/ets/tests/ets_sdk/fuzztest/BUILD.gn +++ b/static_core/plugins/ets/tests/ets_sdk/fuzztest/BUILD.gn @@ -13,12 +13,10 @@ group("fuzztest") { testonly = true - if (defined(ohos_indep_compiler_enable) && ohos_indep_compiler_enable) { - deps = [] - } else { - deps = [ - "anitextdecoder_fuzzer:fuzztest", - "anitextencoder_fuzzer:fuzztest", - ] - } + + deps = [ + "anistringdecoder_fuzzer:fuzztest", + "anitextdecoder_fuzzer:fuzztest", + "anitextencoder_fuzzer:fuzztest", + ] } diff --git a/static_core/plugins/ets/tests/ets_sdk/fuzztest/anistringdecoder_fuzzer/anistringdecoder_fuzzer.cpp b/static_core/plugins/ets/tests/ets_sdk/fuzztest/anistringdecoder_fuzzer/anistringdecoder_fuzzer.cpp index 74e55e6315d137a08f9f505cf58bf5b50983a85f..bd2359f27f303d9e49e9acccdce4e62729640560 100644 --- a/static_core/plugins/ets/tests/ets_sdk/fuzztest/anistringdecoder_fuzzer/anistringdecoder_fuzzer.cpp +++ b/static_core/plugins/ets/tests/ets_sdk/fuzztest/anistringdecoder_fuzzer/anistringdecoder_fuzzer.cpp @@ -66,9 +66,9 @@ void AniStringDecoderFuzzTest(const char *data, size_t size) StringDecoderEngine *engine = StringDecoderEngine::GetInstance(); engine->AniStringDecoder(data, 0, size); - engine->AniStringDecoder(data, 1, size); + engine->AniStringDecoder(data, 1, size - 1); engine->AniStringDecoderEnd(data, 0, size); - engine->AniStringDecoderEnd(data, 1, size); + engine->AniStringDecoderEnd(data, 1, size - 1); } } // namespace OHOS diff --git a/static_core/plugins/ets/tests/stdlib-templates/utils/test_core_typedarray_function2.j2 b/static_core/plugins/ets/tests/stdlib-templates/utils/test_core_typedarray_function2.j2 index dd4de7c0037581cc6cdb0098043e1d6c54270cbc..00e219315b61b2c4fb961fba3fa740c87be6761e 100644 --- a/static_core/plugins/ets/tests/stdlib-templates/utils/test_core_typedarray_function2.j2 +++ b/static_core/plugins/ets/tests/stdlib-templates/utils/test_core_typedarray_function2.j2 @@ -82,7 +82,7 @@ const abnormalSource: FixedArray<{{.item.primitiveType}}> = {{.item.abnormalData function testSubarrayWithOutParam(): int { //let source: FixedArray<{{.item.primitiveType}}> = [10, 20, 30, 40, 50, 60]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -103,13 +103,13 @@ function testSubarrayWithOutParam(): int { return fail; } - if (target.length as int != origin.length as int) { + if (target.length.toInt() != origin.length.toInt()) { console.log("Array length mismatch on slice"); return fail; } //Check all the data copied; - for (let i: int = 0; i< origin.length as int; i++) { + for (let i: int = 0; i< origin.length.toInt(); i++) { let tv = target[i]{{.item.cast2primitive}}; let ov = origin[i]{{.item.cast2primitive}}; console.log(source[i] + "->" + tv + "->" + ov); @@ -120,7 +120,7 @@ function testSubarrayWithOutParam(): int { } origin= new {{.item.objectType}}(0); - if (origin.length as int != 0){ + if (origin.length.toInt() != 0){ return fail; } @@ -131,7 +131,7 @@ function testSubarrayWithOutParam(): int { return fail; } - if (target.length as int != 0){ + if (target.length.toInt() != 0){ return fail; } return success; @@ -139,7 +139,7 @@ function testSubarrayWithOutParam(): int { function testSubarrayOneParam(): int { //let source: FixedArray<{{.item.primitiveType}}> = [10, 20, 30, 40, 50, 60]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -152,7 +152,7 @@ function testSubarrayOneParam(): int { } let subarrayStart: int = 1; - let subarrayEnd: int = origin.length as int; + let subarrayEnd: int = origin.length.toInt(); let target: {{.item.objectType}}; @@ -163,7 +163,7 @@ function testSubarrayOneParam(): int { return fail; } - if (target.length as int != origin.length as int - subarrayStart) { + if (target.length.toInt() != origin.length.toInt() - subarrayStart) { console.log("Array length mismatch on subarray One Params" + target.length); return fail; } @@ -187,7 +187,7 @@ function testSubarrayOneParam(): int { return fail; } - if (target.length as int != origin.length as int) { + if (target.length.toInt() != origin.length.toInt()) { console.log("Array length mismatch on subarray One Params" + target.length); return fail; } @@ -232,7 +232,7 @@ function testSubarrayOneParam(): int { function testSubarrayTwoParams(): int { //let source: FixedArray<{{.item.primitiveType}}> = [10, 20, 30, 40, 50, 60, 70, 80]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -244,7 +244,7 @@ function testSubarrayTwoParams(): int { return fail; } - let subarrayStart: int = 2; + let subarrayStart: int | undefined = 2; let subarrayEnd: int | undefined = 4; let target: {{.item.objectType}}; @@ -256,7 +256,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -273,7 +273,7 @@ function testSubarrayTwoParams(): int { } subarrayStart = 0; - subarrayEnd = origin.length as int; + subarrayEnd = origin.length.toInt(); try { target = origin.subarray(subarrayStart, subarrayEnd); } catch(e) { @@ -281,7 +281,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -304,7 +304,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -327,7 +327,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -350,7 +350,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -373,7 +373,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != 0) { + if (target.length.toInt() != 0) { console.log("Array length mismatch on subarray2"); return fail; } @@ -383,7 +383,7 @@ function testSubarrayTwoParams(): int { function testSubarrayTwoParamsWithOtherNumber(): int { //let source: FixedArray<{{.item.primitiveType}}> = [10, 20, 30, 40, 50, 60, 70, 80]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -395,7 +395,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { return fail; } - let subarrayStart: int = 4; + let subarrayStart: int | undefined = 4; let subarrayEnd: int | undefined = 2; let target: {{.item.objectType}}; @@ -406,27 +406,27 @@ function testSubarrayTwoParamsWithOtherNumber(): int { return fail; } - if (target.length as int != 0) { + if (target.length.toInt() != 0) { console.log("Array length mismatch on subarray2"); return fail; } subarrayStart = -1; - subarrayEnd = origin.length as int; + subarrayEnd = origin.length.toInt(); try { target = origin.subarray(subarrayStart, subarrayEnd); } catch(e) { return fail; } - if (target.length as int != subarrayEnd - (origin.length + subarrayStart)) { + if (target.length.toInt() != subarrayEnd - (origin.length + subarrayStart)) { console.log("Array length mismatch on subarray2"); return fail; } //Check all the data copied; - for (let i: int = (origin.length + subarrayStart) as int; i< subarrayEnd; i++) { - let tv = target[(i - (origin.length + subarrayStart)) as int]{{.item.cast2primitive}}; + for (let i: int = (origin.length + subarrayStart).toInt(); i< subarrayEnd; i++) { + let tv = target[(i - (origin.length + subarrayStart)).toInt()]{{.item.cast2primitive}}; let ov = origin[i]{{.item.cast2primitive}}; console.log(source[i] + "->" + tv + "->" + ov); if (tv != ov) { @@ -436,7 +436,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { } subarrayStart = 0; - subarrayEnd = -origin.length as int; + subarrayEnd = -origin.length.toInt(); try { target = origin.subarray(subarrayStart, subarrayEnd); } catch(e) { @@ -444,7 +444,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { return fail; } - if (target.length as int != (origin.length + subarrayEnd) - subarrayStart) { + if (target.length.toInt() != (origin.length + subarrayEnd) - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -454,7 +454,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { function testSubarrayOneLengthTwoParams(): int { let source1: FixedArray<{{.item.primitiveType}}> = [10]; - let ss = new ArrayBuffer(source1.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source1.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -465,8 +465,8 @@ function testSubarrayOneLengthTwoParams(): int { console.log(e); return fail; } - - let subarrayStart: int = 4; + + let subarrayStart: int | undefined = 4; let subarrayEnd: int | undefined = 2; let target: {{.item.objectType}}; @@ -477,7 +477,7 @@ function testSubarrayOneLengthTwoParams(): int { return fail; } - if (target.length as int != 0) { + if (target.length.toInt() != 0) { console.log("Array length mismatch on subarray2"); return fail; } @@ -490,7 +490,7 @@ function testSubarrayOneLengthTwoParams(): int { return fail; } - if (target.length as int != 0) { + if (target.length.toInt() != 0) { console.log("Array length mismatch on subarray2"); return fail; } @@ -512,11 +512,11 @@ function testNonEmptyTypedArraySetValue(): number { let length = typedArray.length; {%- if item.objectType == 'Int8Array' %} - const minValue = Byte.MIN_VALUE as byte; - const maxValue = Byte.MAX_VALUE as byte; + const minValue = Byte.MIN_VALUE.toByte(); + const maxValue = Byte.MAX_VALUE.toByte(); {%- elif item.objectType == 'Int16Array' %} - const minValue = Short.MIN_VALUE as short; - const maxValue = Short.MAX_VALUE as short; + const minValue = Short.MIN_VALUE.toShort(); + const maxValue = Short.MAX_VALUE.toShort(); {%- elif item.objectType == 'Int32Array' %} const minValue = Int.MIN_VALUE; const maxValue = Int.MAX_VALUE; @@ -529,7 +529,7 @@ function testNonEmptyTypedArraySetValue(): number { {% elif item.objectType == 'BigInt64Array' %} const minValue = Long.MIN_VALUE; const maxValue = Long.MAX_VALUE; - {%- endif %} + {%- endif %} try { typedArray[0] = {{.item.create}}(1); @@ -550,7 +550,7 @@ function testNonEmptyTypedArraySetValue(): number { return fail; } } - + if (typedArray.at(length) != undefined) { // check value, wrong index console.log("Test failed. typedArray.at(length) : " + typedArray.at(length)); return fail; @@ -584,15 +584,15 @@ function assertEq(expr: boolean, expected: boolean, name: string): int { function testTypedArrayEntries(): int { let failures = 0 - const firstResult = [0 as number, {{.item.create}}(1)] as [number, {{.item.type}}] - const secondResult = [1 as number, {{.item.create}}(2)] as [number, {{.item.type}}] + const firstResult = [0, {{.item.create}}(1)] as [number, {{.item.type}}] + const secondResult = [1, {{.item.create}}(2)] as [number, {{.item.type}}] const numbers = [{{.item.create}}(1), {{.item.create}}(2)] let entries = (new {{.item.objectType}}(numbers)).entries() let entry1 = entries.next().value! - let entry1res: [number, {{.item.type}}] = [entry1[0] as number, {{.item.create}}(entry1[1])] + let entry1res: [number, {{.item.type}}] = [entry1[0], {{.item.create}}(entry1[1])] let entry2 = entries.next().value! - let entry2res: [number, {{.item.type}}] = [entry2[0] as number, {{.item.create}}(entry2[1])] + let entry2res: [number, {{.item.type}}] = [entry2[0], {{.item.create}}(entry2[1])] failures += checkEntries(entry1res, firstResult, "{{.item.objectType}}.entries() - next() #1") failures += checkEntries(entry2res, secondResult, "{{.item.objectType}}.entries() - next() #2") @@ -602,7 +602,7 @@ function testTypedArrayEntries(): int { // Test case for TypedArray.includes(searchElement) with Normal Number function testTypedArrayIncludesOneParamWithNormalNum(): number { - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let typedArray: {{.item.objectType}}; try { @@ -631,13 +631,13 @@ function testTypedArrayIncludesOneParamWithNormalNum(): number { } else { console.log(`Includes ${searchElement}? false`); } - return success; + return success; } {%- if item.objectType != 'BigInt64Array' %} // Test case for TypedArray.includes(searchElement) with abnormal number function testTypedArrayIncludesOneParamWithAbnormalNum(): number { - let ss = new ArrayBuffer(abnormalSource.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(abnormalSource.length.toInt() * {{.item.primitiveSizeBytes}}); let typedArray: {{.item.objectType}}; try { @@ -649,9 +649,9 @@ function testTypedArrayIncludesOneParamWithAbnormalNum(): number { } {%- if item.objectType == 'Int8Array' %} - let searchElementArray: FixedArray<{{.item.primitiveType}}> = [Byte.MAX_VALUE - 1 as byte, Byte.MAX_VALUE as byte, Byte.MIN_VALUE as byte, Byte.MIN_VALUE + 1 as byte]; + let searchElementArray: FixedArray<{{.item.primitiveType}}> = [(Byte.MAX_VALUE - 1).toByte(), Byte.MAX_VALUE, Byte.MIN_VALUE, (Byte.MIN_VALUE + 1).toByte()]; {%- elif item.objectType == 'Int16Array' %} - let searchElementArray: FixedArray<{{.item.primitiveType}}> = [Short.MAX_VALUE - 1 as short, Short.MAX_VALUE as short, Short.MIN_VALUE as short, Short.MIN_VALUE + 1 as short]; + let searchElementArray: FixedArray<{{.item.primitiveType}}> = [(Short.MAX_VALUE - 1).toShort(), Short.MAX_VALUE, Short.MIN_VALUE, (Short.MIN_VALUE + 1).toShort()]; {%- elif item.objectType == 'Int32Array' %} let searchElementArray: FixedArray<{{.item.primitiveType}}> = [Int.MAX_VALUE - 1, Int.MAX_VALUE, Int.MIN_VALUE, Int.MIN_VALUE + 1]; {%- elif item.objectType == 'Float32Array' %} @@ -695,7 +695,7 @@ function testTypedArrayIncludesOneParamWithAbnormalNum(): number { return fail; } } - return success; + return success; } {% endif %} @@ -777,10 +777,10 @@ function testTypedArrayIncludesTwoParamWithAbnormalIndex(): number { let fromIndex = 0; let arrayLen = typedArray.length; //a. Let k be len + n. - //ECMA: if fromIndex < 0(k < 0) ,a. Let k be len + n. + //ECMA: if fromIndex < 0(k < 0) ,a. Let k be len + n. // b. If k < 0, set k to 0. typedArray need to be fixed for (let i = 0 as int; i < arrayLen; i++) { - fromIndex = (- arrayLen + i) as int; + fromIndex = (- arrayLen + i).toInt(); result = typedArray.includes(searchElement, fromIndex); let exceptResult = typedArray.includes(searchElement, i); if (result == exceptResult) { @@ -790,7 +790,7 @@ function testTypedArrayIncludesTwoParamWithAbnormalIndex(): number { return fail; } } - + //index > len, return false; fromIndex = 6 as int; result = typedArray.includes(searchElement, fromIndex); @@ -803,7 +803,7 @@ function testTypedArrayIncludesTwoParamWithAbnormalIndex(): number { //Currently not supported for typedArray //b. If k < 0, set k to 0. - fromIndex = (-arrayLen -1) as int; + fromIndex = (-arrayLen -1).toInt(); result = typedArray.includes(searchElement, fromIndex); if (result) { console.log(`Includes ${searchElement} from index ${fromIndex}? true`); @@ -846,7 +846,7 @@ function testTypedArrayJoinWithEmptyArray(): number { } catch(e) { return fail; } - if (typedArray.length as number != 0 || typedArray.byteOffset as number != 0) { + if (typedArray.length != 0 || typedArray.byteOffset != 0) { return fail; } @@ -956,10 +956,10 @@ function testTypedArrayKeysWithEmptyArray(): number { } catch(e) { return fail; } - if (typedArray.length as number != 0 || typedArray.byteOffset as number != 0) { + if (typedArray.length != 0 || typedArray.byteOffset != 0) { return fail; } - + const emptyKeysArray = typedArray.keys(); if(emptyKeysArray.next().value != undefined){ return fail; @@ -974,7 +974,7 @@ function testTypedArrayKeysWithSingleElementArray(): number { } catch(e) { return fail; } - + const emptyKeysArray = typedArray.keys(); if(emptyKeysArray.next().value != 0){ return fail; @@ -999,7 +999,7 @@ function testTypedArrayKeysWithSingleElementArray(): number { return fail; } - //test big length,comment out the code to prevent potential unknown errors. + //test big length,comment out the code to prevent potential unknown errors. /* try { const largeLengthArray = new {{.item.objectType}}(1e4); @@ -1069,7 +1069,7 @@ function testTypedArrayForEach(): number{ } typedArray.forEach((value, index, array) => { - array[index] = value * {{.item.create}}(2); + array[index.toInt()] = value * {{.item.create}}(2); }); // Compare the modified array with the expected result @@ -1104,8 +1104,8 @@ function testTypedArrayForEachValueIndexCallback(): number { } typedArray.forEach((value, index) => { - typedArray[index] = value * {{.item.create}}(2); - console.log("In forEach: " + JSON.stringify(typedArray[index])); + typedArray[index.toInt()] = value * {{.item.create}}(2); + console.log("In forEach: " + JSON.stringify(typedArray[index.toInt()])); }); // Compare the modified array with the expected result @@ -1210,9 +1210,6 @@ const testTypedArrayGet = parametrize>( const isMinusOneThrows = testTypedArrayGetException(array, -1) == success const isLengthThrows = testTypedArrayGetException(array, array.length) == success - const isNegInfThrows = testTypedArrayGetException(array, -Infinity) == success - const isPosInfThrows = testTypedArrayGetException(array, +Infinity) == success - const isNanZero = array[NaN] == array[0] let isCorrectValues = true for (let i = 0; i < array.length; i++) { @@ -1226,16 +1223,13 @@ const testTypedArrayGet = parametrize>( failures += check(boolToResult(isMinusOneThrows == expected[0]), `{{.item.objectType}} didn't throw: array[-1]`) failures += check(boolToResult(isLengthThrows == expected[1]), `{{.item.objectType}} didn't throw: array[array.length]`) - failures += check(boolToResult(isNegInfThrows == expected[2]), `{{.item.objectType}} didn't throw: array[-Infinity]`) - failures += check(boolToResult(isPosInfThrows == expected[3]), `{{.item.objectType}} didn't throw: array[+Infinity]`) - failures += check(boolToResult(isNanZero == expected[4]), `{{.item.objectType}} failed: array[NaN] == array[0]`) - failures += check(boolToResult(isCorrectValues == expected[5]), `{{.item.objectType}} failed: array[i] == (i + 1) * 10`) - + failures += check(boolToResult(isCorrectValues == expected[2]), `{{.item.objectType}} failed: array[i] == (i + 1) * 10`) + return failures == 0 ? success: fail } ) -function testTypedArrayGetException(array: {{.item.objectType}}, index: number): number { +function testTypedArrayGetException(array: {{.item.objectType}}, index: int): number { try { const tmp = array[index] } catch (e: RangeError) { @@ -1261,47 +1255,39 @@ const testTypedArraySet = parametrize>( const isMinusOneThrows = testTypedArraySetException(array, -1, value) == success const isLengthThrows = testTypedArraySetException(array, array.length, value) == success - const isNegInfThrows = testTypedArraySetException(array, -Infinity, value) == success - const isPosInfThrows = testTypedArraySetException(array, +Infinity, value) == success - - array[NaN] = value - const isNanZero = array[0] == {{.item.create}}(value) {%- if item.objectType != 'BigInt64Array' %} - array[0 as number] = (value + 1) as number + array[0] = (value + 1) const isNumberNumberCorrect = array[0] == {{.item.create}}(value + 1) - - array[0 as int] = (value + 2) as number + + array[0 as int] = (value + 2) const isIntNumberCorrect = array[0] == {{.item.create}}(value + 2) - + {%- endif %} - array[0 as number] = (value + 3) as int + array[0] = (value + 3).toInt() const isNumberIntCorrect = array[0] == {{.item.create}}(value + 3) - array[0 as int] = (value + 4) as int + array[0 as int] = (value + 4).toInt() const isIntIntCorrect = array[0] == {{.item.create}}(value + 4) let failures = 0 failures += check(boolToResult(isMinusOneThrows == expected[0]), `{{.item.objectType}} didn't throw: array[-1] = value`) failures += check(boolToResult(isLengthThrows == expected[1]), `{{.item.objectType}} didn't throw: array[array.length] = value`) - failures += check(boolToResult(isNegInfThrows == expected[2]), `{{.item.objectType}} didn't throw: array[-Infinity] = value`) - failures += check(boolToResult(isPosInfThrows == expected[3]), `{{.item.objectType}} didn't throw: array[+Infinity] = value`) - failures += check(boolToResult(isNanZero == expected[4]), `{{.item.objectType}} failed: array[NaN] = value`) {%- if item.objectType != 'BigInt64Array' %} - failures += check(boolToResult(isNumberNumberCorrect == expected[5]), `{{.item.objectType}} failed: array[0 as number] = value as number`) - failures += check(boolToResult(isIntNumberCorrect == expected[6]), `{{.item.objectType}} failed: array[0 as int] = value as number`) + failures += check(boolToResult(isNumberNumberCorrect == expected[2]), `{{.item.objectType}} failed: array[0] = value`) + failures += check(boolToResult(isIntNumberCorrect == expected[3]), `{{.item.objectType}} failed: array[0 as int] = value`) {%- endif %} - failures += check(boolToResult(isNumberIntCorrect == expected[7]), `{{.item.objectType}} failed: array[0 as number] = value as int`) - failures += check(boolToResult(isIntIntCorrect == expected[8]), `{{.item.objectType}} failed: array[0 as int] = value as int`) - + failures += check(boolToResult(isNumberIntCorrect == expected[4]), `{{.item.objectType}} failed: array[0] = value.toInt()`) + failures += check(boolToResult(isIntIntCorrect == expected[5]), `{{.item.objectType}} failed: array[0 as int] = value.toInt()`) + return failures == 0 ? success: fail } ) -function testTypedArraySetException(array: {{.item.objectType}}, index: number, value: int): number { +function testTypedArraySetException(array: {{.item.objectType}}, index: int, value: int): number { try { array[index] = value } catch (e: RangeError) { diff --git a/static_core/plugins/ets/tests/stdlib-templates/utils/test_core_typeduarray_function2.j2 b/static_core/plugins/ets/tests/stdlib-templates/utils/test_core_typeduarray_function2.j2 index 02e4c92ac6adb46699c0891360805eea99676b68..5bc2cf3d2a8d74b94cd35a70aa60757479b6954b 100644 --- a/static_core/plugins/ets/tests/stdlib-templates/utils/test_core_typeduarray_function2.j2 +++ b/static_core/plugins/ets/tests/stdlib-templates/utils/test_core_typeduarray_function2.j2 @@ -90,7 +90,7 @@ function testSubarrayWithOutParam(): int { {{.item.create}}(50), {{.item.create}}(60) ]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -111,13 +111,13 @@ function testSubarrayWithOutParam(): int { return fail; } - if (target.length as int != origin.length as int) { + if (target.length.toInt() != origin.length.toInt()) { console.log("Array length mismatch on slice"); return fail; } //Check all the data copied; - for (let i: int = 0; i< origin.length as int; i++) { + for (let i: int = 0; i< origin.length.toInt(); i++) { let tv = target[i] as {{.item.type}}; let ov = origin[i] as {{.item.type}}; console.log(source[i] + "->" + tv + "->" + ov); @@ -128,7 +128,7 @@ function testSubarrayWithOutParam(): int { } origin= new {{.item.objectType}}(0); - if (origin.length as int != 0){ + if (origin.length.toInt() != 0){ return fail; } @@ -139,7 +139,7 @@ function testSubarrayWithOutParam(): int { return fail; } - if (target.length as int != 0){ + if (target.length.toInt() != 0){ return fail; } return success; @@ -154,7 +154,7 @@ function testSubarrayOneParam(): int { {{.item.create}}(50), {{.item.create}}(60) ]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -167,7 +167,7 @@ function testSubarrayOneParam(): int { } let subarrayStart: int = 1; - let subarrayEnd: int = origin.length as int; + let subarrayEnd: int = origin.length.toInt(); let target: {{.item.objectType}}; @@ -178,7 +178,7 @@ function testSubarrayOneParam(): int { return fail; } - if (target.length as int != origin.length as int - subarrayStart) { + if (target.length.toInt() != origin.length.toInt() - subarrayStart) { console.log("Array length mismatch on subarray One Params" + target.length); return fail; } @@ -202,7 +202,7 @@ function testSubarrayOneParam(): int { return fail; } - if (target.length as int != origin.length as int) { + if (target.length.toInt() != origin.length.toInt()) { console.log("Array length mismatch on subarray One Params" + target.length); return fail; } @@ -253,7 +253,7 @@ function testSubarrayTwoParams(): int { {{.item.create}}(50), {{.item.create}}(60) ]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -265,7 +265,7 @@ function testSubarrayTwoParams(): int { return fail; } - let subarrayStart: int = 2; + let subarrayStart: int | undefined = 2; let subarrayEnd: int | undefined = 4; let target: {{.item.objectType}}; @@ -277,7 +277,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -294,7 +294,7 @@ function testSubarrayTwoParams(): int { } subarrayStart = 0; - subarrayEnd = origin.length as int; + subarrayEnd = origin.length.toInt(); try { target = origin.subarray(subarrayStart, subarrayEnd); } catch(e) { @@ -302,7 +302,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -325,7 +325,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -348,7 +348,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -371,7 +371,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != subarrayEnd - subarrayStart) { + if (target.length.toInt() != subarrayEnd - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -394,7 +394,7 @@ function testSubarrayTwoParams(): int { return fail; } - if (target.length as int != 0) { + if (target.length.toInt() != 0) { console.log("Array length mismatch on subarray2"); return fail; } @@ -411,7 +411,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { {{.item.create}}(50), {{.item.create}}(60) ]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -423,7 +423,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { return fail; } - let subarrayStart: int = 4; + let subarrayStart: int | undefined = 4; let subarrayEnd: int | undefined = 2; let target: {{.item.objectType}}; @@ -434,26 +434,26 @@ function testSubarrayTwoParamsWithOtherNumber(): int { return fail; } - if (target.length as int != 0) { + if (target.length.toInt() != 0) { console.log("Array length mismatch on subarray2"); return fail; } subarrayStart = -1; - subarrayEnd = origin.length as int; + subarrayEnd = origin.length.toInt(); try { target = origin.subarray(subarrayStart, subarrayEnd); } catch(e) { return fail; } - if (target.length as int != subarrayEnd - (origin.length + subarrayStart)) { + if (target.length.toInt() != subarrayEnd - (origin.length + subarrayStart)) { console.log("Array length mismatch on subarray2"); return fail; } //Check all the data copied; - for (let i: int = (origin.length + subarrayStart) as int; i< subarrayEnd; i++) { + for (let i: int = (origin.length + subarrayStart).toInt(); i< subarrayEnd; i++) { let tv = target[i - (origin.length + subarrayStart)] as {{.item.type}}; let ov = origin[i] as {{.item.type}}; console.log(source[i] + "->" + tv + "->" + ov); @@ -464,7 +464,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { } subarrayStart = 0; - subarrayEnd = -origin.length as int; + subarrayEnd = -origin.length.toInt(); try { target = origin.subarray(subarrayStart, subarrayEnd); } catch(e) { @@ -472,7 +472,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { return fail; } - if (target.length as int != (origin.length + subarrayEnd) - subarrayStart) { + if (target.length.toInt() != (origin.length + subarrayEnd) - subarrayStart) { console.log("Array length mismatch on subarray2"); return fail; } @@ -482,7 +482,7 @@ function testSubarrayTwoParamsWithOtherNumber(): int { function testSubarrayOneLengthTwoParams(): int { let source: FixedArray<{{.item.type}}> = [{{.item.create}}(10)]; - let ss = new ArrayBuffer(source.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(source.length.toInt() * {{.item.primitiveSizeBytes}}); let origin: {{.item.objectType}}; @@ -494,7 +494,7 @@ function testSubarrayOneLengthTwoParams(): int { return fail; } - let subarrayStart: int = 4; + let subarrayStart: int | undefined = 4; let subarrayEnd: int | undefined = 2; let target: {{.item.objectType}}; @@ -505,7 +505,7 @@ function testSubarrayOneLengthTwoParams(): int { return fail; } - if (target.length as int != 0) { + if (target.length.toInt() != 0) { console.log("Array length mismatch on subarray2"); return fail; } @@ -518,7 +518,7 @@ function testSubarrayOneLengthTwoParams(): int { return fail; } - if (target.length as int != 0) { + if (target.length.toInt() != 0) { console.log("Array length mismatch on subarray2"); return fail; } @@ -594,7 +594,7 @@ function testNonEmptyTypedUArraySetValue(): number { // Test case for typedUArray.includes(searchElement) with Normal Number function testTypedUArrayIncludesOneParamWithNormalNum(): number { - let ss = new ArrayBuffer(normalSource.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(normalSource.length.toInt() * {{.item.primitiveSizeBytes}}); let typedUArray: {{.item.objectType}}; try { @@ -629,7 +629,7 @@ function testTypedUArrayIncludesOneParamWithNormalNum(): number { {%- if item.objectType != 'BigUint64Array' %} // Test case for typedUArray.includes(searchElement) with abnormal number function testTypedUArrayIncludesOneParamWithAbnormalNum(): number { - let ss = new ArrayBuffer(abnormalSource.length as int * {{.item.primitiveSizeBytes}}); + let ss = new ArrayBuffer(abnormalSource.length.toInt() * {{.item.primitiveSizeBytes}}); let typedUArray: {{.item.objectType}}; try { @@ -647,7 +647,7 @@ function testTypedUArrayIncludesOneParamWithAbnormalNum(): number { {%- elif item.objectType == 'Uint16Array' %} let searchElementArray: FixedArray<{{.item.type}}> = [65535, 0, 1, Short.MAX_VALUE, Short.MAX_VALUE - 1, Short.MAX_VALUE + 1]; {%- else %} - let searchElementArray: FixedArray<{{.item.type}}> = [4294967295, 0, 1, Int.MAX_VALUE, Int.MAX_VALUE - 1, Int.MAX_VALUE as number + 1]; + let searchElementArray: FixedArray<{{.item.type}}> = [4294967295, 0, 1, Int.MAX_VALUE, Int.MAX_VALUE - 1, Int.MAX_VALUE.toDouble() + 1]; {%- endif %} for (let i = 0; i < searchElementArray.length; i++) { @@ -659,7 +659,7 @@ function testTypedUArrayIncludesOneParamWithAbnormalNum(): number { // as int for (let i = 0; i < searchElementArray.length; i++) { - if (!typedUArray.includes(searchElementArray[i] as int)) { + if (!typedUArray.includes(searchElementArray[i].toInt())) { console.log("Test failed. testTypedUArrayIncludesOneParamWithAbnormalNum: as int" + JSON.stringify(searchElementArray[i])); return fail; } @@ -727,7 +727,7 @@ function testTypedUArrayIncludesTwoParamWithNormalIndex(): number { return fail; } //as int - result = typedUArray.includes((searchElement{{.item.cast2primitive}}) as int, fromIndex as int); + result = typedUArray.includes((searchElement{{.item.cast2primitive}}).toInt(), fromIndex.toInt()); if (result) { console.log(`Includes ${searchElement} from index ${fromIndex}? true`); } else { @@ -744,7 +744,7 @@ function testTypedUArrayIncludesTwoParamWithNormalIndex(): number { console.log(`Includes ${searchElement} from index ${fromIndex}? false`); } //as int - result = typedUArray.includes((searchElement{{.item.cast2primitive}}) as int, fromIndex as int); + result = typedUArray.includes((searchElement{{.item.cast2primitive}}).toInt(), fromIndex.toInt()); if (result) { console.log(`Includes ${searchElement} from index ${fromIndex}? true`); return fail; @@ -791,7 +791,7 @@ function testTypedUArrayIncludesTwoParamWithAbnormalIndex(): number { //a. Let k be len + n. let arrayLen = typedUArray.length; for (let i = 0 as int; i < arrayLen; i++) { - fromIndex = (- arrayLen + i) as int; + fromIndex = (- arrayLen + i).toInt(); result = typedUArray.includes(searchElement, fromIndex); let exceptResult = typedUArray.includes(searchElement, i); if (result == exceptResult) { @@ -803,7 +803,7 @@ function testTypedUArrayIncludesTwoParamWithAbnormalIndex(): number { } //b. If k < 0, set k to 0. - fromIndex = (-arrayLen -1) as int; + fromIndex = (-arrayLen -1).toInt(); result = typedUArray.includes(searchElement, fromIndex); if (result) { console.log(`Includes ${searchElement} from index ${fromIndex}? true`); @@ -857,7 +857,7 @@ function testTypedUArrayJoinWithEmptyArray(): number { } catch(e) { return fail; } - if (typedUArray.length as number != 0 || typedUArray.byteOffset as number != 0) { + if (typedUArray.length.toDouble() != 0 || typedUArray.byteOffset.toDouble() != 0) { return fail; } @@ -973,7 +973,7 @@ function testTypedUArrayKeysWithEmptyArray(): number { } catch(e) { return fail; } - if (typedUArray.length as number != 0 || typedUArray.byteOffset as number != 0) { + if (typedUArray.length.toDouble() != 0 || typedUArray.byteOffset.toDouble() != 0) { return fail; } @@ -1082,7 +1082,7 @@ function testTypedUArrayForEach(): number { let resultArray = new {{.item.objectType}}(typedUArray.length); typedUArray.forEach((value: {{.item.type}}, index: number, array: {{.item.objectType}}) => { - resultArray[index] = value * {{.item.create}}(2); + resultArray[index.toInt()] = value * {{.item.create}}(2); }); // Compare the modified array with the expected result @@ -1118,7 +1118,7 @@ function testTypedUArrayForEachValueIndexCallback(): number { let resultArray = new {{.item.objectType}}(typedUArray.length); typedUArray.forEach((value: {{.item.type}}, index: number) => { - resultArray[index] = value * {{.item.create}}(2); + resultArray[index.toInt()] = value * {{.item.create}}(2); }); // Compare the modified array with the expected result @@ -1221,12 +1221,8 @@ const testTypedArrayGet = parametrize>( const array = args[0] const expected = args[1] - const isMinusOneThrows = testTypedArrayGetException(array, -1) == success - const isLengthThrows = testTypedArrayGetException(array, array.length) == success - const isNegInfThrows = testTypedArrayGetException(array, -Infinity) == success - const isPosInfThrows = testTypedArrayGetException(array, +Infinity) == success - - const isNanZero = array[NaN] == array[0] + const isMinusOneThrows = testTypedArrayGetError(array, -1) == success + const isLengthThrows = testTypedArrayGetError(array, array.length) == success let isCorrectValues = true for (let i = 0; i < array.length; i++) { @@ -1240,16 +1236,13 @@ const testTypedArrayGet = parametrize>( failures += check(boolToResult(isMinusOneThrows == expected[0]), `didn't throw: array[-1]`) failures += check(boolToResult(isLengthThrows == expected[1]), `didn't throw: array[array.length]`) - failures += check(boolToResult(isNegInfThrows == expected[2]), `didn't throw: array[-Infinity]`) - failures += check(boolToResult(isPosInfThrows == expected[3]), `didn't throw: array[+Infinity]`) - failures += check(boolToResult(isNanZero == expected[4]), `failed: array[NaN] == array[0]`) - failures += check(boolToResult(isCorrectValues == expected[5]), `failed: array[i] == (i + 1) * 10`) - + failures += check(boolToResult(isCorrectValues == expected[2]), `failed: array[i] == (i + 1) * 10`) + return failures == 0 ? success: fail } ) -function testTypedArrayGetException(array: {{.item.objectType}}, index: number): number { +function testTypedArrayGetError(array: {{.item.objectType}}, index: int): number { try { const tmp = array[index] } catch (e: RangeError) { @@ -1273,49 +1266,41 @@ const testTypedArraySet = parametrize>( const expected = args[1] const value = 100 - const isMinusOneThrows = testTypedArraySetException(array, -1, value) == success - const isLengthThrows = testTypedArraySetException(array, array.length, value) == success - const isNegInfThrows = testTypedArraySetException(array, -Infinity, value) == success - const isPosInfThrows = testTypedArraySetException(array, +Infinity, value) == success - - array[NaN] = value - const isNanZero = array[0] == {{.item.create}}(value) + const isMinusOneThrows = testTypedArraySetError(array, -1, value) == success + const isLengthThrows = testTypedArraySetError(array, array.length, value) == success {%- if item.objectType != 'BigUint64Array' %} - array[0 as number] = (value + 1) as number + array[0] = (value + 1).toDouble() const isNumberNumberCorrect = array[0] == {{.item.create}}(value + 1) - array[0 as int] = (value + 2) as number + array[0] = (value + 2).toDouble() const isIntNumberCorrect = array[0] == {{.item.create}}(value + 2) {%- endif %} - array[0 as number] = (value + 3) as int + array[0] = (value + 3).toInt() const isNumberIntCorrect = array[0] == {{.item.create}}(value + 3) - array[0 as int] = (value + 4) as int + array[0] = (value + 4).toInt() const isIntIntCorrect = array[0] == {{.item.create}}(value + 4) let failures = 0 failures += check(boolToResult(isMinusOneThrows == expected[0]), `didn't throw: array[-1] = value`) failures += check(boolToResult(isLengthThrows == expected[1]), `didn't throw: array[array.length] = value`) - failures += check(boolToResult(isNegInfThrows == expected[2]), `didn't throw: array[-Infinity] = value`) - failures += check(boolToResult(isPosInfThrows == expected[3]), `didn't throw: array[+Infinity] = value`) - failures += check(boolToResult(isNanZero == expected[4]), `failed: array[NaN] = value`) {%- if item.objectType != 'BigUint64Array' %} - failures += check(boolToResult(isNumberNumberCorrect == expected[5]), `failed: array[0 as number] = value as number`) - failures += check(boolToResult(isIntNumberCorrect == expected[6]), `failed: array[0 as int] = value as number`) + failures += check(boolToResult(isNumberNumberCorrect == expected[2]), `failed: array[0.toDouble()] = value.toDouble()`) + failures += check(boolToResult(isIntNumberCorrect == expected[3]), `failed: array[0 as int] = value.toDouble()`) {%- endif %} - failures += check(boolToResult(isNumberIntCorrect == expected[7]), `failed: array[0 as number] = value as int`) - failures += check(boolToResult(isIntIntCorrect == expected[8]), `failed: array[0 as int] = value as int`) - + failures += check(boolToResult(isNumberIntCorrect == expected[4]), `failed: array[0.toDouble()] = value as int`) + failures += check(boolToResult(isIntIntCorrect == expected[5]), `failed: array[0 as int] = value as int`) + return failures == 0 ? success: fail } ) -function testTypedArraySetException(array: {{.item.objectType}}, index: number, value: int): number { +function testTypedArraySetError(array: {{.item.objectType}}, index: int, value: int): number { try { array[index] = value } catch (e: RangeError) {