1/* 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16package escompat; 17 18/** 19 * Either ArrayBuffer or SharedArrayBuffer 20 */ 21// NOTE (kprokopenko): make abstract after abstract overloads get to work 22abstract class Buffer { 23 abstract getByteLength(): int 24 abstract at(i: int): byte 25 abstract set(i: int, b: byte) 26 // NOTE (egor-porsev): should be slice(int, int) without an extra 'sliceInternal' method. This is a workaround for #13402 27 abstract sliceInternal(begin: int, end: int): Buffer 28} 29 30export interface ArrayBufferView { 31 /** 32 * The ArrayBuffer instance referenced by the array. 33 */ 34 readonly buffer: ArrayBufferLike 35 36 /** 37 * The length in bytes of the array. 38 */ 39 readonly byteLength: number 40 41 /** 42 * The offset in bytes of the array. 43 */ 44 readonly byteOffset: number 45} 46 47/** 48 * JS ArrayBuffer API-compatible class 49 */ 50export class ArrayBuffer extends Buffer 51{ 52 /** 53 * Creates ArrayBuffer with size equal to length parameter 54 * 55 * @param length size of ArrayBuffer 56 */ 57 public constructor(length: int) 58 { 59 this.data = new byte[length] 60 this._byteLength = length 61 } 62 63 /** 64 * Creates ArrayBuffer with size equal to length parameter 65 * 66 * @param length size of ArrayBuffer 67 */ 68 public constructor(length: number) 69 { 70 this(length as int) 71 } 72 73 /** 74 * Checks if the passed Object is a View 75 * 76 * @param obj to check 77 * 78 * @returns true if obj is instance of typed array 79 */ 80 public static isView(obj: Object): boolean 81 { 82 //TODO(ivan-tyulyandin): add DataView and unsigned TypedArray API when it will be implemented 83 return ( obj instanceof Int8Array 84 || obj instanceof Int16Array 85 || obj instanceof Int32Array 86 || obj instanceof BigInt64Array 87 || obj instanceof Float32Array 88 || obj instanceof Float64Array 89 || obj instanceof Uint8Array 90 || obj instanceof Uint8ClampedArray 91 || obj instanceof Uint16Array 92 || obj instanceof Uint32Array 93 || obj instanceof BigUint64Array) 94 } 95 96 internal override sliceInternal(begin: int, end: int): Buffer { 97 const byteLength = this.getByteLength() 98 99 const startIndex = normalizeIndex(begin, byteLength) 100 const endIndex = normalizeIndex(end, byteLength) 101 102 let resultLength = endIndex - startIndex 103 if (resultLength < 0) { 104 resultLength = 0 105 } 106 107 let result = new ArrayBuffer(resultLength); 108 109 if (resultLength == 0) return result 110 for (let i = 0; i < resultLength; ++i) { 111 result.set(i, this.data[startIndex + i]) 112 } 113 114 return result 115 } 116 117 /** 118 * Creates a new ArrayBuffer with copy of bytes in range [begin;end) 119 * 120 * @param begin an inclusive index to start copying with 121 * 122 * @param end a last exclusive index to stop copying 123 * 124 * @returns data taken from current ArrayBuffer with respect to begin and end parameters 125 */ 126 public slice(begin: number, end?: number): ArrayBuffer { 127 if (end == undefined) return this.slice(begin as int) 128 else return this.slice(begin as int, end as int) 129 } 130 131 /** 132 * Creates a new ArrayBuffer with copy of bytes in range [begin;end) 133 * 134 * @param begin an inclusive index to start copying with 135 * 136 * @param end a last exclusive index to stop copying 137 * 138 * @returns data taken from current ArrayBuffer with respect to begin and end parameters 139 */ 140 public slice(begin: int, end?: int): ArrayBuffer { 141 if (end == undefined) return this.sliceInternal(begin, this.getByteLength()) as ArrayBuffer 142 else return this.sliceInternal(begin, end) as ArrayBuffer 143 } 144 145 /** 146 * Returns data at specified index. 147 * No such method in JS library, required for TypedArrays 148 * 149 * @param i index 150 * 151 * @returns byte at index 152 */ 153 internal override at(i: int): byte { 154 return this.data[i] 155 } 156 157 /** 158 * Sets data at specified index. 159 * No such method in JS library, required for TypedArrays 160 * 161 * @param b new value 162 * 163 * @param i index 164 */ 165 internal override set(i: int, b: byte) { 166 this.data[i] = b 167 } 168 169 internal override getByteLength(): int { 170 return this._byteLength; 171 } 172 173 get byteLength(): number { 174 return this._byteLength 175 } 176 177 /** 178 * Resizes the ArrayBuffer 179 * 180 * @param newLen new length 181 */ 182 public resize(newLen : number): void { 183 /* return */ this.resize(newLen as int) 184 } 185 186 /** 187 * Resizes the ArrayBuffer 188 * 189 * @param newLen new length 190 */ 191 //NOTE: return undefined 192 public resize(newLen : int): void { 193 let newData = new byte[newLen] 194 let size = min(newLen, this.data.length) 195 for (let i = 0; i < size; ++i) { 196 newData[i] = this.data[i] 197 } 198 this.data = newData 199 this._byteLength = newLen 200 } 201 202 public static native from(array: Object): ArrayBuffer; 203 204 private data: byte[] 205 /** Length in bytes */ 206 private _byteLength: int 207} 208 209/** 210 * Internal non-movable memory of a SharedArrayBuffer 211 */ 212class SharedMemory { 213 private data: byte[] 214 private waiterPtr: long 215 216 private constructor() {} 217 218 internal native static create(byteLength: int): SharedMemory; 219 220 internal at(i: int): byte { 221 return this.data[i] 222 } 223 224 internal set(i: int, value: byte): void { 225 this.data[i] = value 226 } 227 228 internal getByteLength(): int { 229 return this.data.length 230 } 231 232 internal native atomicAddI8(index: int, value: byte): byte; 233 234 internal native atomicAndI8(index: int, value: byte): byte; 235 236 internal native atomicCompareExchangeI8(index: int, expectedValue: byte, replacementValue: byte): byte; 237 238 internal native atomicExchangeI8(index: int, value: byte): byte; 239 240 internal native atomicLoadI8(index: int): byte; 241 242 internal native atomicOrI8(index: int, value: byte): byte; 243 244 internal native atomicStoreI8(index: int, value: byte): byte; 245 246 internal native atomicSubI8(index: int, value: byte): byte; 247 248 internal native atomicXorI8(index: int, value: byte): byte; 249 250 internal native atomicAddI16(index: int, value: short): short; 251 252 internal native atomicAndI16(index: int, value: short): short; 253 254 internal native atomicCompareExchangeI16(index: int, expectedValue: short, replacementValue: short): short; 255 256 internal native atomicExchangeI16(index: int, value: short): short; 257 258 internal native atomicLoadI16(index: int): short; 259 260 internal native atomicOrI16(index: int, value: short): short; 261 262 internal native atomicStoreI16(index: int, value: short): short; 263 264 internal native atomicSubI16(index: int, value: short): short; 265 266 internal native atomicXorI16(index: int, value: short): short; 267 268 internal native atomicAddI32(index: int, value: int): int; 269 270 internal native atomicAndI32(index: int, value: int): int; 271 272 internal native atomicCompareExchangeI32(index: int, expectedValue: int, replacementValue: int): int; 273 274 internal native atomicExchangeI32(index: int, value: int): int; 275 276 internal native atomicLoadI32(index: int): int; 277 278 internal native atomicOrI32(index: int, value: int): int; 279 280 internal native atomicStoreI32(index: int, value: int): int; 281 282 internal native atomicSubI32(index: int, value: int): int; 283 284 internal native atomicXorI32(index: int, value: int): int; 285 286 internal native atomicAddI64(index: int, value: long): long; 287 288 internal native atomicAndI64(index: int, value: long): long; 289 290 internal native atomicCompareExchangeI64(index: int, expectedValue: long, replacementValue: long): long; 291 292 internal native atomicExchangeI64(index: int, value: long): long; 293 294 internal native atomicLoadI64(index: int): long; 295 296 internal native atomicOrI64(index: int, value: long): long; 297 298 internal native atomicStoreI64(index: int, value: long): long; 299 300 internal native atomicSubI64(index: int, value: long): long; 301 302 internal native atomicXorI64(index: int, value: long): long; 303 304 internal native atomicAddU8(index: int, value: byte): byte; 305 306 internal native atomicAndU8(index: int, value: byte): byte; 307 308 internal native atomicCompareExchangeU8(index: int, expectedValue: byte, replacementValue: byte): byte; 309 310 internal native atomicExchangeU8(index: int, value: byte): byte; 311 312 internal native atomicLoadU8(index: int): byte; 313 314 internal native atomicOrU8(index: int, value: byte): byte; 315 316 internal native atomicStoreU8(index: int, value: byte): byte; 317 318 internal native atomicSubU8(index: int, value: byte): byte; 319 320 internal native atomicXorU8(index: int, value: byte): byte; 321 322 internal native atomicAddU16(index: int, value: short): short; 323 324 internal native atomicAndU16(index: int, value: short): short; 325 326 internal native atomicCompareExchangeU16(index: int, expectedValue: short, replacementValue: short): short; 327 328 internal native atomicExchangeU16(index: int, value: short): short; 329 330 internal native atomicLoadU16(index: int): short; 331 332 internal native atomicOrU16(index: int, value: short): short; 333 334 internal native atomicStoreU16(index: int, value: short): short; 335 336 internal native atomicSubU16(index: int, value: short): short; 337 338 internal native atomicXorU16(index: int, value: short): short; 339 340 internal native atomicAddU32(index: int, value: int): int; 341 342 internal native atomicAndU32(index: int, value: int): int; 343 344 internal native atomicCompareExchangeU32(index: int, expectedValue: int, replacementValue: int): int; 345 346 internal native atomicExchangeU32(index: int, value: int): int; 347 348 internal native atomicLoadU32(index: int): int; 349 350 internal native atomicOrU32(index: int, value: int): int; 351 352 internal native atomicStoreU32(index: int, value: int): int; 353 354 internal native atomicSubU32(index: int, value: int): int; 355 356 internal native atomicXorU32(index: int, value: int): int; 357 358 internal native atomicAddU64(index: int, value: long): long; 359 360 internal native atomicAndU64(index: int, value: long): long; 361 362 internal native atomicCompareExchangeU64(index: int, expectedValue: long, replacementValue: long): long; 363 364 internal native atomicExchangeU64(index: int, value: long): long; 365 366 internal native atomicLoadU64(index: int): long; 367 368 internal native atomicOrU64(index: int, value: long): long; 369 370 internal native atomicStoreU64(index: int, value: long): long; 371 372 internal native atomicSubU64(index: int, value: long): long; 373 374 internal native atomicXorU64(index: int, value: long): long; 375 376 internal native atomicWaitI32(byteOffset: int, value: int): int; 377 378 internal native atomicWaitI64(byteOffset: int, value: long): int; 379 380 internal native atomicTimedWaitI32(byteOffset: int, value: int, timeout: long): int; 381 382 internal native atomicTimedWaitI64(byteOffset: int, value: long, timeout: long): int; 383 384 internal native atomicNotify(byteOffset: int): int; 385 386 internal native atomicBoundedNotify(byteOffset: int, count: int): int; 387} 388 389export class SharedArrayBuffer extends Buffer { 390 private sharedMemory: SharedMemory 391 392 public constructor(byteLength: int) { 393 super() 394 this.sharedMemory = SharedMemory.create(byteLength) 395 } 396 397 internal getSharedMemory(): SharedMemory { 398 return this.sharedMemory 399 } 400 401 internal override getByteLength(): int { 402 return this.sharedMemory.getByteLength() 403 } 404 405 /** 406 * Returns data at specified index. 407 * No such method in JS library, required for TypedArrays 408 * 409 * @param i index 410 * 411 * @returns byte at index 412 */ 413 internal override at(i: int): byte { 414 return this.sharedMemory.at(i) 415 } 416 417 /** 418 * Sets data at specified index. 419 * No such method in JS library, required for TypedArrays 420 * 421 * @param b new value 422 * 423 * @param i index 424 */ 425 internal override set(i: int, b: byte) { 426 this.sharedMemory.set(i, b) 427 } 428 429 internal override sliceInternal(begin: int, end: int): Buffer { 430 // TODO(egor-porsev): code duplication 431 let byteLength = this.getByteLength() 432 if (begin < -byteLength) { 433 begin = 0 434 } 435 if (begin < 0) { 436 begin = byteLength + begin 437 } 438 if (begin >= byteLength) { 439 begin = byteLength 440 } 441 442 if (end < -byteLength) { 443 end = 0 444 } 445 if (end < 0) { 446 end = byteLength + end 447 } 448 if (end >= byteLength) { 449 end = byteLength 450 } 451 452 if (end <= begin) { 453 return new SharedArrayBuffer(0) 454 } 455 456 let len = end - begin 457 if (len < 0) { 458 len = 0 459 } 460 let res = new SharedArrayBuffer(len); 461 for (let i = 0; i < len; ++i) { 462 res.set(i, this.at(begin + i)) 463 } 464 return res 465 } 466 467 /** 468 * Creates a new ArrayBuffer with copy of bytes in range [begin;end) 469 * 470 * @param begin an inclusive index to start copying with 471 * 472 * @param end a last exclusive index to stop copying 473 * 474 * @returns data taken from current ArrayBuffer with respect to begin and end parameters 475 */ 476 public slice(begin: int, end: int): SharedArrayBuffer { 477 return this.sliceInternal(begin, end) as SharedArrayBuffer 478 } 479 480 public grow(newLength: int) { 481 // grow() cannot shrink the array and cannot expand it beyond 'maxByteLength'. 482 // Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer/grow 483 // This is because the underlying SharedMemory object is non-movable and cannot be reallocated. 484 // 485 // Possible implementation: when SharedArrayBuffer(curByteLength, { maxByteLength }) is constructed 486 // preallocate a SharedMemory object of size 'maxByteLength' but the user should observe the length to be 'curByteLength'. 487 // In other words, SharedMemory may be implemented as (pointer to data, current length, max capacity). 488 throw new Error("not implemented"); 489 } 490} 491 492export type ArrayBufferLike = ArrayBuffer | SharedArrayBuffer 493