Skip to content

Commit

Permalink
fix: Use subpath imports to choose Node or vanilla JS environment (#88)
Browse files Browse the repository at this point in the history
Import buffer from `node:buffer` instead of using `globalThis.Buffer` to prevent accidentally using janky polyfills and allow bundlers to cleanly supply valid versions.

Fixes: #87

---------

Co-authored-by: achingbrain <alex@achingbrain.net>
  • Loading branch information
ukstv and achingbrain committed Mar 13, 2024
1 parent dc81e75 commit e43f1f4
Show file tree
Hide file tree
Showing 27 changed files with 192 additions and 59 deletions.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
# About

<!--
!IMPORTANT!
Everything in this README between "# About" and "# Install" is automatically
generated and will be overwritten the next time the doc generator is run.
To make changes to this section, please update the @packageDocumentation section
of src/index.js or src/index.ts
To experiment with formatting, please run "npm run docs" from the root of this
repo and examine the changes made.
-->

`Uint8Array`s bring memory-efficient(ish) byte handling to browsers - they are similar to Node.js `Buffer`s but lack a lot of the utility methods present on that class.

This module exports a number of function that let you do common operations - joining Uint8Arrays together, seeing if they have the same contents etc.
Expand All @@ -13,7 +28,7 @@ Since Node.js `Buffer`s are also `Uint8Array`s, it falls back to `Buffer` intern

## alloc(size)

Create a new `Uint8Array`. If `globalThis.Buffer` is defined, it will be used in preference to `globalThis.Uint8Array`.
Create a new `Uint8Array`. When running under Node.js, `Buffer` will be used in preference to `Uint8Array`.

### Example

Expand All @@ -25,7 +40,7 @@ const buf = alloc(100)

## allocUnsafe(size)

Create a new `Uint8Array`. If `globalThis.Buffer` is defined, it will be used in preference to `globalThis.Uint8Array`.
Create a new `Uint8Array`. When running under Node.js, `Buffer` will be used in preference to `Uint8Array`.

On platforms that support it, memory referenced by the returned `Uint8Array` will not be initialized.

Expand Down
2 changes: 1 addition & 1 deletion benchmarks/alloc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ $ npx playwright-test benchmarks/alloc.js --runner benchmark
*/

import Benchmark from 'benchmark'
import { alloc, allocUnsafe } from '../src/dist/alloc.js'
import { alloc, allocUnsafe } from '#alloc'

const LENGTH = 1024

Expand Down
6 changes: 3 additions & 3 deletions benchmarks/concat.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ $ npx playwright-test benchmarks/concat.js --runner benchmark
*/

import Benchmark from 'benchmark'
import { allocUnsafe } from '../dist/src/alloc.js'
import { concat } from '../dist/src/concat.js'
import { fromString } from '../dist/src/from-string.js'
import { allocUnsafe } from '#alloc'
import { concat } from '#concat'
import { fromString } from '#from-string'

const string = 'Hello world, this is a Uint8Array created from a string'
const DATA1 = fromString(string)
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/from-string.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ $ npx playwright-test benchmarks/from-string.js --runner benchmark
*/

import Benchmark from 'benchmark'
import { fromString } from '../dist/src/from-string.js'
import { fromString } from '#from-string'

const string = 'Hello world, this is a Uint8Array created from a string'
const DATA = fromString(string)
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/to-string.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ $ npx playwright-test benchmarks/to-string.js --runner benchmark
*/

import Benchmark from 'benchmark'
import { fromString } from '../dist/src/from-string.js'
import { toString } from '../dist/src/to-string.js'
import { fromString } from '#from-string'
import { toString } from '#to-string'

const string = 'Hello world, this is a Uint8Array created from a string'
const DATA = fromString(string)
Expand Down
45 changes: 45 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,17 @@
},
"./alloc": {
"types": "./dist/src/alloc.d.ts",
"node": "./dist/src/alloc.node.js",
"import": "./dist/src/alloc.js"
},
"./compare": {
"types": "./dist/src/compare.d.ts",
"node": "./dist/src/compare.node.js",
"import": "./dist/src/compare.js"
},
"./concat": {
"types": "./dist/src/concat.d.ts",
"node": "./dist/src/concat.node.js",
"import": "./dist/src/concat.js"
},
"./equals": {
Expand All @@ -63,17 +66,51 @@
},
"./from-string": {
"types": "./dist/src/from-string.d.ts",
"node": "./dist/src/from-string.node.js",
"import": "./dist/src/from-string.js"
},
"./to-string": {
"types": "./dist/src/to-string.d.ts",
"node": "./dist/src/to-string.node.js",
"import": "./dist/src/to-string.js"
},
"./xor": {
"types": "./dist/src/xor.d.ts",
"import": "./dist/src/xor.js"
}
},
"imports": {
"#util/as-uint8array": {
"types": "./dist/src/util/as-uint8array.d.ts",
"node": "./dist/src/util/as-uint8array.node.js",
"import": "./dist/src/util/as-uint8array.js"
},
"#alloc": {
"types": "./dist/src/alloc.d.ts",
"node": "./dist/src/alloc.node.js",
"import": "./dist/src/alloc.js"
},
"#compare": {
"types": "./dist/src/compare.d.ts",
"node": "./dist/src/compare.node.js",
"import": "./dist/src/compare.js"
},
"#concat": {
"types": "./dist/src/concat.d.ts",
"node": "./dist/src/concat.node.js",
"import": "./dist/src/concat.js"
},
"#from-string": {
"types": "./dist/src/from-string.d.ts",
"node": "./dist/src/from-string.node.js",
"import": "./dist/src/from-string.js"
},
"#to-string": {
"types": "./dist/src/to-string.d.ts",
"node": "./dist/src/to-string.node.js",
"import": "./dist/src/to-string.js"
}
},
"eslintConfig": {
"extends": "ipfs",
"parserOptions": {
Expand Down Expand Up @@ -187,5 +224,13 @@
"@types/benchmark": "^2.1.1",
"aegir": "^42.2.3",
"benchmark": "^2.1.4"
},
"react-native": {
"#util/as-uint8array": "./dist/src/util/as-uint8array.js",
"#alloc": "./dist/src/alloc.js",
"#compare": "./dist/src/compare.js",
"#concat": "./dist/src/concat.js",
"#from-string": "./dist/src/from-string.js",
"#to-string": "./dist/src/to-string.js"
}
}
19 changes: 19 additions & 0 deletions src/alloc.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Buffer } from 'node:buffer'
import { asUint8Array } from '#util/as-uint8array'

