forked from storacha/w3up
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: use an ArrayBuffer for delegation bits in AgentData (storacha#1219)
we noticed that Safari users couldn't log in: storacha/console#46 I tracked this down to a seemingly unreported WebKit bug where IndexedDB throws an error when saving `Uint8Array`s in an object that uses inline-keys. I know these two things don't seem related, but there does seem to be some similar precedent: dexie/Dexie.js#656 (comment) This change tweaks `AgentData`'s export format to use `ArrayBuffer`s rather than `Uint8Array`s, which should solve the issue. I was able to handle coercing back into Uint8Arrays pretty elegantly, and introduced a new `AgentDataImport` type to keep things typesafe.
- Loading branch information
Showing
4 changed files
with
86 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* Convert a Uint8Array to an ArrayBuffer, taking into account | ||
* that we may be looking at a "data view". | ||
* thanks, https://stackoverflow.com/a/54646864 | ||
* | ||
* If we aren't looking at a data view, simply returns the underlying ArrayBuffer | ||
* directly. | ||
* | ||
* @param {Uint8Array} array | ||
* @returns ArrayBuffer | ||
*/ | ||
export function uint8ArrayToArrayBuffer(array) { | ||
if (array.byteOffset === 0 && array.byteLength === array.buffer.byteLength) { | ||
return array.buffer | ||
} else { | ||
return array.buffer.slice( | ||
array.byteOffset, | ||
array.byteLength + array.byteOffset | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import assert from 'assert' | ||
import { uint8ArrayToArrayBuffer } from '../../src/utils/buffers.js' | ||
|
||
/** | ||
* | ||
* @param {any[]} array | ||
* @returns ArrayBuffer | ||
*/ | ||
function arrayBufferFromArray(array) { | ||
return Uint8Array.from(array).buffer | ||
} | ||
|
||
describe('uint8ArrayToArrayBuffer', function () { | ||
it('should convert an empty Uint8Array to an empty ArrayBuffer', function () { | ||
assert.deepEqual( | ||
uint8ArrayToArrayBuffer(Uint8Array.from([])), | ||
new ArrayBuffer(0) | ||
) | ||
}) | ||
|
||
it('should convert a Uint8Array with a few elements to an ArrayBuffer with the same elements', function () { | ||
assert.deepEqual( | ||
uint8ArrayToArrayBuffer(Uint8Array.from([1, 2, 3])), | ||
arrayBufferFromArray([1, 2, 3]) | ||
) | ||
}) | ||
|
||
it('should return the ArrayBuffer instance underlying the Uint8Array if the Uint8Array represents the whole array', function () { | ||
const uint8Array = Uint8Array.from([1, 2, 3]) | ||
assert.strictEqual(uint8ArrayToArrayBuffer(uint8Array), uint8Array.buffer) | ||
}) | ||
|
||
it('should convert a Uint8Array that is a view over an ArrayBuffer to an ArrayBuffer with the same elements', function () { | ||
const originalArrayBuffer = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 8]).buffer | ||
const uint8Array = new Uint8Array(originalArrayBuffer, 3, 3) | ||
// uint8Array is a view on the original buffer | ||
assert.deepEqual(uint8Array, Uint8Array.from([4, 5, 6])) | ||
// if we just get the uint8Array.buffer it is equal to the whole original buffer, not the uint8array's view | ||
assert.deepEqual( | ||
uint8Array.buffer, | ||
arrayBufferFromArray([1, 2, 3, 4, 5, 6, 7, 8]) | ||
) | ||
// but if we use uint8ArrayToArraybuffer it just returns an ArrayBuffer of the Uint8Array's view | ||
assert.deepEqual( | ||
uint8ArrayToArrayBuffer(uint8Array), | ||
arrayBufferFromArray([4, 5, 6]) | ||
) | ||
}) | ||
}) | ||
|
||
// this test simply verifies that a Uint8Array doesn't make a copy of an ArrayBuffer | ||
// passed to its constructor - technically this is verifying the implementation | ||
// of the JavaScript platform behaves as advertised, but it's an important part | ||
// of our performance assumptions about AgentData so worth proving here | ||
describe('new Uint8Array()', function () { | ||
it('should use a passed ArrayBuffer as its underlying buffer', function () { | ||
const arrayBuffer = arrayBufferFromArray([1, 2, 3]) | ||
assert.strictEqual(new Uint8Array(arrayBuffer).buffer, arrayBuffer) | ||
}) | ||
}) |