Skip to content

Commit

Permalink
feat: support decoding ArrayBuffers (#287)
Browse files Browse the repository at this point in the history
When using `fetch` to download something over HTTP, it only makes
an `ArrayBuffer` available, not a `Uint8Array`.

`TextDecoder` supports decoding `ArrayBuffer`s, and the `raw` codec
turns passed `ArrayBuffer`s into `Uint8Array`s so this is just a
type change.

Instead of:

```js
const res = await fetch('...')
const obj = json.decode(new Uint8Array(await res.arrayBuffer()))
```

we can do:

```js
const res = await fetch('...')
const obj = json.decode(await res.arrayBuffer())
```
  • Loading branch information
achingbrain committed Feb 15, 2024
1 parent aa9c730 commit e7f3272
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 7 deletions.
5 changes: 5 additions & 0 deletions src/block/interface.ts
Expand Up @@ -12,6 +12,11 @@ import type { Link, Version } from '../link/interface.js'
*/
export interface ByteView<Data> extends Uint8Array, Phantom<Data> {}

/**
* Similar to ByteView but extends ArrayBuffer.
*/
export interface ArrayBufferView<Data> extends ArrayBuffer, Phantom<Data> {}

declare const Marker: unique symbol

/**
Expand Down
6 changes: 3 additions & 3 deletions src/codecs/interface.ts
@@ -1,4 +1,4 @@
import type { ByteView } from '../block/interface.js'
import type { ArrayBufferView, ByteView } from '../block/interface.js'

/**
* IPLD encoder part of the codec.
Expand All @@ -14,12 +14,12 @@ export interface BlockEncoder<Code extends number, T> {
*/
export interface BlockDecoder<Code extends number, T> {
code: Code
decode(bytes: ByteView<T>): T
decode(bytes: ByteView<T> | ArrayBufferView<T>): T
}

/**
* An IPLD codec is a combination of both encoder and decoder.
*/
export interface BlockCodec<Code extends number, T> extends BlockEncoder<Code, T>, BlockDecoder<Code, T> {}

export type { ByteView }
export type { ArrayBufferView, ByteView }
4 changes: 2 additions & 2 deletions src/codecs/json.ts
@@ -1,4 +1,4 @@
import type { ByteView } from './interface.js'
import type { ArrayBufferView, ByteView } from './interface.js'

const textEncoder = new TextEncoder()
const textDecoder = new TextDecoder()
Expand All @@ -10,6 +10,6 @@ export function encode <T> (node: T): ByteView<T> {
return textEncoder.encode(JSON.stringify(node))
}

export function decode <T> (data: ByteView<T>): T {
export function decode <T> (data: ByteView<T> | ArrayBufferView<T>): T {
return JSON.parse(textDecoder.decode(data))
}
4 changes: 2 additions & 2 deletions src/codecs/raw.ts
@@ -1,5 +1,5 @@
import { coerce } from '../bytes.js'
import type { ByteView } from './interface.js'
import type { ArrayBufferView, ByteView } from './interface.js'

export const name = 'raw'
export const code = 0x55
Expand All @@ -8,6 +8,6 @@ export function encode (node: Uint8Array): ByteView<Uint8Array> {
return coerce(node)
}

export function decode (data: ByteView<Uint8Array>): Uint8Array {
export function decode (data: ByteView<Uint8Array> | ArrayBufferView<Uint8Array>): Uint8Array {
return coerce(data)
}
12 changes: 12 additions & 0 deletions test/test-multicodec.spec.ts
Expand Up @@ -12,12 +12,24 @@ describe('multicodec', () => {
assert.deepStrictEqual(raw.decode(buff), bytes.fromString('test'))
})

it('encode/decode raw arraybuffer', () => {
const buff = raw.encode(bytes.fromString('test'))
assert.deepStrictEqual(buff, bytes.fromString('test'))
assert.deepStrictEqual(raw.decode(buff.buffer), bytes.fromString('test'))
})

it('encode/decode json', () => {
const buff = json.encode({ hello: 'world' })
assert.deepStrictEqual(buff, bytes.fromString(JSON.stringify({ hello: 'world' })))
assert.deepStrictEqual(json.decode(buff), { hello: 'world' })
})

it('encode/decode json arraybuffer', () => {
const buff = json.encode({ hello: 'world' })
assert.deepStrictEqual(buff, bytes.fromString(JSON.stringify({ hello: 'world' })))
assert.deepStrictEqual(json.decode(buff.buffer), { hello: 'world' })
})

it('raw cannot encode string', async () => {
// @ts-expect-error - 'string' is not assignable to parameter of type 'Uint8Array'
assert.throws(() => raw.encode('asdf'), 'Unknown type, must be binary type')
Expand Down

0 comments on commit e7f3272

Please sign in to comment.