/**
* Returns a `Uint8Array` of the requested size. Referenced memory will
* be initialized to 0.
*/
export function alloc (size: number = 0): Uint8Array {
return asUint8Array(Buffer.alloc(size))
}

/**
* Where possible returns a Uint8Array of the requested size that references
* uninitialized memory. Only use if you are certain you will immediately
* overwrite every value in the returned `Uint8Array`.
*/
export function allocUnsafe (size: number = 0): Uint8Array {
return asUint8Array(Buffer.allocUnsafe(size))
}
10 changes: 0 additions & 10 deletions src/alloc.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { asUint8Array } from './util/as-uint8array.js'

/**
* Returns a `Uint8Array` of the requested size. Referenced memory will
* be initialized to 0.
*/
export function alloc (size: number = 0): Uint8Array {
if (globalThis.Buffer?.alloc != null) {
return asUint8Array(globalThis.Buffer.alloc(size))
}

return new Uint8Array(size)
}

Expand All @@ -18,9 +12,5 @@ export function alloc (size: number = 0): Uint8Array {
* overwrite every value in the returned `Uint8Array`.
*/
export function allocUnsafe (size: number = 0): Uint8Array {
if (globalThis.Buffer?.allocUnsafe != null) {
return asUint8Array(globalThis.Buffer.allocUnsafe(size))
}

return new Uint8Array(size)
}
8 changes: 8 additions & 0 deletions src/compare.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Buffer } from 'node:buffer'

