• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15interface ArkPrivate {
16  FastBuffer: number;
17  Load(key: number): Object;
18}
19let FastBufferInner = undefined;
20let arkPritvate: ArkPrivate = globalThis.ArkPrivate || undefined;
21FastBufferInner = arkPritvate.Load(arkPritvate.FastBuffer);
22
23function getTypeName(obj: unknown): string {
24  if (obj === null) {
25    return 'null';
26  }
27  if (typeof obj !== 'object') {
28    return typeof obj;
29  }
30  if (obj != null && obj.constructor != null) {
31    return obj.constructor.name;
32  }
33  return 'unknown';
34}
35
36class BusinessError extends Error {
37  code: number;
38  constructor(message: string, errorNumber: number) {
39    super(message);
40    this.name = 'BusinessError';
41    this.code = errorNumber;
42  }
43}
44
45const ERROR_CODES = {
46  TYPE_ERROR: 401, // 401: TYPE_ ERROR code value
47  RANGE_ERROR: 10200001, // 10200001: RANGE_ERROR code value
48  BUFFER_SIZE_ERROR: 10200009, // 10200009: BUFFER_SIZE_ERROR code value
49  PROPERTY_TYPE_ERROR: 10200013, // 10200013: TYPE_ ERROR code value
50  ARRAY_BUFFER_IS_NULL_OR_DETACHED: 10200068 // 10200013: TYPE_ ERROR code value
51};
52
53let errorMap = {
54  'typeError': ERROR_CODES.TYPE_ERROR,
55  'rangeError': ERROR_CODES.RANGE_ERROR,
56  'bufferSizeError': ERROR_CODES.BUFFER_SIZE_ERROR,
57  'typeErrorForProperty': ERROR_CODES.PROPERTY_TYPE_ERROR,
58  'arrayBufferIsDetachedError': ERROR_CODES.ARRAY_BUFFER_IS_NULL_OR_DETACHED
59};
60
61enum TypeErrorCategories {
62  COMMON = 0,
63  SIZE,
64  ENCODING,
65  PROPERTY
66};
67enum RangeErrorCategories {
68  WHOLE = 0,
69  LEFT
70};
71
72const UINT32MAX = 4294967295;
73const INT64_MAX = 2n ** 63n - 1n;
74const UINT64_MAX = 2n ** 64n - 1n;
75
76class ErrorMessage {
77  public errorNumber: number = 0;
78  public argument: string = '';
79  public types: string[] = [];
80  public receivedObj: unknown = '';
81  public rangeLeft: string | bigint | number = 0;
82  public rangeRight: string | bigint | number = 0;
83
84  public typeErrorCat: TypeErrorCategories = TypeErrorCategories.COMMON;
85  public rangeErrorCat: RangeErrorCategories = RangeErrorCategories.WHOLE;
86
87  constructor(errNo: number, argument?: string) {
88    this.errorNumber = errNo;
89    this.argument = argument === undefined ? '' : argument;
90  }
91
92  public setTypeInfo(types: string[], receivedObj: unknown): ErrorMessage {
93    this.types = types;
94    this.receivedObj = receivedObj;
95    return this;
96  }
97
98  public setSizeTypeInfo(types: string[], receivedObj: unknown): ErrorMessage {
99    this.types = types;
100    this.receivedObj = receivedObj;
101    this.typeErrorCat = TypeErrorCategories.SIZE;
102    return this;
103  }
104
105  public setEncodingTypeInfo(types: string[], receivedObj: unknown): ErrorMessage {
106    this.types = types;
107    this.receivedObj = receivedObj;
108    this.typeErrorCat = TypeErrorCategories.ENCODING;
109    return this;
110  }
111
112  public setProperty(argument: string): ErrorMessage {
113    this.typeErrorCat = TypeErrorCategories.PROPERTY;
114    this.argument = argument;
115    return this;
116  }
117
118  public setRangeInfo(rangeLeft: string | bigint | number, rangeRight: string | bigint | number, receivedObj: unknown): ErrorMessage {
119    this.rangeLeft = rangeLeft;
120    this.rangeRight = rangeRight;
121    this.receivedObj = receivedObj;
122    return this;
123  }
124
125  public setRangeLeftInfo(rangeLeft: string | number, receivedObj: unknown): ErrorMessage {
126    this.rangeLeft = rangeLeft;
127    this.receivedObj = receivedObj;
128    this.rangeErrorCat = RangeErrorCategories.LEFT;
129    return this;
130  }
131
132  public setSizeInfo(receivedObj: string): ErrorMessage {
133    this.receivedObj = receivedObj;
134    return this;
135  }
136
137  private getErrorTypeStrings(types: string[]): string {
138    let ret = types.join(', ');
139    ret = ret.replace(',', ' or');
140    return ret;
141  }
142
143  private getArgumentStr(flag: boolean): string {
144    if (flag) {
145      return 'Parameter error. The type of "' + this.argument + '" must be ';
146    } else {
147      return 'The type of "' + this.argument + '" must be ';
148    }
149  }
150
151  private getTypeString(flag: boolean): string {
152    let str = '';
153    switch (this.typeErrorCat) {
154      case TypeErrorCategories.COMMON:
155        str += this.getArgumentStr(flag) + this.getErrorTypeStrings(this.types) +
156          '. Received value is: ' + getTypeName(this.receivedObj);
157        break;
158      case TypeErrorCategories.SIZE:
159        str += this.getArgumentStr(flag) + this.getErrorTypeStrings(this.types) +
160          ' and the value cannot be negative. Received value is: ' + getTypeName(this.receivedObj);
161        break;
162      case TypeErrorCategories.ENCODING:
163        str += this.getArgumentStr(flag) + this.getErrorTypeStrings(this.types) +
164          '. the encoding ' + this.receivedObj + ' is unknown';
165        break;
166      case TypeErrorCategories.PROPERTY:
167        str += this.argument + ' cannot be set for the buffer that has only a getter';
168      default:
169        break;
170    }
171    return str;
172  }
173
174  private getRangeString(): string {
175    let str = '';
176    switch (this.rangeErrorCat) {
177      case RangeErrorCategories.WHOLE:
178        str += 'The value of "' + this.argument + '" is out of range. It must be >= ' + this.rangeLeft +
179          ' and <= ' + this.rangeRight + '. Received value is: ' + this.receivedObj;
180        break;
181      case RangeErrorCategories.LEFT:
182        str += 'The value of "' + this.argument + '" is out of range. It must be >= ' + this.rangeLeft +
183          '. Received value is: ' + this.receivedObj;
184        break;
185      default:
186        break;
187    }
188    return str;
189  }
190
191  public getString(): string {
192    let str = '';
193    switch (this.errorNumber) {
194      case ERROR_CODES.TYPE_ERROR:
195        str = this.getTypeString(true);
196        break;
197      case ERROR_CODES.PROPERTY_TYPE_ERROR:
198        str = this.getTypeString(false);
199        break;
200      case ERROR_CODES.RANGE_ERROR:
201        str = this.getRangeString();
202        break;
203      case ERROR_CODES.BUFFER_SIZE_ERROR:
204        str = 'The buffer size must be a multiple of ' + this.receivedObj;
205        break;
206      default:
207        break;
208    }
209    return str;
210  }
211}
212
213const initialPoolSize: number = 8 * 1024; // 8 * 1024 : initialPoolSize number
214let poolSize: number;
215let poolOffset: number;
216let pool: FastBuffer;
217const oneByte: number = 1; // 1 : one Byte
218const twoBytes: number = 2; // 2 : two Bytes
219const threeBytes: number = 3; // 3 : three Bytes
220const fourBytes: number = 4; // 4 : four Bytes
221const fiveBytes: number = 5; // 5 : five Bytes
222const sixBytes: number = 6; // 6 : six Bytes
223const sevenBytes: number = 7; // 7 : seven Bytes
224const eightBytes: number = 8; // 8 : eight Bytes
225
226type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array |
227Int32Array | Uint32Array | Float32Array | Float64Array;
228type BackingType = FastBuffer | TypedArray | DataView | ArrayBuffer | SharedArrayBuffer;
229
230const bufferEncoding = ['ascii', 'utf8', 'utf-8', 'utf16le', 'utf-16le', 'ucs2', 'ucs-2',
231  'base64', 'base64url', 'latin1', 'binary', 'hex'];
232
233interface FastBuffer {
234    length: number;
235    buffer: ArrayBuffer;
236    byteOffset: number;
237    fill(value: string | FastBuffer | Uint8Array | number, offset?: number, end?: number, encoding?: string): FastBuffer;
238    compare(target: FastBuffer | Uint8Array, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): -1 | 0 | 1;
239    copy(target: FastBuffer | Uint8Array, targetStart?: number, sourceStart?: number, sourceEnd?: number): number;
240    equals(otherBuffer: Uint8Array | FastBuffer): boolean;
241    includes(value: string | number | FastBuffer | Uint8Array, byteOffset?: number, encoding?: string): boolean;
242    indexOf(value: string | number | FastBuffer | Uint8Array, byteOffset?: number, encoding?: string): number;
243    keys(): IterableIterator<number>;
244    values(): IterableIterator<number>;
245    entries(): IterableIterator<[
246        number,
247        number
248    ]>;
249    lastIndexOf(value: string | number | FastBuffer | Uint8Array, byteOffset?: number, encoding?: string): number;
250    readBigInt64BE(offset?: number): bigint;
251    readBigInt64LE(offset?: number): bigint;
252    readBigUInt64BE(offset?: number): bigint;
253    readBigUInt64LE(offset?: number): bigint;
254    readDoubleBE(offset?: number): number;
255    readDoubleLE(offset?: number): number;
256    readFloatBE(offset?: number): number;
257    readFloatLE(offset?: number): number;
258    readInt8(offset?: number): number;
259    readInt16BE(offset?: number): number;
260    readInt16LE(offset?: number): number;
261    readInt32BE(offset?: number): number;
262    readInt32LE(offset?: number): number;
263    readIntBE(offset: number, byteLength: number): number;
264    readIntLE(offset: number, byteLength: number): number;
265    readUInt8(offset?: number): number;
266    readUInt16BE(offset?: number): number;
267    readUInt16LE(offset?: number): number;
268    readUInt32BE(offset?: number): number;
269    readUInt32LE(offset?: number): number;
270    readUIntBE(offset: number, byteLength: number): number;
271    readUIntLE(offset: number, byteLength: number): number;
272    subarray(start?: number, end?: number): FastBuffer;
273    swap16(): FastBuffer;
274    swap32(): FastBuffer;
275    swap64(): FastBuffer;
276    toJSON(): Object;
277    toString(encoding?: string, start?: number, end?: number): string;
278    write(str: string, offset?: number, length?: number, encoding?: string): number;
279    writeBigInt64BE(value: bigint, offset?: number): number;
280    writeBigInt64LE(value: bigint, offset?: number): number;
281    writeBigUInt64BE(value: bigint, offset?: number): number;
282    writeBigUInt64LE(value: bigint, offset?: number): number;
283    writeDoubleBE(value: number, offset?: number): number;
284    writeDoubleLE(value: number, offset?: number): number;
285    writeFloatBE(value: number, offset?: number): number;
286    writeFloatLE(value: number, offset?: number): number;
287    writeInt8(value: number, offset?: number): number;
288    writeInt16BE(value: number, offset?: number): number;
289    writeInt16LE(value: number, offset?: number): number;
290    writeInt32BE(value: number, offset?: number): number;
291    writeInt32LE(value: number, offset?: number): number;
292    writeIntBE(value: number, offset: number, byteLength: number): number;
293    writeIntLE(value: number, offset: number, byteLength: number): number;
294    writeUInt8(value: number, offset?: number): number;
295    writeUInt16BE(value: number, offset?: number): number;
296    writeUInt16LE(value: number, offset?: number): number;
297    writeUInt32BE(value: number, offset?: number): number;
298    writeUInt32LE(value: number, offset?: number): number;
299    writeUIntBE(value: number, offset: number, byteLength: number): number;
300    writeUIntLE(value: number, offset: number, byteLength: number): number;
301}
302
303class FastBuffer extends FastBufferInner {
304  constructor(value: number | FastBuffer | Uint8Array | ArrayBuffer | SharedArrayBuffer | Array<number> | string,
305    byteOffsetOrEncoding?: number | string, length?: number) {
306      super(value, byteOffsetOrEncoding, length);
307  }
308
309  toString(encoding: string = 'utf8', start: number = 0, end: number = this.length): string {
310    if (!encoding) {
311      encoding = 'utf8';
312    }
313    let enc = encoding.toLowerCase();
314    start = isNaN(start) ? 0 : (Number(start) < 0 ? 0 : Number(start));
315    end = isNaN(end) ? 0 : Number(end);
316    if (start >= this.length || start > end) {
317      return '';
318    }
319    return super.toString(enc, start, end);
320  }
321
322  indexOf(value: string | number | FastBuffer | Uint8Array, byteOffset: number = 0, encoding: string = 'utf8'): number {
323    if (typeof byteOffset === 'string') {
324      encoding = byteOffset;
325    }
326    if (typeof byteOffset !== 'number') {
327      byteOffset = 0;
328    }
329    if (!encoding) {
330      encoding = 'utf8';
331    }
332    encoding = encoding.toLowerCase();
333    return super.indexOf(value, byteOffset, encoding);
334  }
335
336  lastIndexOf(value: string | number | FastBuffer | Uint8Array, byteOffset: number = this.length,
337    encoding: string = 'utf8'): number {
338    if (typeof byteOffset === 'string') {
339      encoding = byteOffset;
340    }
341    if (typeof byteOffset !== 'number') {
342      byteOffset = 0;
343    }
344    if (!encoding) {
345      encoding = 'utf8';
346    }
347    encoding = encoding.toLowerCase();
348    return super.lastIndexOf(value, byteOffset, encoding);
349  }
350
351  copy(target: FastBuffer | Uint8Array, targetStart: number = 0, sourceStart: number = 0,
352    sourceEnd: number = this.length): number {
353    targetStart = isNaN(targetStart) ? 0 : Number(targetStart);
354    sourceStart = isNaN(sourceStart) ? 0 : Number(sourceStart);
355    sourceEnd = isNaN(sourceEnd) ? this.length : Number(sourceEnd);
356    return super.copy(target, targetStart, sourceStart, sourceEnd);
357  }
358
359  fill(value: string | FastBuffer | Uint8Array | number, offset: number = 0, end: number = this.length,
360    encoding: string = 'utf8'): FastBuffer {
361    return super.fill(value, offset, end, encoding);
362  }
363
364  compare(target: FastBuffer | Uint8Array, targetStart: number = 0, targetEnd: number = target.length,
365    sourceStart: number = 0, sourceEnd: number = this.length): 0 | 1 | -1 {
366    if (targetStart === null) {
367      targetStart = 0;
368    }
369    if (targetEnd === null) {
370      targetEnd = target.length;
371    }
372    if (sourceStart === null) {
373      sourceStart = 0;
374    }
375    if (sourceEnd === null) {
376      sourceEnd = this.length;
377    }
378    typeErrorCheck(target, ['FastBuffer', 'Uint8Array'], 'target');
379    return super.compare(target, targetStart, targetEnd, sourceStart, sourceEnd);
380  }
381
382  toJSON(): Object {
383    if (this.length <= 0) {
384      return { type: 'FastBuffer', data: [] };
385    }
386    let data = new Array<number>;
387    let len = this.length;
388    for (let i = 0; i < len; i++) {
389      data.push(this[i]);
390    }
391    return { type: 'FastBuffer', data: data };
392  }
393
394  subarray(start: number = 0, end: number = this.length): FastBuffer {
395    let newBuf: FastBuffer = new FastBuffer(0);
396    start = isNaN(start) ? 0 : Number(start);
397    end = isNaN(end) ? 0 : Number(end);
398    end = (end > this.length) ? this.length : end;
399    if (start < 0 || end < 0 || end <= start) {
400      return newBuf;
401    }
402    return new FastBuffer(this.buffer, start, end - start);
403  }
404
405  writeBigInt64BE(value: bigint, offset: number = 0): number {
406    if (typeof value !== 'bigint') {
407      throw typeError(value, 'value', ['bigint']);
408    }
409    rangeErrorCheck(value, 'value', -INT64_MAX, INT64_MAX);
410    return super.writeBigInt64BE(value, offset);
411  }
412
413  writeBigInt64LE(value: bigint, offset: number = 0): number {
414    if (typeof value !== 'bigint') {
415      throw typeError(value, 'value', ['bigint']);
416    }
417    rangeErrorCheck(value, 'value', -INT64_MAX, INT64_MAX);
418    return super.writeBigInt64LE(value, offset);
419  }
420
421  writeBigUInt64BE(value: bigint, offset: number = 0): number {
422    if (typeof value !== 'bigint') {
423      throw typeError(value, 'value', ['bigint']);
424    }
425    rangeErrorCheck(value, 'value', 0, UINT64_MAX);
426    return super.writeBigUInt64BE(value, offset);
427  }
428
429  writeBigUInt64LE(value: bigint, offset: number = 0): number {
430    if (typeof value !== 'bigint') {
431      throw typeError(value, 'value', ['bigint']);
432    }
433    rangeErrorCheck(value, 'value', 0, UINT64_MAX);
434    return super.writeBigUInt64LE(value, offset);
435  }
436
437  swap16(): FastBuffer {
438    const len = this.length;
439    const dealLen: number = twoBytes;
440    if (len % dealLen !== 0) {
441      throw bufferSizeError('16-bits');
442    }
443    return this.reverseBits(dealLen);
444  }
445
446  swap32(): FastBuffer {
447    const len = this.length;
448    const dealLen: number = 4; // Process every 4 bits
449    if (len % dealLen !== 0) {
450      throw bufferSizeError('32-bits');
451    }
452    return this.reverseBits(dealLen);
453  }
454
455  swap64(): FastBuffer {
456    const len = this.length;
457    const dealLen: number = eightBytes;
458    if (len % dealLen !== 0) {
459      throw bufferSizeError('64-bits');
460    }
461    return this.reverseBits(dealLen);
462  }
463
464  reverseBits(dealNum: number): FastBuffer {
465    const len: number = this.length;
466    const dealLen: number = dealNum;
467    for (let i = 0; i < len / dealLen; i++) {
468      let times: number = 0;
469      let startIndex: number = dealLen * i;
470      let endIndex: number = startIndex + dealLen - 1;
471      while (times < dealLen / twoBytes) {
472        let tmp = this[startIndex + times];
473        this[startIndex + times] = this[endIndex - times];
474        this[endIndex - times] = tmp;
475        times++;
476      }
477    }
478    return this;
479  }
480
481  [Symbol.iterator](): IterableIterator<[number, number]> {
482    return this.entries();
483  }
484}
485
486function createBufferFromArrayBuffer(value: ArrayBuffer | SharedArrayBuffer,
487  offsetOrEncoding?: number | string, length?: number): FastBuffer {
488  offsetOrEncoding = isNaN(Number(offsetOrEncoding)) ? 0 : Number(offsetOrEncoding);
489  let valueLength = 0;
490  try {
491    valueLength = value.byteLength;
492  } catch(e) {
493    throw new BusinessError('The underlying ArrayBuffer is null or detached.', errorMap.arrayBufferIsDetachedError);
494  }
495  const maxLength: number = valueLength - offsetOrEncoding;
496  if (length === undefined) {
497    length = maxLength;
498  } else {
499    length = isNaN(Number(length)) ? 0 : Number(length);
500  }
501  rangeErrorCheck(offsetOrEncoding, 'byteOffset', 0, valueLength);
502  rangeErrorCheck(length, 'length', 0, maxLength);
503  return new FastBuffer(value, offsetOrEncoding, length);
504}
505
506function from(value: FastBuffer | Uint8Array | ArrayBuffer | SharedArrayBuffer | string | object | Array<number>,
507  offsetOrEncoding?: number | string, length?: number): FastBuffer {
508  if (typeof value === 'string') {
509    if (!offsetOrEncoding || typeof offsetOrEncoding === 'number') {
510      offsetOrEncoding = 'utf8';
511    }
512    return fromString(value, offsetOrEncoding);
513  }
514  if (value instanceof FastBuffer || value instanceof Uint8Array || value instanceof Array) {
515    return new FastBuffer(value);
516  }
517  if (value instanceof ArrayBuffer || value instanceof SharedArrayBuffer) {
518    return createBufferFromArrayBuffer(value, offsetOrEncoding, length);
519  }
520  if (typeof value === 'object' && value !== null) {
521    const valueOf = value.valueOf && value.valueOf();
522    if (valueOf != null && valueOf !== value &&
523      (typeof valueOf === 'string' || typeof valueOf === 'object')) {
524      return from(valueOf, offsetOrEncoding, length);
525    }
526    if (typeof value[Symbol.toPrimitive] === 'function') {
527      const primitive = value[Symbol.toPrimitive]('string');
528      if (typeof primitive === 'string' && typeof offsetOrEncoding === 'string') {
529        return fromString(primitive, offsetOrEncoding);
530      }
531    }
532  }
533  throw typeError(value, 'value', ['FastBuffer', 'ArrayBuffer', 'Array', 'Array-like', 'string', 'object']);
534}
535
536function alloc(size: number, fill?: string | FastBuffer | number, encoding?: string): FastBuffer {
537  if (size < 0 || size > UINT32MAX) {
538    let msg = 'Parameter error. The type of "size" must be ' + 'number' +
539      ' and the value cannot be negative. Received value is: ' + Number(size).toString();
540    throw new BusinessError(msg, errorMap.typeError);
541  }
542  const buf = new FastBuffer(size);
543  if (arguments.length === twoBytes && fill !== undefined && fill !== 0) {
544    buf.fill(fill);
545    return buf;
546  } else if (arguments.length === 3) { // 3 is array->maxIndex
547    if (!encoding) {
548      encoding = 'utf-8';
549    }
550    buf.fill(fill, 0, buf.length, encoding);
551    return buf;
552  }
553  buf.fill(0);
554  return buf;
555}
556
557function allocUninitialized(size: number): FastBuffer {
558  if (size < 0 || size > UINT32MAX) {
559    let msg = 'Parameter error. The type of "size" must be ' + 'number' +
560      ' and the value cannot be negative. Received value is: ' + Number(size).toString();
561    throw new BusinessError(msg, errorMap.typeError);
562  }
563  const buf = new FastBuffer(size);
564  return buf;
565}
566
567function bufferSizeError(size: string): BusinessError {
568  let msg = new ErrorMessage(errorMap.bufferSizeError).setSizeInfo(size).getString();
569  return new BusinessError(msg, errorMap.bufferSizeError);
570}
571
572function typeError(param: unknown, paramName: string, excludedTypes: string[]): BusinessError {
573  let msg = new ErrorMessage(errorMap.typeError, paramName).setTypeInfo(excludedTypes, param).getString();
574  return new BusinessError(msg, errorMap.typeError);
575}
576
577function rangeErrorCheck(param: number | bigint, paramName: string,
578                         rangeLeft: number | bigint, rangeRight: number | bigint): void {
579  if (param < rangeLeft || param > rangeRight) {
580    throw rangeError(paramName, rangeLeft, rangeRight, param);
581  }
582}
583
584function rangeError(paramName: string, rangeLeft: string | bigint | number, rangeRight: string | bigint | number,
585  receivedValue: number | bigint): BusinessError {
586  let msg =
587    new ErrorMessage(errorMap.rangeError, paramName).setRangeInfo(rangeLeft, rangeRight, receivedValue).getString();
588  return new BusinessError(msg, errorMap.rangeError);
589}
590
591function fromString(value: string, encoding: string): FastBuffer {
592  let enc = encoding.toLowerCase();
593  return new FastBuffer(value, enc);
594}
595
596function createPool(): void {
597  poolSize = initialPoolSize;
598  pool = new FastBuffer(poolSize);
599  poolOffset = 0;
600}
601
602function alignPool(): void {
603  if (poolOffset & 0x7) {
604    poolOffset |= 0x7; // 0x7 : align offset based of 8-bits
605    poolOffset++;
606  }
607}
608
609function allocUninitializedFromPool(size: number): FastBuffer {
610  if (size < 0 || size > UINT32MAX) {
611    let msg = 'Parameter error. The type of "size" must be ' + 'number' +
612      ' and the value cannot be negative. Received value is: ' + Number(size).toString();
613    throw new BusinessError(msg, errorMap.typeError);
614  }
615  return new FastBuffer(size);
616}
617
618function getBase64ByteLength(str: string): number {
619  let bytes = str.length;
620  let pos = 0;
621  while (bytes > 1 && (pos = str.indexOf('=', pos)) !== -1) { // Find '=' in str and calculate the length of str
622    bytes--;
623    pos++;
624  }
625  return (bytes * threeBytes) >>> twoBytes;
626}
627
628function getUtf8ByteLength(str: string): number {
629  let byteLength = 0;
630  for (let i = 0; i < str.length; i++) {
631    const code = str.charCodeAt(i);
632    if (code >= 0xD800 && code <= 0xDBFF) {
633      const nextCode = str.charCodeAt(i + 1);
634      if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) {
635        byteLength += 4;
636        i++;
637        continue;
638      }
639    }
640    if (code <= 0x7F) {
641      byteLength += 1;
642    } else if (code <= 0x7FF) {
643      byteLength += 2;
644    } else if (code <= 0xFFFF) {
645      byteLength += 3;
646    }
647  }
648  return byteLength;
649}
650
651function getEncodingByteLength(str: string, type: string): number {
652  type = type.toLowerCase();
653  switch (type) {
654    case 'utf8':
655    case 'utf-8':
656      return getUtf8ByteLength(str);
657    case 'ucs2':
658    case 'ucs-2':
659      return str.length * twoBytes;
660    case 'ascii':
661      return str.length;
662    case 'binary':
663    case 'latin1':
664      return str.length;
665    case 'utf16le':
666    case 'utf-16le':
667      return str.length * twoBytes;
668    case 'base64':
669    case 'base64url':
670      return getBase64ByteLength(str);
671    case 'hex':
672      return str.length >>> 1; // 1 : one-half
673    default:
674      return undefined;
675  }
676}
677
678function isTypedArray(self: unknown): boolean {
679  let typeArr = [Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array,
680    Int32Array, Uint32Array, Float32Array, Float64Array];
681  for (let i = 0, len = typeArr.length; i < len; i++) {
682    if (self instanceof typeArr[i]) {
683      return true;
684    }
685  }
686  return false;
687}
688
689function byteLength(string: string | BackingType, encoding: string = 'utf8'): number {
690    if (string instanceof FastBuffer) {
691      return string.length;
692    } else if (typeof string === 'string') {
693      if (string.length === 0) {
694        return 0;
695      }
696      if (!encoding) {
697        encoding = 'utf8';
698      }
699      return getEncodingByteLength(string, encoding);
700    } else {
701        if (isTypedArray(string) || string instanceof DataView ||
702          string instanceof ArrayBuffer || string instanceof SharedArrayBuffer) {
703          return string.byteLength;
704        }
705        throw typeError(string, 'string', ['string', 'FastBuffer', 'ArrayBuffer']);
706    }
707}
708
709
710function isBuffer(obj: Object): boolean {
711  return obj instanceof FastBuffer;
712}
713
714function isEncoding(enc: string): boolean {
715  if (!enc) {
716    return false;
717  }
718  enc = enc.toLowerCase();
719  if (bufferEncoding.includes(enc)) {
720    return true;
721  }
722  return false;
723}
724
725function transcode(source: FastBuffer | Uint8Array, fromEnc: string, toEnc: string): FastBuffer {
726  typeErrorCheck(source, ['FastBuffer', 'Uint8Array'], 'source');
727  typeErrorCheck(fromEnc, ['string'], 'fromEnc');
728  typeErrorCheck(toEnc, ['string'], 'toEnc');
729  let from = source.toString(fromEnc);
730  return fromString(from, toEnc);
731}
732
733function compare(buf1: FastBuffer | Uint8Array, buf2: FastBuffer | Uint8Array): 1 | 0 | -1 {
734  if (!(buf1 instanceof FastBuffer) && !(buf1 instanceof Uint8Array)) {
735    throw new BusinessError(new ErrorMessage(errorMap.typeError, 'buf1').setTypeInfo(['FastBuffer', 'Uint8Array'],
736      getTypeName(buf1)).getString(), errorMap.typeError);
737  }
738  if (!(buf2 instanceof FastBuffer) && !(buf2 instanceof Uint8Array)) {
739    throw new BusinessError(new ErrorMessage(errorMap.typeError, 'buf2').setTypeInfo(['FastBuffer', 'Uint8Array'],
740      getTypeName(buf2)).getString(), errorMap.typeError);
741  }
742
743  let tempBuf: FastBuffer;
744  if (buf1 instanceof FastBuffer) {
745    return buf1.compare(buf2, 0, buf2.length, 0, buf1.length);
746  } else {
747    tempBuf = new FastBuffer(buf1);
748    return tempBuf.compare(buf2, 0, buf2.length, 0, tempBuf.length);
749  }
750}
751
752function typeErrorCheck(param: unknown, types: string[], paramName: string): void {
753  let typeName = getTypeName(param);
754  if (!types.includes(typeName)) {
755    throw typeError(param, paramName, types);
756  }
757}
758
759function concat(list: FastBuffer[] | Uint8Array[], totalLength?: number): FastBuffer {
760  typeErrorCheck(list, ['Array'], 'list');
761  if (!(typeof totalLength === 'number' || typeof totalLength === 'undefined' || 'null')) {
762    throw typeError(totalLength, 'totalLength', ['number']);
763  }
764  if (list.length === 0) {
765    return new FastBuffer(0);
766  }
767  if (!totalLength) {
768    totalLength = 0;
769    for (let i = 0, len = list.length; i < len; i++) {
770      let buf = list[i];
771      if (buf instanceof Uint8Array || buf instanceof FastBuffer) {
772        totalLength += list[i].length;
773      }
774    }
775  }
776
777  rangeErrorCheck(totalLength, 'totalLength', 0, UINT32MAX);
778
779  let buffer = allocUninitializedFromPool(totalLength);
780  let offset = 0;
781  for (let i = 0, len = list.length; i < len; i++) {
782    const buf = list[i];
783    if (buf instanceof Uint8Array) {
784      buf.forEach((val) => buffer[offset++] = val);
785    } else if (buf instanceof FastBuffer) {
786      buf.copy(buffer, offset);
787      offset += buf.length;
788    }
789  }
790  return buffer;
791}
792
793export default {
794  FastBuffer,
795  from,
796  alloc,
797  allocUninitializedFromPool,
798  allocUninitialized,
799  byteLength,
800  isBuffer,
801  isEncoding,
802  compare,
803  concat,
804  transcode
805};
806