Last active
January 22, 2025 22:21
-
-
Save Fuzzyma/bcd48102e28dcc9f54413f3b9c5c34bf to your computer and use it in GitHub Desktop.
This file contains hidden or 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
export function createBase64Encoder() { | |
let leftover = null; // Buffer for leftover bytes from the previous chunk | |
return new TransformStream({ | |
transform(chunk, controller) { | |
// Concatenate leftover from the previous chunk | |
let input = leftover ? new Uint8Array([...leftover, ...chunk]) : chunk; | |
// Process in chunks of 3 bytes | |
const length = input.length - (input.length % 3); | |
for (let i = 0; i < length; i += 3) { | |
const threeBytes = input.subarray(i, i + 3); | |
const binary = String.fromCharCode(...threeBytes); | |
controller.enqueue(btoa(binary)); | |
} | |
// Save remaining bytes for the next chunk | |
leftover = input.slice(length); | |
}, | |
flush(controller) { | |
// Process leftover bytes at the end (if any) | |
if (leftover && leftover.length > 0) { | |
const binary = String.fromCharCode(...leftover); | |
controller.enqueue(btoa(binary).replace(/=+$/, "")); // Strip padding | |
} | |
}, | |
}); | |
} | |
export function createBase64Decoder() { | |
let buffer = ""; // Buffer to hold incomplete Base64 chunks | |
return new TransformStream({ | |
transform(chunk, controller) { | |
// Concatenate chunks into the buffer | |
buffer += chunk; | |
// Process complete Base64 segments (multiple of 4) | |
const validLength = buffer.length - (buffer.length % 4); | |
const validBase64 = buffer.slice(0, validLength); | |
buffer = buffer.slice(validLength); // Keep the remaining part in the buffer | |
if (validBase64) { | |
const binaryString = atob(validBase64); | |
const uint8Array = new Uint8Array(binaryString.length); | |
for (let i = 0; i < binaryString.length; i++) { | |
uint8Array[i] = binaryString.charCodeAt(i); | |
} | |
controller.enqueue(uint8Array); | |
} | |
}, | |
flush(controller) { | |
// Process remaining buffered data on stream close | |
if (buffer) { | |
const binaryString = atob(buffer); | |
const uint8Array = new Uint8Array(binaryString.length); | |
for (let i = 0; i < binaryString.length; i++) { | |
uint8Array[i] = binaryString.charCodeAt(i); | |
} | |
controller.enqueue(uint8Array); | |
} | |
}, | |
}); | |
} | |
export async function textStreamToString(stream) { | |
const reader = stream.getReader(); | |
let result = ""; | |
let done = false; | |
while (!done) { | |
const { value, done: isDone } = await reader.read(); | |
if (value) { | |
result += value; // Directly append the text chunk | |
} | |
done = isDone; | |
} | |
return result; | |
} | |
export async function arrayStreamToString(stream, encoding = "utf-8") { | |
const reader = stream.getReader(); | |
const decoder = new TextDecoder(encoding); | |
let result = ""; | |
let done = false; | |
while (!done) { | |
const { value, done: isDone } = await reader.read(); | |
if (value) { | |
result += decoder.decode(value, { stream: true }); | |
} | |
done = isDone; | |
} | |
// Ensure the final decoding is handled | |
result += decoder.decode(); | |
return result; | |
} | |
export async function compressAndEncode(input) { | |
const inputStream = new ReadableStream({ | |
start(controller) { | |
controller.enqueue(new TextEncoder().encode(input)); | |
controller.close(); | |
}, | |
}); | |
const compressedStream = inputStream.pipeThrough( | |
new CompressionStream("gzip") | |
); | |
const encodedStream = compressedStream.pipeThrough(createBase64Encoder()); | |
const base64String = await textStreamToString(encodedStream); | |
const encodedURI = encodeURIComponent(base64String); | |
return encodedURI; | |
} | |
export async function decodeAndDecompress(encodedURI) { | |
const base64String = decodeURIComponent(encodedURI); | |
const base64Stream = new ReadableStream({ | |
start(controller) { | |
controller.enqueue(base64String); | |
controller.close(); | |
}, | |
}); | |
const decodedStream = base64Stream.pipeThrough(createBase64Decoder()); | |
const decompressedStream = decodedStream.pipeThrough( | |
new DecompressionStream("gzip") | |
); | |
const output = await arrayStreamToString(decompressedStream, "utf-8"); | |
return output; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment