From a3d02441742df7f38fb502a130fe81c9578fa32e Mon Sep 17 00:00:00 2001 From: Konrad Jamrozik Date: Sat, 29 Jun 2024 22:28:30 -0700 Subject: [PATCH] ongoin attempts at compressing in client --- .vscode/settings.json | 5 +++ web/src/lib/storedData/StoredData.ts | 5 ++- web/test/lib_tests/compression.test.ts | 61 +++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9a61a99e..da341319 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -57,4 +57,9 @@ ], "colorize.decoration_type": "background", "colorize.hide_current_line_decorations": false, + "search.exclude": { + "**/.git": true, + "**/node_modules": true, + "**/build": true + } } diff --git a/web/src/lib/storedData/StoredData.ts b/web/src/lib/storedData/StoredData.ts index 2f9e6c7a..d9969743 100644 --- a/web/src/lib/storedData/StoredData.ts +++ b/web/src/lib/storedData/StoredData.ts @@ -45,13 +45,16 @@ export class StoredData { value: StoredDataTypeMap[T], ): void { const json: string = JSON.stringify(value) + // kja WIP + // const gzippedJson: string = zlib.gzipSync(json).toString('base64') console.log( - `setInLocalStorage. key: '${key}'. json.length: ${json.length}.`, + `setInLocalStorage. key: '${key}'. json.length: ${json.length}, gzippedJson.length: ${json.length}.`, ) try { // https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem // Max in Chrome is 5_000_000 bytes for the entire local storage. See storage.test.ts for details. localStorage.setItem(key, json) + // localStorage.setItem(`${key}_gzipped`, gzippedJson) } catch (error: unknown) { if (error instanceof DOMException) { // https://developer.mozilla.org/en-US/docs/Web/API/DOMException diff --git a/web/test/lib_tests/compression.test.ts b/web/test/lib_tests/compression.test.ts index adb01589..52ebd889 100644 --- a/web/test/lib_tests/compression.test.ts +++ b/web/test/lib_tests/compression.test.ts @@ -1,3 +1,5 @@ +/* eslint-disable max-statements */ +/* eslint-disable max-lines-per-function */ import * as fs from 'node:fs' import path from 'node:path' import { promisify } from 'node:util' @@ -15,7 +17,6 @@ const inputDataStr = '{"turns":[{"EventsUntilStartState":[],"StartState":{"IsGameOver":false,"IsGameLost":false,"IsGameWon":false,"Timeline":{"CurrentTurn":1},"Assets":{"Money":500,"Intel":0,"Funding":20,"Support":30,"CurrentTransportCapacity":4,"MaxTransportCapacity":4,"Agents":[]},"MissionSites":[],"Missions":[],"TerminatedAgents":[],"Factions":[{"Id":0,"Name":"Black Lotus cult","Power":200,"MissionSiteCountdown":3,"PowerClimb":4,"PowerAcceleration":8,"AccumulatedPowerAcceleration":0,"IntelInvested":0},{"Id":1,"Name":"Red Dawn remnants","Power":300,"MissionSiteCountdown":3,"PowerClimb":5,"PowerAcceleration":5,"AccumulatedPowerAcceleration":0,"IntelInvested":0},{"Id":2,"Name":"EXALT","Power":400,"MissionSiteCountdown":9,"PowerClimb":6,"PowerAcceleration":4,"AccumulatedPowerAcceleration":0,"IntelInvested":0},{"Id":3,"Name":"Zombies","Power":100,"MissionSiteCountdown":6,"PowerClimb":1,"PowerAcceleration":20,"AccumulatedPowerAcceleration":0,"IntelInvested":0}],"UpdateCount":0},"EventsInTurn":[],"EndState":{"IsGameOver":false,"IsGameLost":false,"IsGameWon":false,"Timeline":{"CurrentTurn":1},"Assets":{"Money":500,"Intel":0,"Funding":20,"Support":30,"CurrentTransportCapacity":4,"MaxTransportCapacity":4,"Agents":[]},"MissionSites":[],"Missions":[],"TerminatedAgents":[],"Factions":[{"Id":0,"Name":"Black Lotus cult","Power":200,"MissionSiteCountdown":3,"PowerClimb":4,"PowerAcceleration":8,"AccumulatedPowerAcceleration":0,"IntelInvested":0},{"Id":1,"Name":"Red Dawn remnants","Power":300,"MissionSiteCountdown":3,"PowerClimb":5,"PowerAcceleration":5,"AccumulatedPowerAcceleration":0,"IntelInvested":0},{"Id":2,"Name":"EXALT","Power":400,"MissionSiteCountdown":9,"PowerClimb":6,"PowerAcceleration":4,"AccumulatedPowerAcceleration":0,"IntelInvested":0},{"Id":3,"Name":"Zombies","Power":100,"MissionSiteCountdown":6,"PowerClimb":1,"PowerAcceleration":20,"AccumulatedPowerAcceleration":0,"IntelInvested":0}],"UpdateCount":0},"AdvanceTimeEvent":null}]}' describe('compression tests', () => { - // eslint-disable-next-line max-statements test('test json-gzip compression file round-trip', async () => { const repoDir = path.normalize(`${import.meta.dirname}/../../..`) const inputFilePath = path.normalize(`${repoDir}/input.json`) @@ -45,6 +46,7 @@ describe('compression tests', () => { // reduction to: 3.66 % // // or + // // minifiedData.length: 4_963_132 // compressedData.length: 231_230 // reduction to: 4.66 % @@ -79,3 +81,60 @@ describe('compression tests', () => { } }) }) + +// Research: +// https://pieroxy.net/blog/pages/lz-string/index.html + +/* + + // Skipped as it doesn't work. + // Based on: + // - https://stackoverflow.com/questions/50681564/gzip-a-string-in-javascript-using-pako-js + // - https://github.com/nodeca/pako#examples--api + // - https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string + // - https://chatgpt.com/c/7c58252d-c8f5-48c2-a622-32849f217372 + test.skip('test pako string round-trip', () => { + const inputData = JSON.stringify(JSON.parse(inputDataStr)) + const compressedData = pakoInflate(inputData) + console.log( + `pako: inputDataStr.length: ${inputDataStr.length}, inputData.length: ${inputData.length}, compressedData.length: ${compressedData.length}`, + ) + const decompressedData = pakoDeflate(compressedData) + expect(decompressedData.length).toBe(inputData.length) + expect(decompressedData).toBe(inputData) + }) + + function pakoInflate(inputStr: string): string { + // Compress the JSON string + + const deflated: Uint8Array = pako.deflate(inputStr) + + const decoder = new TextDecoder('utf8') + const decoded = decoder.decode(deflated) + + console.log( + `compressJson: inputStr.length: ${inputStr.length}, deflated.length: ${deflated.length}, ` + + `decoded.length: ${decoded.length}`, + ) + + return decoded + } + + function pakoDeflate(compressedString: string): string { + const encoder = new TextEncoder() + + const encoded = encoder.encode(compressedString) + + const inflated = pako.inflate(encoded) + + const decoder = new TextDecoder('utf8') + const decoded = decoder.decode(inflated) + + console.log( + `decompressJson: compressedString.length: ${compressedString.length}, encoded.length: ${encoded.length}, ` + + `inflated.length: ${inflated.length}, decoded.length: ${decoded.length}`, + ) + return decoded + } + +*/