mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-26 10:21:55 +00:00
242 lines
7.4 KiB
JavaScript
242 lines
7.4 KiB
JavaScript
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
|
// This module is browser compatible.
|
|
/**
|
|
* Provides helper functions to manipulate `Uint8Array` byte slices that are not
|
|
* included on the `Uint8Array` prototype.
|
|
*
|
|
* @module
|
|
*/
|
|
/** Returns the index of the first occurrence of the needle array in the source
|
|
* array, or -1 if it is not present.
|
|
*
|
|
* A start index can be specified as the third argument that begins the search
|
|
* at that given index. The start index defaults to the start of the array.
|
|
*
|
|
* The complexity of this function is O(source.lenth * needle.length).
|
|
*
|
|
* ```ts
|
|
* import { indexOfNeedle } from "./mod.ts";
|
|
* const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]);
|
|
* const needle = new Uint8Array([1, 2]);
|
|
* console.log(indexOfNeedle(source, needle)); // 1
|
|
* console.log(indexOfNeedle(source, needle, 2)); // 3
|
|
* ```
|
|
*/
|
|
export function indexOfNeedle(source, needle, start = 0) {
|
|
if (start >= source.length) {
|
|
return -1;
|
|
}
|
|
if (start < 0) {
|
|
start = Math.max(0, source.length + start);
|
|
}
|
|
const s = needle[0];
|
|
for (let i = start; i < source.length; i++) {
|
|
if (source[i] !== s)
|
|
continue;
|
|
const pin = i;
|
|
let matched = 1;
|
|
let j = i;
|
|
while (matched < needle.length) {
|
|
j++;
|
|
if (source[j] !== needle[j - pin]) {
|
|
break;
|
|
}
|
|
matched++;
|
|
}
|
|
if (matched === needle.length) {
|
|
return pin;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
/** Returns the index of the last occurrence of the needle array in the source
|
|
* array, or -1 if it is not present.
|
|
*
|
|
* A start index can be specified as the third argument that begins the search
|
|
* at that given index. The start index defaults to the end of the array.
|
|
*
|
|
* The complexity of this function is O(source.lenth * needle.length).
|
|
*
|
|
* ```ts
|
|
* import { lastIndexOfNeedle } from "./mod.ts";
|
|
* const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]);
|
|
* const needle = new Uint8Array([1, 2]);
|
|
* console.log(lastIndexOfNeedle(source, needle)); // 5
|
|
* console.log(lastIndexOfNeedle(source, needle, 4)); // 3
|
|
* ```
|
|
*/
|
|
export function lastIndexOfNeedle(source, needle, start = source.length - 1) {
|
|
if (start < 0) {
|
|
return -1;
|
|
}
|
|
if (start >= source.length) {
|
|
start = source.length - 1;
|
|
}
|
|
const e = needle[needle.length - 1];
|
|
for (let i = start; i >= 0; i--) {
|
|
if (source[i] !== e)
|
|
continue;
|
|
const pin = i;
|
|
let matched = 1;
|
|
let j = i;
|
|
while (matched < needle.length) {
|
|
j--;
|
|
if (source[j] !== needle[needle.length - 1 - (pin - j)]) {
|
|
break;
|
|
}
|
|
matched++;
|
|
}
|
|
if (matched === needle.length) {
|
|
return pin - needle.length + 1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
/** Returns true if the prefix array appears at the start of the source array,
|
|
* false otherwise.
|
|
*
|
|
* The complexity of this function is O(prefix.length).
|
|
*
|
|
* ```ts
|
|
* import { startsWith } from "./mod.ts";
|
|
* const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]);
|
|
* const prefix = new Uint8Array([0, 1, 2]);
|
|
* console.log(startsWith(source, prefix)); // true
|
|
* ```
|
|
*/
|
|
export function startsWith(source, prefix) {
|
|
for (let i = 0, max = prefix.length; i < max; i++) {
|
|
if (source[i] !== prefix[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
/** Returns true if the suffix array appears at the end of the source array,
|
|
* false otherwise.
|
|
*
|
|
* The complexity of this function is O(suffix.length).
|
|
*
|
|
* ```ts
|
|
* import { endsWith } from "./mod.ts";
|
|
* const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]);
|
|
* const suffix = new Uint8Array([1, 2, 3]);
|
|
* console.log(endsWith(source, suffix)); // true
|
|
* ```
|
|
*/
|
|
export function endsWith(source, suffix) {
|
|
for (let srci = source.length - 1, sfxi = suffix.length - 1; sfxi >= 0; srci--, sfxi--) {
|
|
if (source[srci] !== suffix[sfxi])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
/** Returns a new Uint8Array composed of `count` repetitions of the `source`
|
|
* array.
|
|
*
|
|
* If `count` is negative, a `RangeError` is thrown.
|
|
*
|
|
* ```ts
|
|
* import { repeat } from "./mod.ts";
|
|
* const source = new Uint8Array([0, 1, 2]);
|
|
* console.log(repeat(source, 3)); // [0, 1, 2, 0, 1, 2, 0, 1, 2]
|
|
* console.log(repeat(source, 0)); // []
|
|
* console.log(repeat(source, -1)); // RangeError
|
|
* ```
|
|
*/
|
|
export function repeat(source, count) {
|
|
if (count === 0) {
|
|
return new Uint8Array();
|
|
}
|
|
if (count < 0) {
|
|
throw new RangeError("bytes: negative repeat count");
|
|
}
|
|
else if ((source.length * count) / count !== source.length) {
|
|
throw new Error("bytes: repeat count causes overflow");
|
|
}
|
|
const int = Math.floor(count);
|
|
if (int !== count) {
|
|
throw new Error("bytes: repeat count must be an integer");
|
|
}
|
|
const nb = new Uint8Array(source.length * count);
|
|
let bp = copy(source, nb);
|
|
for (; bp < nb.length; bp *= 2) {
|
|
copy(nb.slice(0, bp), nb, bp);
|
|
}
|
|
return nb;
|
|
}
|
|
/** Concatenate the given arrays into a new Uint8Array.
|
|
*
|
|
* ```ts
|
|
* import { concat } from "./mod.ts";
|
|
* const a = new Uint8Array([0, 1, 2]);
|
|
* const b = new Uint8Array([3, 4, 5]);
|
|
* console.log(concat(a, b)); // [0, 1, 2, 3, 4, 5]
|
|
*/
|
|
export function concat(...buf) {
|
|
let length = 0;
|
|
for (const b of buf) {
|
|
length += b.length;
|
|
}
|
|
const output = new Uint8Array(length);
|
|
let index = 0;
|
|
for (const b of buf) {
|
|
output.set(b, index);
|
|
index += b.length;
|
|
}
|
|
return output;
|
|
}
|
|
/** Returns true if the source array contains the needle array, false otherwise.
|
|
*
|
|
* A start index can be specified as the third argument that begins the search
|
|
* at that given index. The start index defaults to the beginning of the array.
|
|
*
|
|
* The complexity of this function is O(source.length * needle.length).
|
|
*
|
|
* ```ts
|
|
* import { includesNeedle } from "./mod.ts";
|
|
* const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]);
|
|
* const needle = new Uint8Array([1, 2]);
|
|
* console.log(includesNeedle(source, needle)); // true
|
|
* console.log(includesNeedle(source, needle, 6)); // false
|
|
* ```
|
|
*/
|
|
export function includesNeedle(source, needle, start = 0) {
|
|
return indexOfNeedle(source, needle, start) !== -1;
|
|
}
|
|
/** Copy bytes from the `src` array to the `dst` array. Returns the number of
|
|
* bytes copied.
|
|
*
|
|
* If the `src` array is larger than what the `dst` array can hold, only the
|
|
* amount of bytes that fit in the `dst` array are copied.
|
|
*
|
|
* An offset can be specified as the third argument that begins the copy at
|
|
* that given index in the `dst` array. The offset defaults to the beginning of
|
|
* the array.
|
|
*
|
|
* ```ts
|
|
* import { copy } from "./mod.ts";
|
|
* const src = new Uint8Array([9, 8, 7]);
|
|
* const dst = new Uint8Array([0, 1, 2, 3, 4, 5]);
|
|
* console.log(copy(src, dst)); // 3
|
|
* console.log(dst); // [9, 8, 7, 3, 4, 5]
|
|
* ```
|
|
*
|
|
* ```ts
|
|
* import { copy } from "./mod.ts";
|
|
* const src = new Uint8Array([1, 1, 1, 1]);
|
|
* const dst = new Uint8Array([0, 0, 0, 0]);
|
|
* console.log(copy(src, dst, 1)); // 3
|
|
* console.log(dst); // [0, 1, 1, 1]
|
|
* ```
|
|
*/
|
|
export function copy(src, dst, off = 0) {
|
|
off = Math.max(0, Math.min(off, dst.byteLength));
|
|
const dstBytesAvailable = dst.byteLength - off;
|
|
if (src.byteLength > dstBytesAvailable) {
|
|
src = src.subarray(0, dstBytesAvailable);
|
|
}
|
|
dst.set(src, off);
|
|
return src.byteLength;
|
|
}
|
|
export { equals } from "./equals.js";
|