refactor(bytes): move to single-export files (#2955)

This commit is contained in:
Asher Gomez 2022-11-29 17:01:21 +11:00 committed by GitHub
parent 8e46f5953e
commit e1117a8c0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 596 additions and 587 deletions

26
bytes/concat.ts Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/** Concatenate the given arrays into a new Uint8Array.
*
* ```ts
* import { concat } from "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array[]): Uint8Array {
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;
}

36
bytes/concat_test.ts Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "../testing/asserts.ts";
import { concat } from "./concat.ts";
Deno.test("[bytes] concat", () => {
const encoder = new TextEncoder();
const u1 = encoder.encode("Hello ");
const u2 = encoder.encode("World");
const joined = concat(u1, u2);
assertEquals(new TextDecoder().decode(joined), "Hello World");
assert(u1 !== joined);
assert(u2 !== joined);
});
Deno.test("[bytes] concat empty arrays", () => {
const u1 = new Uint8Array();
const u2 = new Uint8Array();
const joined = concat(u1, u2);
assertEquals(joined.byteLength, 0);
assert(u1 !== joined);
assert(u2 !== joined);
});
Deno.test("[bytes] concat multiple arrays", () => {
const encoder = new TextEncoder();
const u1 = encoder.encode("Hello ");
const u2 = encoder.encode("W");
const u3 = encoder.encode("o");
const u4 = encoder.encode("r");
const u5 = encoder.encode("l");
const u6 = encoder.encode("d");
const joined = concat(u1, u2, u3, u4, u5, u6);
assertEquals(new TextDecoder().decode(joined), "Hello World");
assert(u1 !== joined);
assert(u2 !== joined);
});

38
bytes/copy.ts Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/** 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 "https://deno.land/std@$STD_VERSION/bytes/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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array, dst: Uint8Array, off = 0): number {
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;
}

37
bytes/copy_test.ts Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "../testing/asserts.ts";
import { copy } from "./copy.ts";
Deno.test("[bytes] copy", function () {
const dst = new Uint8Array(4);
dst.fill(0);
let src = Uint8Array.of(1, 2);
let len = copy(src, dst, 0);
assert(len === 2);
assertEquals(dst, Uint8Array.of(1, 2, 0, 0));
dst.fill(0);
src = Uint8Array.of(1, 2);
len = copy(src, dst, 1);
assert(len === 2);
assertEquals(dst, Uint8Array.of(0, 1, 2, 0));
dst.fill(0);
src = Uint8Array.of(1, 2, 3, 4, 5);
len = copy(src, dst);
assert(len === 4);
assertEquals(dst, Uint8Array.of(1, 2, 3, 4));
dst.fill(0);
src = Uint8Array.of(1, 2);
len = copy(src, dst, 100);
assert(len === 0);
assertEquals(dst, Uint8Array.of(0, 0, 0, 0));
dst.fill(0);
src = Uint8Array.of(3, 4);
len = copy(src, dst, -2);
assert(len === 2);
assertEquals(dst, Uint8Array.of(3, 4, 0, 0));
});

25
bytes/ends_with.ts Normal file
View File

@ -0,0 +1,25 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/** 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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array, suffix: Uint8Array): boolean {
for (
let srci = source.length - 1, sfxi = suffix.length - 1;
sfxi >= 0;
srci--, sfxi--
) {
if (source[srci] !== suffix[sfxi]) return false;
}
return true;
}

13
bytes/ends_with_test.ts Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assert } from "../testing/asserts.ts";
import { endsWith } from "./ends_with.ts";
Deno.test("[bytes] endsWith", () => {
const v = endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([1, 2]));
const v2 = endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1]));
const v3 = endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1, 2, 3]));
assert(v);
assert(!v2);
assert(!v3);
});

View File

@ -6,7 +6,7 @@
* @param a first array to check equality
* @param b second array to check equality
*/
export function equalsNaive(a: Uint8Array, b: Uint8Array): boolean {
function equalsNaive(a: Uint8Array, b: Uint8Array): boolean {
if (a.length !== b.length) return false;
for (let i = 0; i < b.length; i++) {
if (a[i] !== b[i]) return false;
@ -19,7 +19,7 @@ export function equalsNaive(a: Uint8Array, b: Uint8Array): boolean {
* @param a first array to check equality
* @param b second array to check equality
*/
export function equals32Bit(a: Uint8Array, b: Uint8Array): boolean {
function equals32Bit(a: Uint8Array, b: Uint8Array): boolean {
if (a.length !== b.length) return false;
const len = a.length;
const compressable = Math.floor(len / 4);

View File

@ -1,35 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { equals32Bit, equalsNaive } from "./equals.ts";
console.log("generating benchmarks...");
const testCases: [Uint8Array, Uint8Array][] = [];
// CHANGE THESE
const len = 10000;
const nCases = 10000;
for (let i = 0; i < nCases; i++) {
const arr1 = crypto.getRandomValues(new Uint8Array(len));
const arr2 = crypto.getRandomValues(new Uint8Array(len));
const arr3 = arr1.slice(0);
arr3[arr3.length - 1] = arr1[arr1.length - 1] ^ 1;
testCases.push([arr1, arr1.slice(0)]);
testCases.push([arr1, arr2]);
testCases.push([arr1, arr3]);
}
Deno.bench({
name: "bench old equals",
fn() {
for (const [a, b] of testCases) {
equalsNaive(a, b);
}
},
});
Deno.bench({
name: "bench simd equals",
fn() {
for (const [a, b] of testCases) {
equals32Bit(a, b);
}
},
});

34
bytes/equals_test.ts Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { equals } from "./equals.ts";
import { assert } from "../testing/asserts.ts";
Deno.test("[bytes] equals", () => {
const v = equals(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2, 3]));
const v2 = equals(new Uint8Array([0, 1, 2, 2]), new Uint8Array([0, 1, 2, 3]));
const v3 = equals(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2]));
assert(v);
assert(!v2);
assert(!v3);
});
Deno.test("[bytes] equals randomized testing", () => {
// run tests before and after cutoff
for (let len = 995; len <= 1005; len++) {
const arr1 = crypto.getRandomValues(new Uint8Array(len));
const arr2 = crypto.getRandomValues(new Uint8Array(len));
const arr3 = arr1.slice(0);
// the chance of arr1 equaling arr2 is basically 0
// but introduce an inequality at the end just in case
arr2[arr2.length - 1] = arr1[arr1.length - 1] ^ 1;
// arr3 is arr1 but with an inequality in the very last element
// this is to test the equality check when length isn't a multiple of 4
arr3[arr3.length - 1] ^= 1;
// arrays with same underlying ArrayBuffer should be equal
assert(equals(arr1, arr1));
// equal arrays with different underlying ArrayBuffers should be equal
assert(equals(arr1, arr1.slice(0)));
// inequal arrays should be inequal
assert(!equals(arr1, arr2));
assert(!equals(arr1, arr3));
}
});

27
bytes/includes_needle.ts Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { indexOfNeedle } from "./index_of_needle.ts";
/** 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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array,
needle: Uint8Array,
start = 0,
): boolean {
return indexOfNeedle(source, needle, start) !== -1;
}

View File

@ -0,0 +1,15 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { includesNeedle } from "./includes_needle.ts";
import { assert } from "../testing/asserts.ts";
Deno.test("[bytes] includesNeedle", () => {
const encoder = new TextEncoder();
const source = encoder.encode("deno.land");
const pattern = encoder.encode("deno");
assert(includesNeedle(source, pattern));
assert(includesNeedle(new Uint8Array([0, 1, 2, 3]), new Uint8Array([2, 3])));
assert(includesNeedle(source, pattern, -10));
assert(!includesNeedle(source, pattern, -1));
});

49
bytes/index_of_needle.ts Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/** 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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array,
needle: Uint8Array,
start = 0,
): number {
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;
}

View File

@ -0,0 +1,64 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { indexOfNeedle } from "./index_of_needle.ts";
import { assertEquals } from "../testing/asserts.ts";
Deno.test("[bytes] indexOfNeedle1", () => {
const i = indexOfNeedle(
new Uint8Array([1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 3]),
new Uint8Array([0, 1, 2]),
);
assertEquals(i, 2);
});
Deno.test("[bytes] indexOfNeedle2", () => {
const i = indexOfNeedle(new Uint8Array([0, 0, 1]), new Uint8Array([0, 1]));
assertEquals(i, 1);
});
Deno.test("[bytes] indexOfNeedle3", () => {
const encoder = new TextEncoder();
const i = indexOfNeedle(encoder.encode("Deno"), encoder.encode("D"));
assertEquals(i, 0);
});
Deno.test("[bytes] indexOfNeedle4", () => {
const i = indexOfNeedle(new Uint8Array(), new Uint8Array([0, 1]));
assertEquals(i, -1);
});
Deno.test("[bytes] indexOfNeedle with start index", () => {
const i = indexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
1,
);
assertEquals(i, 3);
});
Deno.test("[bytes] indexOfNeedle with start index 2", () => {
const i = indexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
7,
);
assertEquals(i, -1);
});
Deno.test("[bytes] indexOfNeedle with start index < 0", () => {
assertEquals(
indexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
3,
),
3,
);
assertEquals(
indexOfNeedle(
new Uint8Array([0, 1, 2, 1, 1, 2]),
new Uint8Array([0, 1]),
3,
),
-1,
);
});

View File

@ -0,0 +1,49 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/** 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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array,
needle: Uint8Array,
start = source.length - 1,
): number {
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;
}

View File

@ -0,0 +1,42 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assertEquals } from "../testing/asserts.ts";
import { lastIndexOfNeedle } from "./last_index_of_needle.ts";
Deno.test("[bytes] lastIndexOfNeedle1", () => {
const i = lastIndexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2, 0, 1, 3]),
new Uint8Array([0, 1, 2]),
);
assertEquals(i, 3);
});
Deno.test("[bytes] lastIndexOfNeedle2", () => {
const i = lastIndexOfNeedle(
new Uint8Array([0, 1, 1]),
new Uint8Array([0, 1]),
);
assertEquals(i, 0);
});
Deno.test("[bytes] lastIndexOfNeedle3", () => {
const i = lastIndexOfNeedle(new Uint8Array(), new Uint8Array([0, 1]));
assertEquals(i, -1);
});
Deno.test("[bytes] lastIndexOfNeedle with start index", () => {
const i = lastIndexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
2,
);
assertEquals(i, 0);
});
Deno.test("[bytes] lastIndexOfNeedle with start index 2", () => {
const i = lastIndexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
-1,
);
assertEquals(i, -1);
});

View File

@ -8,264 +8,13 @@
* @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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array,
needle: Uint8Array,
start = 0,
): number {
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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array,
needle: Uint8Array,
start = source.length - 1,
): number {
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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array, prefix: Uint8Array): boolean {
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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array, suffix: Uint8Array): boolean {
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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array, count: number): Uint8Array {
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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array[]): Uint8Array {
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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array,
needle: Uint8Array,
start = 0,
): boolean {
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 "https://deno.land/std@$STD_VERSION/bytes/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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array, dst: Uint8Array, off = 0): number {
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.ts";
export * from "./bytes_list.ts";
export * from "./concat.ts";
export * from "./copy.ts";
export * from "./ends_with.ts";
export * from "./equals.ts";
export * from "./includes_needle.ts";
export * from "./index_of_needle.ts";
export * from "./last_index_of_needle.ts";
export * from "./repeat.ts";
export * from "./starts_with.ts";

44
bytes/repeat.ts Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
import { copy } from "./copy.ts";
/** Returns a new Uint8Array composed of `count` repetitions of the `source`
* array.
*
* If `count` is negative, a `RangeError` is thrown.
*
* ```ts
* import { repeat } from "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array, count: number): Uint8Array {
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;
}

36
bytes/repeat_test.ts Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertThrows } from "../testing/asserts.ts";
import { repeat } from "./repeat.ts";
Deno.test("[bytes] repeat", () => {
// input / output / count / error message
const repeatTestCase = [
["", "", 0],
["", "", 1],
["", "", 1.1, "bytes: repeat count must be an integer"],
["", "", 2],
["", "", 0],
["-", "", 0],
["-", "-", -1, "bytes: negative repeat count"],
["-", "----------", 10],
["abc ", "abc abc abc ", 3],
];
for (const [input, output, count, errMsg] of repeatTestCase) {
if (errMsg) {
assertThrows(
() => {
repeat(new TextEncoder().encode(input as string), count as number);
},
Error,
errMsg as string,
);
} else {
const newBytes = repeat(
new TextEncoder().encode(input as string),
count as number,
);
assertEquals(new TextDecoder().decode(newBytes), output);
}
}
});

21
bytes/starts_with.ts Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/** 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 "https://deno.land/std@$STD_VERSION/bytes/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: Uint8Array, prefix: Uint8Array): boolean {
for (let i = 0, max = prefix.length; i < max; i++) {
if (source[i] !== prefix[i]) return false;
}
return true;
}

15
bytes/starts_with_test.ts Normal file
View File

@ -0,0 +1,15 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assert } from "../testing/asserts.ts";
import { startsWith } from "./starts_with.ts";
Deno.test("[bytes] startsWith", () => {
const v = startsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1]));
const v2 = startsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 2]));
const v3 = startsWith(
new Uint8Array([0, 1, 2]),
new Uint8Array([0, 2, 3, 4]),
);
assert(v);
assert(!v2);
assert(!v3);
});

View File

@ -1,278 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import {
concat,
copy,
endsWith,
equals,
includesNeedle,
indexOfNeedle,
lastIndexOfNeedle,
repeat,
startsWith,
} from "./mod.ts";
import { assert, assertEquals, assertThrows } from "../testing/asserts.ts";
Deno.test("[bytes] indexOfNeedle1", () => {
const i = indexOfNeedle(
new Uint8Array([1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 3]),
new Uint8Array([0, 1, 2]),
);
assertEquals(i, 2);
});
Deno.test("[bytes] indexOfNeedle2", () => {
const i = indexOfNeedle(new Uint8Array([0, 0, 1]), new Uint8Array([0, 1]));
assertEquals(i, 1);
});
Deno.test("[bytes] indexOfNeedle3", () => {
const encoder = new TextEncoder();
const i = indexOfNeedle(encoder.encode("Deno"), encoder.encode("D"));
assertEquals(i, 0);
});
Deno.test("[bytes] indexOfNeedle4", () => {
const i = indexOfNeedle(new Uint8Array(), new Uint8Array([0, 1]));
assertEquals(i, -1);
});
Deno.test("[bytes] indexOfNeedle with start index", () => {
const i = indexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
1,
);
assertEquals(i, 3);
});
Deno.test("[bytes] indexOfNeedle with start index 2", () => {
const i = indexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
7,
);
assertEquals(i, -1);
});
Deno.test("[bytes] indexOfNeedle with start index < 0", () => {
assertEquals(
indexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
3,
),
3,
);
assertEquals(
indexOfNeedle(
new Uint8Array([0, 1, 2, 1, 1, 2]),
new Uint8Array([0, 1]),
3,
),
-1,
);
});
Deno.test("[bytes] lastIndexOfNeedle1", () => {
const i = lastIndexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2, 0, 1, 3]),
new Uint8Array([0, 1, 2]),
);
assertEquals(i, 3);
});
Deno.test("[bytes] lastIndexOfNeedle2", () => {
const i = lastIndexOfNeedle(
new Uint8Array([0, 1, 1]),
new Uint8Array([0, 1]),
);
assertEquals(i, 0);
});
Deno.test("[bytes] lastIndexOfNeedle3", () => {
const i = lastIndexOfNeedle(new Uint8Array(), new Uint8Array([0, 1]));
assertEquals(i, -1);
});
Deno.test("[bytes] lastIndexOfNeedle with start index", () => {
const i = lastIndexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
2,
);
assertEquals(i, 0);
});
Deno.test("[bytes] lastIndexOfNeedle with start index 2", () => {
const i = lastIndexOfNeedle(
new Uint8Array([0, 1, 2, 0, 1, 2]),
new Uint8Array([0, 1]),
-1,
);
assertEquals(i, -1);
});
Deno.test("[bytes] equals", () => {
const v = equals(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2, 3]));
const v2 = equals(new Uint8Array([0, 1, 2, 2]), new Uint8Array([0, 1, 2, 3]));
const v3 = equals(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2]));
assert(v);
assert(!v2);
assert(!v3);
});
Deno.test("[bytes] equals randomized testing", () => {
// run tests before and after cutoff
for (let len = 995; len <= 1005; len++) {
const arr1 = crypto.getRandomValues(new Uint8Array(len));
const arr2 = crypto.getRandomValues(new Uint8Array(len));
const arr3 = arr1.slice(0);
// the chance of arr1 equaling arr2 is basically 0
// but introduce an inequality at the end just in case
arr2[arr2.length - 1] = arr1[arr1.length - 1] ^ 1;
// arr3 is arr1 but with an inequality in the very last element
// this is to test the equality check when length isn't a multiple of 4
arr3[arr3.length - 1] ^= 1;
// arrays with same underlying ArrayBuffer should be equal
assert(equals(arr1, arr1));
// equal arrays with different underlying ArrayBuffers should be equal
assert(equals(arr1, arr1.slice(0)));
// inequal arrays should be inequal
assert(!equals(arr1, arr2));
assert(!equals(arr1, arr3));
}
});
Deno.test("[bytes] startsWith", () => {
const v = startsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1]));
const v2 = startsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 2]));
const v3 = startsWith(
new Uint8Array([0, 1, 2]),
new Uint8Array([0, 2, 3, 4]),
);
assert(v);
assert(!v2);
assert(!v3);
});
Deno.test("[bytes] endsWith", () => {
const v = endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([1, 2]));
const v2 = endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1]));
const v3 = endsWith(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1, 2, 3]));
assert(v);
assert(!v2);
assert(!v3);
});
Deno.test("[bytes] repeat", () => {
// input / output / count / error message
const repeatTestCase = [
["", "", 0],
["", "", 1],
["", "", 1.1, "bytes: repeat count must be an integer"],
["", "", 2],
["", "", 0],
["-", "", 0],
["-", "-", -1, "bytes: negative repeat count"],
["-", "----------", 10],
["abc ", "abc abc abc ", 3],
];
for (const [input, output, count, errMsg] of repeatTestCase) {
if (errMsg) {
assertThrows(
() => {
repeat(new TextEncoder().encode(input as string), count as number);
},
Error,
errMsg as string,
);
} else {
const newBytes = repeat(
new TextEncoder().encode(input as string),
count as number,
);
assertEquals(new TextDecoder().decode(newBytes), output);
}
}
});
Deno.test("[bytes] concat", () => {
const encoder = new TextEncoder();
const u1 = encoder.encode("Hello ");
const u2 = encoder.encode("World");
const joined = concat(u1, u2);
assertEquals(new TextDecoder().decode(joined), "Hello World");
assert(u1 !== joined);
assert(u2 !== joined);
});
Deno.test("[bytes] concat empty arrays", () => {
const u1 = new Uint8Array();
const u2 = new Uint8Array();
const joined = concat(u1, u2);
assertEquals(joined.byteLength, 0);
assert(u1 !== joined);
assert(u2 !== joined);
});
Deno.test("[bytes] concat multiple arrays", () => {
const encoder = new TextEncoder();
const u1 = encoder.encode("Hello ");
const u2 = encoder.encode("W");
const u3 = encoder.encode("o");
const u4 = encoder.encode("r");
const u5 = encoder.encode("l");
const u6 = encoder.encode("d");
const joined = concat(u1, u2, u3, u4, u5, u6);
assertEquals(new TextDecoder().decode(joined), "Hello World");
assert(u1 !== joined);
assert(u2 !== joined);
});
Deno.test("[bytes] includesNeedle", () => {
const encoder = new TextEncoder();
const source = encoder.encode("deno.land");
const pattern = encoder.encode("deno");
assert(includesNeedle(source, pattern));
assert(includesNeedle(new Uint8Array([0, 1, 2, 3]), new Uint8Array([2, 3])));
assert(includesNeedle(source, pattern, -10));
assert(!includesNeedle(source, pattern, -1));
});
Deno.test("[bytes] copy", function () {
const dst = new Uint8Array(4);
dst.fill(0);
let src = Uint8Array.of(1, 2);
let len = copy(src, dst, 0);
assert(len === 2);
assertEquals(dst, Uint8Array.of(1, 2, 0, 0));
dst.fill(0);
src = Uint8Array.of(1, 2);
len = copy(src, dst, 1);
assert(len === 2);
assertEquals(dst, Uint8Array.of(0, 1, 2, 0));
dst.fill(0);
src = Uint8Array.of(1, 2, 3, 4, 5);
len = copy(src, dst);
assert(len === 4);
assertEquals(dst, Uint8Array.of(1, 2, 3, 4));
dst.fill(0);
src = Uint8Array.of(1, 2);
len = copy(src, dst, 100);
assert(len === 0);
assertEquals(dst, Uint8Array.of(0, 0, 0, 0));
dst.fill(0);
src = Uint8Array.of(3, 4);
len = copy(src, dst, -2);
assert(len === 2);
assertEquals(dst, Uint8Array.of(3, 4, 0, 0));
});

View File

@ -1,7 +1,7 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "../testing/asserts.ts";
import { crypto as stdCrypto } from "./mod.ts";
import * as bytes from "../bytes/mod.ts";
import { repeat } from "../bytes/repeat.ts";
import { dirname, fromFileUrl } from "../path/mod.ts";
import { DigestAlgorithm, digestAlgorithms } from "./_wasm/mod.ts";
const moduleDir = dirname(fromFileUrl(import.meta.url));
@ -352,7 +352,7 @@ Deno.test("[crypto/digest] Memory use should remain reasonable even with many ca
// Simple periodic data, but the periods shouldn't line up with any block
// or chunk sizes.
const aboutAMeg = bytes.repeat(
const aboutAMeg = repeat(
new Uint8Array(1237).fill(0).map((_, i) => i % 251),
839,
);

View File

@ -1,7 +1,7 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { crypto, type DigestAlgorithm } from "./mod.ts";
import { toHashString } from "./util.ts";
import { repeat } from "../bytes/mod.ts";
import { repeat } from "../bytes/repeat.ts";
import { assertEquals } from "../testing/asserts.ts";
const encoder = new TextEncoder();

View File

@ -1,7 +1,8 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assert } from "../_util/asserts.ts";
import { BytesList } from "../bytes/bytes_list.ts";
import { concat, copy } from "../bytes/mod.ts";
import { concat } from "../bytes/concat.ts";
import { copy } from "../bytes/copy.ts";
import type { Reader, ReaderSync, Writer, WriterSync } from "./types.d.ts";
// MIN_READ is the minimum ArrayBuffer size passed to a read call by

View File

@ -3,7 +3,7 @@
// This code has been ported almost directly from Go's src/bytes/buffer_test.go
// Copyright 2009 The Go Authors. All rights reserved. BSD license.
// https://github.com/golang/go/blob/master/LICENSE
import { copy } from "../bytes/mod.ts";
import { copy } from "../bytes/copy.ts";
import {
assert,
assertEquals,

View File

@ -1,6 +1,6 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { copy as copyBytes } from "../bytes/mod.ts";
import { copy as copyBytes } from "../bytes/copy.ts";
import { assert } from "../_util/asserts.ts";
const DEFAULT_BUFFER_SIZE = 32 * 1024;

View File

@ -1,6 +1,6 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { copy } from "../bytes/mod.ts";
import { copy } from "../bytes/copy.ts";
import {
assert,
assertEquals,

View File

@ -1,6 +1,6 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { Encodings } from "./_node.ts";
import { indexOfNeedle } from "../../bytes/mod.ts";
import { indexOfNeedle } from "../../bytes/index_of_needle.ts";
export function numberToBytes(n: number): Uint8Array {
if (n === 0) return new Uint8Array([0]);

View File

@ -1,6 +1,6 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { assert } from "../_util/asserts.ts";
import { copy } from "../bytes/mod.ts";
import { copy } from "../bytes/copy.ts";
const MAX_SIZE = 2 ** 32 - 2;
const DEFAULT_CHUNK_SIZE = 16_640;

View File

@ -19,7 +19,8 @@ import {
writerFromStreamWriter,
} from "./conversion.ts";
import { Buffer } from "../io/buffer.ts";
import { concat, copy as copyBytes } from "../bytes/mod.ts";
import { concat } from "../bytes/concat.ts";
import { copy as copyBytes } from "../bytes/copy.ts";
function repeat(c: string, bytes: number): Uint8Array {
assertEquals(c.length, 1);

View File

@ -2,7 +2,7 @@
// This module is browser compatible.
import { bytesToUuid, uuidToBytes } from "./_common.ts";
import { concat } from "../bytes/mod.ts";
import { concat } from "../bytes/concat.ts";
import { assert } from "../_util/asserts.ts";
const UUID_RE =