-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: i am frontend, i write javascript
- Loading branch information
theprimeagen
committed
May 21, 2024
1 parent
7280907
commit b096502
Showing
16 changed files
with
551 additions
and
14 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,19 @@ | ||
import decode from "./decode/index.js" | ||
import { parseFrame } from "./net/frame.js" | ||
import { WS } from "./ws/index.js" | ||
|
||
// TODO: provide a url? | ||
|
||
/** | ||
* @param {HTMLElement} el | ||
*/ | ||
function run(el) { | ||
console.log("here i am") | ||
decode.test() | ||
const ws = new WS("ws://localhost:8080/ws") | ||
|
||
ws.onMessage(async function(blob) { | ||
const buf = new Uint8Array(await blob.arrayBuffer()) | ||
const frame = parseFrame(buf) | ||
console.log(frame.cmd, frame.data.byteLength) | ||
}) | ||
} | ||
|
||
run(document.body) |
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,23 @@ | ||
/** | ||
* @param {any} truthy | ||
* @param {string} msg | ||
* */ | ||
export function debugAssert(truthy, msg) { | ||
if (!truthy) { | ||
debugger | ||
console.error(msg) | ||
} | ||
} | ||
|
||
/** | ||
* no idea how to make that work... | ||
* @param {any} truthy | ||
* @param {string} msg | ||
* @returns {asserts truthy is true} | ||
* */ | ||
export function assert(truthy, msg) { | ||
if (!truthy) { | ||
throw new Error(msg) | ||
} | ||
} | ||
|
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,9 @@ | ||
|
||
/** | ||
* @param {Uint8Array} buf | ||
* @param {number} offset | ||
*/ | ||
export function read16(buf, offset) { | ||
return (buf[offset] << 8) + buf[offset + 1] | ||
} | ||
|
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,48 @@ | ||
import { assert, debugAssert } from "../assert" | ||
|
||
export class EightBitWriter { | ||
/** @type {Uint8Array | null} */ | ||
#buffer = null | ||
|
||
/** @type {number} */ | ||
#idx | ||
|
||
constructor() { | ||
this.#idx = 0 | ||
} | ||
|
||
data() { | ||
return this.#buffer?.slice(0, this.#idx) ?? new Uint8Array(0) | ||
} | ||
|
||
len() { | ||
return this.#idx | ||
} | ||
|
||
/** @param {Uint8Array} buf */ | ||
reset(buf) { | ||
this.#idx = 0 | ||
this.#buffer = buf | ||
} | ||
|
||
/** | ||
* @param {number} num | ||
* @returns {boolean} | ||
**/ | ||
write(num) { | ||
assert(this.#buffer !== null, "expected buffer to not equal null") | ||
|
||
// TODO: figure out how to make assert "type safe" | ||
if (this.#buffer === null) { | ||
return false | ||
} | ||
|
||
if (this.#buffer.byteLength === this.#idx) { | ||
return false | ||
} | ||
|
||
this.#buffer[this.#idx++] = num | ||
return true | ||
} | ||
} | ||
|
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,15 @@ | ||
export const types = { | ||
open: 0, | ||
brightnessToAscii: 1, | ||
frame: 2, | ||
} | ||
|
||
export const encodings = { | ||
NONE: 1, | ||
XOR_RLE: 2, | ||
HUFFMAN: 3, | ||
XOR_BIT_DIFF: 4, // Not implement, but i am horned up for it | ||
XOR_HUFFMAN: 5, | ||
HUFFMAN_QUADTREE: 6, // Maybe implement? | ||
XOR_RLE_QUADTREE: 7, // Maybe implement? | ||
} |
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,194 @@ | ||
/** @typedef {import("./types.ts").DecodeFrame} DecodeFrame */ | ||
|
||
import { assert } from "../assert.js"; | ||
import { read16 } from "../bytes/utils.js"; | ||
import { types, encodings } from "../cmds.js"; | ||
import { scratchArr, scratchWriter8Bit } from "../scratch.js"; | ||
|
||
/** | ||
* @param {import("../types.ts").Frame} frame | ||
* @return DecodeFrame | null | ||
*/ | ||
export function createFrame(frame) { | ||
if (frame.cmd !== types.frame) { | ||
return null | ||
} | ||
|
||
return { frame } | ||
} | ||
|
||
/** | ||
* @param {DecodeFrame} decode | ||
* @return {boolean} | ||
*/ | ||
function isHuffmanEncoded(decode) { | ||
return decode.frame.data[0] === encodings.HUFFMAN | ||
} | ||
|
||
/** | ||
* @param {DecodeFrame} decode | ||
* @return {boolean} | ||
*/ | ||
function isXOR_RLE(decode) { | ||
return decode.frame.data[0] === encodings.XOR_RLE | ||
} | ||
|
||
/** | ||
* @param {Uint8Array} decoder | ||
* @param {number} idx | ||
* @returns {number} | ||
**/ | ||
function left(decoder, idx) { | ||
assert(decoder.byteLength > idx + 5, "decoder length + idx is shorter than huffmanNode decode length") | ||
return read16(decoder, idx + 2) | ||
} | ||
|
||
/** | ||
* @param {Uint8Array} decoder | ||
* @param {number} idx | ||
* @returns {number} | ||
**/ | ||
function right(decoder, idx) { | ||
assert(decoder.byteLength > idx + 5, "decoder length + idx is shorter than huffmanNode decode length") | ||
return read16(decoder, idx + 4) | ||
} | ||
|
||
/** | ||
* @param {Uint8Array} decoder | ||
* @param {number} idx | ||
* @param {number} bit | ||
* @returns {number} | ||
**/ | ||
function jump(decoder, idx, bit) { | ||
if (bit === 1) { | ||
return right(decoder, idx) | ||
} | ||
return left(decoder, idx) | ||
} | ||
|
||
/** | ||
* @param {Uint8Array} decoder | ||
* @param {number} idx | ||
* @returns {number} | ||
**/ | ||
function value(decoder, idx) { | ||
return read16(decoder, idx) | ||
} | ||
|
||
/** | ||
* @param {Uint8Array} decoder | ||
* @param {number} idx | ||
* @returns {boolean} | ||
**/ | ||
function isLeaf(decoder, idx) { | ||
assert(decoder.byteLength > idx + 5, "decoder length + idx is shorter than huffmanNode decode length") | ||
return read16(decoder, idx + 2) == 0 && | ||
read16(decoder, idx + 4) == 0 | ||
} | ||
|
||
/** | ||
* @param {Uint8Array} decodingTree | ||
* @param {Uint8Array} data | ||
* @param {number} bitLength | ||
* @param {import("../types.ts").ByteWriter} writer | ||
*/ | ||
function decodeHuffman(decodingTree, data, bitLength, writer) { | ||
assert(data.byteLength >= bitLength/8 + 1, "you did not provide enough data") | ||
|
||
let idx = 0 | ||
let decodeIdx = 0 | ||
|
||
outer: | ||
while (true) { | ||
for (let bitIdx = 7; bitIdx >= 0; bitIdx--) { | ||
const bit = (data[idx] >> bitIdx) & 0x1 | ||
bitLength-- | ||
|
||
decodeIdx = jump(decodingTree, decodeIdx, bit) | ||
|
||
if (isLeaf(decodingTree, decodeIdx)) { | ||
|
||
if (!writer.write(value(decodingTree, decodeIdx))) { | ||
throw new Error("unable to write value into buffer") | ||
} | ||
|
||
decodeIdx = 0 | ||
} | ||
|
||
if (bitLength === 0) { | ||
break outer | ||
} | ||
} | ||
|
||
idx++ | ||
} | ||
} | ||
|
||
/** | ||
* @param {DecodeFrame} decode | ||
*/ | ||
function expandHuffman(decode) { | ||
// 1 byte to encoding type (huffman) | ||
// 1 + 2 bytes bitLen | ||
// 3 + 2 bytes decodingTreeLength | ||
|
||
const bitLen = read16(decode.decodeFrame, 1) | ||
const decodingTreeLength = read16(decode.decodeFrame, 3) | ||
const decodingTree = decode.decodeFrame.subarray(5, 5 + decodingTreeLength) | ||
const writer = scratchWriter8Bit() | ||
|
||
decodeHuffman(decodingTree, decode.decodeFrame, bitLen, writer) | ||
decode.decodeFrame = writer.data() | ||
|
||
assert(decode.decodeFrame.byteLength > 0, "decoding failed") | ||
} | ||
|
||
|
||
/** | ||
* @param {DecodeFrame} decode | ||
*/ | ||
function expandXOR_RLE(decode) { | ||
if (decode.prevDecodeFrame === null) { | ||
return | ||
} | ||
|
||
let idx = 0 | ||
const data = decode.frame.data | ||
for (let i = 1; i < data.length; i += 2) { | ||
const repeat = data[i] | ||
const char = data[i + 1] | ||
for (let count = 0; count < repeat; count++, idx++) { | ||
scratchArr[idx] = char ^ decode.prevDecodeFrame[idx] | ||
} | ||
} | ||
|
||
// TODO: Copy within? | ||
decode.decodeFrame = scratchArr.slice(0, idx) | ||
} | ||
|
||
/** | ||
* @param {DecodeFrame} decode | ||
*/ | ||
export function expand(decode) { | ||
if (isXOR_RLE(decode)) { | ||
expandXOR_RLE(decode) | ||
} else { | ||
expandHuffman(decode) | ||
} | ||
} | ||
|
||
/** | ||
* @param {DecodeFrame} decode | ||
* @return {Uint8Array} | ||
*/ | ||
export function asciiPixel(decode) { | ||
const frame = decode.decodeFrame | ||
const out = new Uint8Array(frame.byteLength) | ||
for (let j = 0, i = 0; i < frame.byteLength; ++i, j += 2) { | ||
out[j] = frame[i] | ||
out[j + 1] = frame[i] | ||
} | ||
return out | ||
} | ||
|
||
|
This file was deleted.
Oops, something went wrong.
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,8 @@ | ||
import { Frame } from "../types" | ||
|
||
export type DecodeFrame = { | ||
frame: Frame, | ||
decodeFrame: Uint8Array, | ||
prevDecodeFrame: Uint8Array | null, | ||
length: number, | ||
} |
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,25 @@ | ||
import { debugAssert } from "../assert.js" | ||
import { read16 } from "../bytes/utils.js" | ||
|
||
const VERSION = 1 | ||
const HEADER_SIZE = 4 | ||
|
||
/** | ||
* @param {Uint8Array} buf | ||
* @return {import("../types.ts").Frame} | ||
* */ | ||
export function parseFrame(buf) { | ||
const v = VERSION + 8 | ||
const D = buf[0] | ||
debugAssert(v - 8===D, "the frame received doesn't have version alignment") | ||
const cmd = buf[1] | ||
|
||
const len = read16(buf, 2) | ||
|
||
debugAssert(buf.byteLength - HEADER_SIZE === len, "the frame received doesn't have version alignment") | ||
|
||
return { | ||
cmd, | ||
data: buf.subarray(HEADER_SIZE), | ||
} | ||
} |
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,11 @@ | ||
import { EightBitWriter } from "./bytes/writer" | ||
export const scratchBuff = new ArrayBuffer(1024 * 1024) | ||
export const scratchArr = new Uint8Array(scratchBuff) | ||
|
||
const writer = new EightBitWriter() | ||
/** @returns {import("./types").ByteWriter} */ | ||
export function scratchWriter8Bit() { | ||
writer.reset(scratchArr) | ||
return writer | ||
} | ||
|
Oops, something went wrong.