From c771706353af9a93ee7a40b075c20118efd6b073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Sat, 8 Jul 2023 16:05:49 +0200 Subject: [PATCH] more serialization --- icons.config.ts | 6 ++ src/lib/serial/chord.spec.ts | 14 ---- src/lib/serial/chord.ts | 94 --------------------------- src/lib/serial/serialization.ts | 33 ++++++++++ src/routes/config/chords/+page.svelte | 29 ++++++++- 5 files changed, 67 insertions(+), 109 deletions(-) create mode 100644 src/lib/serial/serialization.ts diff --git a/icons.config.ts b/icons.config.ts index ba12b383..e262d8ca 100644 --- a/icons.config.ts +++ b/icons.config.ts @@ -30,6 +30,11 @@ const config: IconsConfig = { "123", "abc", "function", + "cloud_done", + "backup", + "cloud_download", + "share", + "ios_share", ], codePoints: { speed: "e9e4", @@ -39,6 +44,7 @@ const config: IconsConfig = { counter_1: "f784", counter_2: "f783", counter_3: "f782", + ios_share: "e6b8", }, } diff --git a/src/lib/serial/chord.spec.ts b/src/lib/serial/chord.spec.ts index 952d5303..d60c5bc0 100644 --- a/src/lib/serial/chord.spec.ts +++ b/src/lib/serial/chord.spec.ts @@ -2,8 +2,6 @@ import {describe, it, expect} from "vitest" import { chordAsCommandCompatible, chordFromCommandCompatible, - chordsFromFile, - chordsToFile, deserializeActions, serializeActions, } from "./chord" @@ -41,16 +39,4 @@ describe("chords", function () { }) }) }) - - describe("chl file format", function () { - const fileData: Chord[] = [ - {phrase: [1, 2, 3, 4], actions: [5, 6, 7, 8, 9]}, - {phrase: [10, 11], actions: [12, 13, 14, 15]}, - {phrase: [16], actions: [17]}, - ] - - it("should should convert back-forth a file", function () { - expect(chordsFromFile(chordsToFile(fileData))).toEqual(fileData) - }) - }) }) diff --git a/src/lib/serial/chord.ts b/src/lib/serial/chord.ts index 81dce2c0..2da7dd16 100644 --- a/src/lib/serial/chord.ts +++ b/src/lib/serial/chord.ts @@ -57,97 +57,3 @@ export function deserializeActions(native: bigint): number[] { return actions } - -const CHL_VERSION = 1 -const CHL_MAGIC = "CHL" - -/** - * Binary serialization of the chord library - * - * Layout is as follows: - * ```rs - * struct Chords { - * magic: "CHL" - * version: u8 - * chordCount: u32 - * chords: chord[] - * } - * - * struct Chord { - * id: u32 - * phrase: u128 - * actionCount: u16 - * actions: u8[] - * } - * ``` - * Serialized as little endian. - * - * @param chords - */ -export function chordsToFile(chords: Chord[]): ArrayBuffer { - const actionsTotalCount = chords.reduce((size, chord) => size + chord.actions.length, 0) - - const buffer = new ArrayBuffer(4 + 4 + chords.length * (4 + 16 + 2) + actionsTotalCount) - const view = new DataView(buffer) - let byteOffset = 0 - - for (const byte of CHL_MAGIC.split("")) { - view.setUint8(byteOffset++, byte.codePointAt(0)!) - } - view.setUint8(byteOffset++, CHL_VERSION) - view.setUint32(byteOffset, chords.length, true) - byteOffset += 4 - for (const chord of chords) { - const actions = serializeActions(chord.actions) - view.setBigUint64(byteOffset, actions >> 64n, true) - byteOffset += 8 - view.setBigUint64(byteOffset, actions & 0xffff_ffff_ffff_ffffn, true) - byteOffset += 8 - - view.setUint16(byteOffset, chord.phrase.length, true) - byteOffset += 2 - for (const action of chord.phrase) { - view.setUint8(byteOffset++, action) - } - } - - return buffer -} - -/** - * @see {chordsToFile} - */ -export function chordsFromFile(buffer: ArrayBuffer): Chord[] { - const view = new DataView(buffer) - let byteOffset = 0 - - const magic = [] - for (let i = 0; i < CHL_MAGIC.length; i++) { - magic.push(view.getUint8(byteOffset++)) - } - const magicString = String.fromCodePoint(...magic) - if (magicString !== CHL_MAGIC) throw new Error(`Not a .chl file [magic ${magicString}]`) - if (view.getUint8(byteOffset++) !== CHL_VERSION) throw Error("Invalid .chl [version]") - - const chords: Chord[] = Array.from({length: view.getUint32(byteOffset, true)}) - byteOffset += 4 - for (let i = 0; i < chords.length; i++) { - let actions = view.getBigUint64(byteOffset, true) << 64n - byteOffset += 8 - actions |= view.getBigUint64(byteOffset, true) - byteOffset += 8 - - const phrase: number[] = Array.from({length: view.getUint16(byteOffset, true)}) - byteOffset += 2 - for (let i = 0; i < phrase.length; i++) { - phrase[i] = view.getUint8(byteOffset++) - } - - chords[i] = { - actions: deserializeActions(actions), - phrase, - } - } - - return chords -} diff --git a/src/lib/serial/serialization.ts b/src/lib/serial/serialization.ts new file mode 100644 index 00000000..d60c43ea --- /dev/null +++ b/src/lib/serial/serialization.ts @@ -0,0 +1,33 @@ +/** + * Compress JSON.stringify with gzip + */ +export async function stringifyCompressed(chords: any): Promise { + const stream = new Blob([JSON.stringify(chords)]).stream().pipeThrough(new CompressionStream("gzip")) + return await new Response(stream).blob() +} + +/** + * Decompress JSON.parse with gzip + */ +export async function parseCompressed(blob: Blob): Promise { + const stream = blob.stream().pipeThrough(new DecompressionStream("gzip")) + return await new Response(stream).json() +} + +/** + * Share JS object as url query param + */ +export async function getSharableUrl(name: string, data: any, baseHref = window.location.href): Promise { + return new Promise(async resolve => { + const reader = new FileReader() + reader.onloadend = function () { + const base64String = (reader.result as string) + .replace(/^data:application\/octet-stream;base64,/, "") + .replace(/==$/, "") + const url = new URL(baseHref) + url.searchParams.set(name, base64String) + resolve(url) + } + reader.readAsDataURL(await stringifyCompressed(data)) + }) +} diff --git a/src/routes/config/chords/+page.svelte b/src/routes/config/chords/+page.svelte index e49b8529..10d78fcc 100644 --- a/src/routes/config/chords/+page.svelte +++ b/src/routes/config/chords/+page.svelte @@ -1,10 +1,11 @@ @@ -55,6 +70,12 @@ {/each} +
+ +
cloud_done
+ + +