/**
* Can be used with Array.sort to sort and array with Uint8Array entries
*/
export function compare (a: Uint8Array, b: Uint8Array): number {
return Buffer.compare(a, b)
}
4 changes: 0 additions & 4 deletions src/compare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
* Can be used with Array.sort to sort and array with Uint8Array entries
*/
export function compare (a: Uint8Array, b: Uint8Array): number {
if (globalThis.Buffer != null) {
return globalThis.Buffer.compare(a, b)
}

for (let i = 0; i < a.byteLength; i++) {
if (a[i] < b[i]) {
return -1
Expand Down
9 changes: 9 additions & 0 deletions src/concat.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Buffer } from 'node:buffer'
import { asUint8Array } from '#util/as-uint8array'

/**
* Returns a new Uint8Array created by concatenating the passed Uint8Arrays
*/
export function concat (arrays: Uint8Array[], length?: number): Uint8Array {
return asUint8Array(Buffer.concat(arrays, length))
}
8 changes: 2 additions & 6 deletions src/concat.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { allocUnsafe } from './alloc.js'
import { asUint8Array } from './util/as-uint8array.js'
import { allocUnsafe } from '#alloc'
import { asUint8Array } from '#util/as-uint8array'

/**
* Returns a new Uint8Array created by concatenating the passed Uint8Arrays
*/
export function concat (arrays: Uint8Array[], length?: number): Uint8Array {
if (globalThis.Buffer != null) {
return asUint8Array(globalThis.Buffer.concat(arrays, length))
}

if (length == null) {
length = arrays.reduce((acc, curr) => acc + curr.length, 0)
}
Expand Down
27 changes: 27 additions & 0 deletions src/from-string.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Buffer } from 'node:buffer'
import bases, { type SupportedEncodings } from './util/bases.js'
import { asUint8Array } from '#util/as-uint8array'

export type { SupportedEncodings }

/**
* Create a `Uint8Array` from the passed string
*
* Supports `utf8`, `utf-8`, `hex`, and any encoding supported by the multiformats module.
*
* Also `ascii` which is similar to node's 'binary' encoding.
*/
export function fromString (string: string, encoding: SupportedEncodings = 'utf8'): Uint8Array {
const base = bases[encoding]

if (base == null) {
throw new Error(`Unsupported encoding "${encoding}"`)
}

if (encoding === 'utf8' || encoding === 'utf-8') {
return asUint8Array(Buffer.from(string, 'utf-8'))
}

// add multibase prefix
return base.decoder.decode(`${base.prefix}${string}`) // eslint-disable-line @typescript-eslint/restrict-template-expressions
}
5 changes: 0 additions & 5 deletions src/from-string.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { asUint8Array } from './util/as-uint8array.js'
import bases, { type SupportedEncodings } from './util/bases.js'

export type { SupportedEncodings }
Expand All @@ -17,10 +16,6 @@ export function fromString (string: string, encoding: SupportedEncodings = 'utf8
throw new Error(`Unsupported encoding "${encoding}"`)
}

if ((encoding === 'utf8' || encoding === 'utf-8') && globalThis.Buffer != null && globalThis.Buffer.from != null) {
return asUint8Array(globalThis.Buffer.from(string, 'utf-8'))
}

// add multibase prefix
return base.decoder.decode(`${base.prefix}${string}`) // eslint-disable-line @typescript-eslint/restrict-template-expressions
}
12 changes: 6 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*
* ## alloc(size)
*
* Create a new `Uint8Array`. If `globalThis.Buffer` is defined, it will be used in preference to `globalThis.Uint8Array`.
* Create a new `Uint8Array`. When running under Node.js, `Buffer` will be used in preference to `Uint8Array`.
*
* ### Example
*
Expand All @@ -21,7 +21,7 @@
*
* ## allocUnsafe(size)
*
* Create a new `Uint8Array`. If `globalThis.Buffer` is defined, it will be used in preference to `globalThis.Uint8Array`.
* Create a new `Uint8Array`. When running under Node.js, `Buffer` will be used in preference to `Uint8Array`.
*
* On platforms that support it, memory referenced by the returned `Uint8Array` will not be initialized.
*
Expand Down Expand Up @@ -143,12 +143,12 @@
* ```
*/

import { compare } from './compare.js'
import { concat } from './concat.js'
import { equals } from './equals.js'
import { fromString } from './from-string.js'
import { toString } from './to-string.js'
import { xor } from './xor.js'
import { compare } from '#compare'
import { concat } from '#concat'
import { fromString } from '#from-string'
import { toString } from '#to-string'

export {
compare,
Expand Down
26 changes: 26 additions & 0 deletions src/to-string.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Buffer } from 'node:buffer'
import bases, { type SupportedEncodings } from './util/bases.js'

export type { SupportedEncodings }

/**
* Turns a `Uint8Array` into a string.
*
* Supports `utf8`, `utf-8` and any encoding supported by the multibase module.
*
* Also `ascii` which is similar to node's 'binary' encoding.
*/
export function toString (array: Uint8Array, encoding: SupportedEncodings = 'utf8'): string {
const base = bases[encoding]

if (base == null) {
throw new Error(`Unsupported encoding "${encoding}"`)
}

if (encoding === 'utf8' || encoding === 'utf-8') {
return Buffer.from(array.buffer, array.byteOffset, array.byteLength).toString('utf8')
}

// strip multibase prefix
return base.encoder.encode(array).substring(1)
}
4 changes: 0 additions & 4 deletions src/to-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ export function toString (array: Uint8Array, encoding: SupportedEncodings = 'utf
throw new Error(`Unsupported encoding "${encoding}"`)
}

if ((encoding === 'utf8' || encoding === 'utf-8') && globalThis.Buffer != null && globalThis.Buffer.from != null) {
return globalThis.Buffer.from(array.buffer, array.byteOffset, array.byteLength).toString('utf8')
}

// strip multibase prefix
return base.encoder.encode(array).substring(1)
}
7 changes: 7 additions & 0 deletions src/util/as-uint8array.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* To guarantee Uint8Array semantics, convert nodejs Buffers
* into vanilla Uint8Arrays
*/
export function asUint8Array (buf: Uint8Array): Uint8Array {
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)
}
4 changes: 0 additions & 4 deletions src/util/as-uint8array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,5 @@
* into vanilla Uint8Arrays
*/
export function asUint8Array (buf: Uint8Array): Uint8Array {
if (globalThis.Buffer != null) {
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength)
}

return buf
}
Loading

0 comments on commit e43f1f4

Please sign in to comment.