commit 72b700daef9a319d5dca2f504ea3cf41c4c56462 Author: Logan Date: Sun Apr 28 17:11:48 2024 -0500 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cb090aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +lgatlin.dev.cert +lgatlin.dev.priv +/logs diff --git a/404.html b/404.html new file mode 100644 index 0000000..7079916 --- /dev/null +++ b/404.html @@ -0,0 +1,56 @@ + + + + + + + + + + + + +
+ +
+
+

+ Page does not exist +

+

+ Error 404 +

+ + + +
+
+
+ + + diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..835ebb0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2462 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitstream-io" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "built" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41bfbdb21256b87a8b5e80fab81a8eed158178e812fd7ba451907518b2742f16" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.52.4", +] + +[[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "com" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" +dependencies = [ + "com_macros", +] + +[[package]] +name = "com_macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" +dependencies = [ + "com_macros_support", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "com_macros_support" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "d3d12" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" +dependencies = [ + "bitflags 2.5.0", + "libloading 0.8.3", + "winapi", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "destructure_traitobject" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" + +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fishbowl" +version = "0.1.0" +dependencies = [ + "bytemuck", + "futures", + "futures-intrusive", + "gif", + "image", + "tokio", + "wgpu", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.5.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +dependencies = [ + "log", + "presser", + "thiserror", + "winapi", + "windows", +] + +[[package]] +name = "gpu-descriptor" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" +dependencies = [ + "bitflags 2.5.0", + "gpu-descriptor-types", + "hashbrown", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hassle-rs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" +dependencies = [ + "bitflags 2.5.0", + "com", + "libc", + "libloading 0.8.3", + "thiserror", + "widestring", + "winapi", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "image" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d" +dependencies = [ + "byteorder-lite", + "thiserror", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.3", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.4", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +dependencies = [ + "serde", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" +dependencies = [ + "anyhow", + "arc-swap", + "chrono", + "derivative", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "once_cell", + "parking_lot", + "rand", + "serde", + "serde-value", + "serde_json", + "serde_yaml", + "thiserror", + "thread-id", + "typemap-ors", + "winapi", +] + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "metal" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +dependencies = [ + "bitflags 2.5.0", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "naga" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" +dependencies = [ + "bit-set", + "bitflags 2.5.0", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "num-traits", + "rustc-hash", + "spirv", + "termcolor", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.58", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "range-alloc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc3bcbdb1ddfc11e700e62968e6b4cc9c75bb466464ad28fb61c5b2c964418b" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "server" +version = "0.1.0" +dependencies = [ + "ascii", + "fishbowl", + "log", + "log4rs", + "tiny_http", + "tokio", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.5.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "thread-id" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", + "openssl", + "zeroize", +] + +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typemap-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsafe-any-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "wgpu" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01" +dependencies = [ + "arrayvec", + "cfg-if", + "cfg_aliases", + "js-sys", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.5.0", + "cfg_aliases", + "codespan-reporting", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle", + "rustc-hash", + "smallvec", + "thiserror", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1a4924366df7ab41a5d8546d6534f1f33231aa5b3f72b9930e300f254e39c3" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.5.0", + "block", + "cfg_aliases", + "core-graphics-types", + "d3d12", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.3", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "rustc-hash", + "smallvec", + "thiserror", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" +dependencies = [ + "bitflags 2.5.0", + "js-sys", + "web-sys", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +dependencies = [ + "memchr", +] + +[[package]] +name = "xml-rs" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fd56b7e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "server" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tiny_http = {version="0.12", features=["ssl-openssl"]} +log4rs = "1.3" +log = "0.4" +tokio = {version = "1", features = ["full"]} +ascii = "1.1" +fishbowl = {path="../fishbowl"} + diff --git a/html b/html new file mode 100755 index 0000000..675b137 Binary files /dev/null and b/html differ diff --git a/hyper-build/404.html b/hyper-build/404.html new file mode 100644 index 0000000..fa66741 --- /dev/null +++ b/hyper-build/404.html @@ -0,0 +1,3 @@ +

Page does not exist

Error 404

\ No newline at end of file diff --git a/hyper-build/home.html b/hyper-build/home.html new file mode 100644 index 0000000..4d39d3c --- /dev/null +++ b/hyper-build/home.html @@ -0,0 +1,358 @@ + <loganGatlin/>
\ No newline at end of file diff --git a/hyper-build/projects/fishbowl.html b/hyper-build/projects/fishbowl.html new file mode 100644 index 0000000..bb219ac --- /dev/null +++ b/hyper-build/projects/fishbowl.html @@ -0,0 +1,402 @@ + <loganGatlin/>

Fishbowl View source on GitHub

Vector Math - Standard Library

Gallery

Motivation

+ Physics solvers tend to be very complex software. In the + past, I toyed around with physics solvers for different kinds of geometries, but + struggled to implement these algorithms in a way that was both convincing + and performant. The idea for Fishbowl came from a video by the creator + Pezzza. They describe the high-level implementation details of a Verlet + Integration solver. This approach caught my interest. +
+ Pezza's work is very impressive, particularly the demonstration at the end + which proves the program is deterministic. However, the focus of Pezzza's + project was to create a performant real-time application. I was instead + interested in pre-rendered images and animations. I decided to expand the + one-off demonstration at the end of Pezzza's video into a fully fledged + app. +

Approach

+ At the center of any physics solver is the physics 'step', a fixed interval of + time where objects move and collide. The physics step of Fishbowl consists of five + distinct phases. +
+

+// Keep all objects inside the simulation bounds
+self.constrain_rect();
+// Sort objects from left-most to right-most.
+self.sort();
+// Apply collision impulses
+self.collide();
+// Calculate new velocities and accelerations
+self.integrate();
+// Finally, advance the simulation clock
+self.clock += 1;
+    
+
+
+ Because the program doesn't run in real time, simulating over one second + may take more or less than a second. Calculating a larger number of shorter + time steps will make the simulation more accurate, but will take longer to + process. Fishbowl uses an number of optimizations to reduce processing time. + By keeping the array of objects sorted from left to right, the number of collision + checks is significantly reduced. The rendering system is decoupled from the simulation, + and the two are pipelined together and run in parallel. +

Rendering

+ The method I used for drawing circles to a frame went through numerous iterations. + Rendering performance is a major bottleneck, and so it was important to do so in + parallel. After writing and benchmarking several software renderers, I landed on + WebGPU. While quite bloated, WebGPU provides a fast, low level, and cross platform + rendering API designed for async programs. This allows Fishbowl to easily integrate + with both my server, which is entirely async, and the simulation, which is mostly + synchronous. While I would have liked to use a bespoke solution, GPU accelerated + rendering is far too quick to realistically consider any other option. +

Try It Out

+ You can try out Fishbowl below, or download the CLI tool from my GitHub. + It works best on images that are roughly square and don't contain any text. + Keep in mind the web version below will be slower and generate lower quality + results than the native application, as it is being run on my home server with + limited resources. +


\ No newline at end of file diff --git a/hyper-build/projects/forte.html b/hyper-build/projects/forte.html new file mode 100644 index 0000000..c93ff77 --- /dev/null +++ b/hyper-build/projects/forte.html @@ -0,0 +1,394 @@ + <loganGatlin/>

Forte View source on GitHub

Programming Language - Hackathon

Motivation

+ In February of 2024, I competed at RowdyHacks during the 9th + annual Hackathon. This was my second time attending, having + + placed second the previous year + . + My understanding of programming and of Rust had grown significantly over + that time, and I wanted to one-up myself. I had just finished my Computer + Organization class, where we learned to program in x86 assembly. The class + made me realize that the design of an assembly language has a profound + impact on every higher level language built on-top of it. As a Rust + programmer who cares a great deal about safety and soundness guarantees, it + troubles me that I have to compile to an inherently unsafe assembly + language. I wanted to find out how feasible it would be to write a new + assembly language designed for easy static analysis and safety guarantees. + I am very satisfied with the result, which I named Forte. +

Specifications

Instructions

+ Forte is an assembly language and bytecode for a hypothetical 128-bit + processor. It contains 26 instructions, but no directly exposed registers. +
Name Mnemonic
Push push
Pop pop
Duplicate dup
Add add
Difference diff
Multiply mul
Divide div
Remainder rem
Bitwise And and
Bitwise Or or
Bitwise Xor xor
Shift Right shr
Shift Left shl
Name (cont.) Mnemonic (cont.)
Branch Equal beq
Branch Unequal bne
Branch Greater bgt
Branch Lesser blt
Function Start fun
Call call
Return ret
Loop loop
Iterate Loop iter
Begin Execution exe
Store sto
Load lod
Stack Length len

The Three Stacks

+ The stack is an essential part of any assembly language, however it is + often a point of vulnerability. Stack-smashing and stack-overflow + vulnerabilities can very easily allow attackers to overwright function + return addresses, and and execute arbitrary code. This is why Forte uses + three distinct stacks. +

The Program Stack

+ The program stack, or p-stack for short, contains all of the state for the + user-space program. When you push or pop a value in Forte, you are + interacting with the p-stack. What's most interesting about the p-stack is + what it does not contain: pointers. There is no way to jump to an address + on the p-stack, as its contents are considered untrusted. +

The Control Stack

+ In order to call functions, return addresses must be stored somewhere. + While most assembly languages place these on the stack alongside other + values, Forte segregates them into a separate control stack (c-stack for + short.) This is similar to the "shadow stack" option some compilers use, + except implemented at a hardware level. The only way to interact with the + c-stack is through call and return instructions. +

The Function Stack

+ Most assembly languages have a "jump" instruction which moves the program + counter to an arbitrary point in memory. This is useful but not + particularly safe. Forte only allows jumping to valid functions. The + locations of these functions are kept in the function stack (f-stack.) + Functions are added to the f-stack after they are validated during the + warmup phase, which I will discuss next. +

Warmup

+ When a Forte program begins, it does not immediately start executing + instructions. Instead, it validates the programs correctness and builds + the f-stack. Every fun (function start) instruction will + push the address of that instruction to the f-stack. Every instruction that + interacts with the stack will increment or decrement an internal register + accordingly. If this value value falls below zero or above the maximum + stack size, then the program is determined to be unsafe and execution + is cancelled before it begins. The warmup phase will take O(N) time, + where N is the number of instructions. +

Recital

+ After the exe (begin execution) instruction is reached, the + Recital phase begins. The function pointer returns to the address at the + top of the f-stack, which was the last defined function. When the program + counter reaches the exe again, the program has terminated + successfully. This combination of design decisions means that the program + counter can never be lower than the first function address, or larger than + the execution instruction. This makes arbitrary code execution attacks more + difficult to pull off. +

Safety

+ Creating a memory safe assembly language presents much different challenges + from a compiled language like Rust. There are no compile-time checks or + guarantees, any string of bytes could be interpreted as a "program." The core + design principle of Forte is to make unsecure programs impossible (or at least + very difficult) to express, and to make static analysis simple. This is a delicate + balance to strike - limiting Forte's capabilities necessarily makes it more + cumbersome and less performant. +
\ No newline at end of file diff --git a/hyper-build/projects/fractal-explorer.html b/hyper-build/projects/fractal-explorer.html new file mode 100644 index 0000000..a7df32f --- /dev/null +++ b/hyper-build/projects/fractal-explorer.html @@ -0,0 +1,415 @@ + <loganGatlin/>

Fractal Explorer View source on GitHub

Parallel Algorithms - Optimization - Hackathon Finalist

Motivation

+ In March of 2023 I attended RowdyHacks 8, my first hackathon. I was + in the middle of my Calculus II class, which I was enjoying a lot. With + Math at the front of my mind, I decided to write a program to visualize + the Mandelbrot Fractal. I had written a similar program in Snap, a visual + block-based programming language, so I was confident I could achieve results + within the 24 hour time limit. I wanted to specifically optimize the program + for speed without using GPU acceleration, because the laptop I was using did + not have one. +

Approach

+ The premise behind the Mandelbrot Fractal is to take a complex number, and + insert it into a recursive function. If the function does not diverge to + infinity, then that number is part of the Mandelbrot Set, and it gets plotted + on the screen. + Calculating the Mandelbrot Set is deceptively simple. The hard part is doing so + with a very large degree of precision, and repeating the calculations millions of + times (prefferably in parallel.) +
+

+// Snippet from my initial naive algorithm
+//
+// This will produce the value of one pixel on the screen
+pub fn fractal(val: Complex, max_iterations: usize) -> usize {
+  let mut iterations = 0;
+  let mut last = Complex::new(0.0, 0.0);
+  let mut squared = Complex::new(0.0, 0.0);
+  // Values exceeding 4 usually diverge. If the number
+  // does not diverge after `max_iterations`, it probably
+  // never will.
+  while squared.re + squared.im <= 4.0 && 
+    iterations < max_iterations {
+    let im = 2.0 * last.re * last.im + val.im;
+    let re = squared.re - squared.im + val.re;
+    last = Complex { re, im };
+    squared.re = re.powi(2);
+    squared.im = im.powi(2);
+    iterations += 1;
+  }
+  iterations
+}
+
+
+ While researching the Mandelbrot set, I found a faster solution called the derivative + bail algorithm. This algorithm considers the first derivative of the function rather + than the functions value itself, which is more accurate and performant most of the time. +
+

+// Snippet from my derivative bail implementation
+pub fn get_dbail(val: Complex, max_dvt: f64, max_iter: usize) -> usize {
+  let max_dvt = max_dvt.powi(2);
+  let mut it = 0;
+  let mut last = Complex::new(0.0, 0.0);
+  let mut squared = Complex::new(0.0, 0.0);
+  let mut deriv = Complex::new(0.0, 0.0);
+  while squared.re + squared.im <= 4.0
+    && deriv.re.powi(2) + deriv.im.powi(2) <= max_dvt
+    && it <= max_iter
+  {
+    deriv.re = 2.0 * (deriv.re * val.re - deriv.im * val.im);
+    deriv.im = 2.0 * (deriv.re * val.im + deriv.im * val.re);
+    let im = 2.0 * last.re * last.im + val.im;
+    let re = squared.re - squared.im + val.re;
+    last = Complex { re, im };
+    squared.re = re.powi(2);
+    squared.im = im.powi(2);
+    it += 1;
+  }
+  it
+}
+
+
+ Deciding whether a number does or does not fall into the set is more of a heuristic + than an analytic process. A good algorithm balances accuracy with efficiency. The + most glaring limitation of any calculation is the precision of the floating point + numbers used, especially when zooming in to the fractal. I deliberated the pros and + cons of using a floating point library with very high, or even arbitrary precision. + After extensive benchmarking, I was unable to get any of these to be efficient enough. +

Multi-Threading

+ In order to display the fractal, I needed an array of pixel data I could push to the + screen. The X and Y coordinates represent real and imaginary components respectively. + This means I need to run the function for every pixel on the screen. The Mandelbrot + Fractal is an obvious candidate for multi-threading, because the result of every + calculation is independent, and there are many calculations that need to be done. + I achieve this by breaking the pixel buffer into N partitions, and spawning a thread + to handle each (where N can be changed at runtime). This allowed me to rapidly iterate, + and find the optimal number of threads for the best performance. +

Results

+ I finished the project right before the deadline. This was my first time competing at + a hackathon, and the most code I had ever written in a 24 hour period. I was absolutely + exhausted, with no real idea of what to expect. I presented my project to the judges and + the other hackers at the event. The reception was incredibly positive, and I met many + brilliant people I stay in contact with to this day. My project won 2nd prize overall + at the event, for which I am extremely honored and greatful. +
\ No newline at end of file diff --git a/hyper-build/projects/html-templating.html b/hyper-build/projects/html-templating.html new file mode 100644 index 0000000..2a17ced --- /dev/null +++ b/hyper-build/projects/html-templating.html @@ -0,0 +1,432 @@ + <loganGatlin/>

HTML Templating Engine View source on GitHub

Front End - Parser Design

Motivation

+ HTML is a powerful tool for creating static web pages, but + is not easily modular or scalable. Front end frameworks like + React provide reusable components. I want to use components + without the overhead of a front-end framework. I want the final output to be statically generated rather than generated on the fly + by the server or the client, minified and with inlined CSS and + JavaScript. +

Approach

+ After looking at some of the HTML parsing libraries, I was + unsatisfied with the approaches most developers took. The DOM + is represented as a tree structure, where each node keeps a + pointer to its children. +
+

+// From the html_parser crate
+pub enum Node {
+    Text(String),
+    Element(Element),
+    Comment(String),
+}
+
+pub struct Element {
+    pub name: String,
+    pub attributes: HashMap<String, Option<String>>,
+    pub children: Vec<Node>,
+    // other fields omitted
+}
+
+
+ This is bad for cache locality and effectively forces the use + of recursion in order to traverse the DOM. Recursion is undesirable + for performance and robustness reasons. My approach is to create a + flat array of elements. Parsing is done using a non-recursive parser + combinator model. +
+

+// My implementation (fields omitted)
+pub enum HtmlElement {
+  DocType,
+  Comment(/* */),
+  OpenTag { /* */ },
+  CloseTag { /* */ },
+  Text( /* */ ),
+  Script { /* */ },
+  Style { /* */ },
+  Directive { /* */ },
+}
+
+
+ This approach lends itself to linear iterative algorithms. An + element's children can be represented as a slice of the DOM array, + from the opening tag to its close tag. +

Templates

+ I define a "template" as a user defined element which expands into + a larger piece of HTML. Templates can pass on children and attributes + to the final output. Some special templates can perform other functions, + like inlining a CSS file or syntax highlighting a code block. +
+

+<!-- Here is a template definition -->
+<Echo> 
+  <h1 class=@class1> 
+    <@children/>
+  </h1>
+  <h2 class=@class2> 
+    <@children/> 
+  </h2>
+  <h3 class=@class3>  
+    <@children/> 
+  </h3>
+</Echo>
+
+<!-- And this is how to use the template -->
+<Echo class1="some_class1" class2="some_class2" class3="some_class3">
+  Echo!
+</Echo>
+
+<!-- Which expands to this -->
+<h1 class="some_class1"> 
+  Echo!
+</h1>
+<h2 class="some_class2"> 
+  Echo!
+</h2>
+<h3 class="some_class3">  
+  Echo!
+</h3>
+
+
+ The example above shows how templates can inherit attributes and child + elements from their invocation. Special templates begin with the '@' + symbol, and perform some sort of meta-function. In addition to + <@children/>, <@code/> performs syntax highlighting, and + <@style/> inlines a CSS file. + +

Dependencies

+ The templating engine only relies on two crates for performing syntax + highlighting of code blocks. It uses inkjet, a thin wrapper over + treesitter. Treesitter is an efficient error-tolerant language parser + commonly used in IDEs. While it is fast, it is by far the slowest and + most cumbersome part of the program. Good looking formatted code is + important for my website, and implementing parsers for every language + I use is out of scope, so this is a necessary evil. +
+

+// Snippet from Cargo.toml
+[dependencies]
+inkjet = "0.10"
+v_htmlescape = "0.15"
+
+
\ No newline at end of file diff --git a/hyper-build/projects/http-server.html b/hyper-build/projects/http-server.html new file mode 100644 index 0000000..2ad823b --- /dev/null +++ b/hyper-build/projects/http-server.html @@ -0,0 +1,380 @@ + <loganGatlin/>

Rust HTTP Server

Keywords: Back End - TCP - SSL

Motivation

+ As a systems programmer, I want to understand the technologies + I use at a very low level. Modern web stacks abstract away the + process of handling TCP connections, serving SSL certificates, + and parsing HTTP requests. The best way to understand a technology + is to implement it from the ground up, I studied the HTTP 1.1 + specification and wrote a server to host my website. +

Approach

+ At the most basic level, the server will translate the request URL + into a local file path, and respond with the file matching that path + if it exists. Request URLs are sanitized to prevent accessing files + outside of the server's directory. Two tables are used for rewriting + and routing URLs. This makes handling URLs predictable and robust. +
+

+// Snippet from the request handling code
+pub async fn construct_response(&self, request_url: &str) -> Response {
+  let sanitized_url = rewrite_url(request_url);
+  if sanitized_url != request_url {
+    return Self::redirect(&sanitized_url);
+  }
+
+  let routed_url = self.perform_routing(&sanitized_url).await;
+  if let Some(response) = 
+    self.perform_redirecting(&routed_url).await {
+    return response;
+  }
+  match self.get_resource(&routed_url).await {
+    Some(bytes) => Response::from_data(bytes),
+    None => Self::not_found(),
+  }
+}
+
+
+ I use Cloudflare to manage my domain name, DNS records, and SSL certificates. + Cloudflare automatically caches resources, rewrites HTTP requests to HTTPS, + and limits request sizes. This allows my server to only listen on port 443 + and neglect caching server-side. +

Dependencies

+ My goal with this project was to provide a functional server using as few + libraries as possible. The final project directly depends on four crates: +
+

+// Snippet from Cargo.toml
+[dependencies]
+log = "0.4"
+log4rs = "1.3"
+tiny_http = "0.12"
+tokio = {version = "1", features = ["full"]}
+
+
+ The first two crates, log and log4rs, provide logging functions which aren't critical to the + server's functionality. The tiny_http crate provides a simple wrapper for the standard + library TCP stream, and performs SSL encryption. It is used as the backbone + for many Rust web frameworks. While I could implement these features myself, I + decided against it for security and browser compatibility reasons. Finally there is + tokio, an async runtime. This is necessary because Rust does not provide a runtime + by default, and building one is out of scope. +
\ No newline at end of file diff --git a/hyper-build/projects/math-interpreter.html b/hyper-build/projects/math-interpreter.html new file mode 100644 index 0000000..1628bec --- /dev/null +++ b/hyper-build/projects/math-interpreter.html @@ -0,0 +1,324 @@ + <loganGatlin/>

This page is under construction

Check back later

\ No newline at end of file diff --git a/hyper-build/projects/nd-range.html b/hyper-build/projects/nd-range.html new file mode 100644 index 0000000..beac44a --- /dev/null +++ b/hyper-build/projects/nd-range.html @@ -0,0 +1,390 @@ + <loganGatlin/>

nd-range

Vector Math - Standard Library

Motivation

+ The Rust standard library provides several 'Range' types which + represent integers inside a given bounds (i.e. 1 ≤ n ≤ 100). + Ranges can be iterated over, and used for bounds testing of numbers. + In designing video games, it is often necessary to express 2 or 3 + dimensional quantities or bounds. Problems like iterating over + voxels in a 3D world, or testing for an intersection between rectangles + lend themselves to the idea of Ranges. +
+

+// A 3D array representing a 128x128x128 voxel world
+let world_data = [[[0; 128]; 128]; 128];
+// The '0..128' here is a range from 0 to 127 inclusive
+for x in 0..128 {
+  for y in 0..128 {
+    for z in 0..128 {
+      let voxel = &world[x][y][z];
+      // Do something with voxel
+    }
+  }
+}
+
+
+ However, in these contexts the Range type is not particularly helpful. + I am forced to use three different ranges and a triple nested 'for' loop. + The idea for nd-range is to expand the native Range type so that it + can be used over arbitrary dimensions. This allows us to iterate over + X, Y, and Z coordinates in a single loop. +
+

+let world_data = [[[0; 128]; 128]; 128];
+for [x, y, z] in nrange!(0..128, 0..128, 0..128) {
+  let voxel = &world[x][y][z];
+  // Do something with voxel
+}
+
+

Approach

+ An nd-range is similar in concept to an axis-aligned bounding box, + or AABB. These are common abstractions used in game development, + and there are well established algorithms that test for overlap. + Iterating over is slightly more complex, requiring some vector + algebra. If we interpret the range of values over each axis as a + vector, we can apply the cartesian product algorithm to find every + position within the bounded space. Below is a diagram from Wikipedia + (CC BY-SA 3.0). + + Because a range is contiguous by definition, generating a cartesian + product is simple and performant. The iterator object has a space + complexity of O(N), where n is the number of dimensions. Rust's const generics + allow the entire struct to exist on the stack, which is a boon to performance. +

Performance

+ My points of comparison are the itertools and cartesian crates, which provide + comparable algorithms. Despite its popularity, I was shocked to discover how + slow the itertools implementation of the cartesian product is. The cartesian-rs + crate is lesser known, and uses a creative macro-based solution. I benchmarked + all three approaches, as well as the nested for-loop base case, over a 100x100x100 + range. +
Implementation Average Time (ns) Error (+/- ns)
+ itertools + 1,821,573 31,501
+ cartesian-rs + 989,242 50,835
nd-range 968,853 15,792
nested loops 911,853 46,066

Pros and Cons

+ My implementation is competitive with, and possibly faster than + its competitors for my use case. While nd-range only works for + ranges of contiguous integers, itertools and cartesian-rs work + for any iterators. However, by restricting my use case I can + extract more performance gains and integrate better with the Rust + standard library. +
\ No newline at end of file diff --git a/hyper-build/projects/pokedex.html b/hyper-build/projects/pokedex.html new file mode 100644 index 0000000..cfcab1e --- /dev/null +++ b/hyper-build/projects/pokedex.html @@ -0,0 +1,324 @@ + <loganGatlin/>

This page is under construction

Check back later

\ No newline at end of file diff --git a/hyper-build/projects/stocktrading.html b/hyper-build/projects/stocktrading.html new file mode 100644 index 0000000..2e37c16 --- /dev/null +++ b/hyper-build/projects/stocktrading.html @@ -0,0 +1,324 @@ + <loganGatlin/>

This page is under construction

Check back later

\ No newline at end of file diff --git a/hyper-src/404.html b/hyper-src/404.html new file mode 100644 index 0000000..2e2cb6e --- /dev/null +++ b/hyper-src/404.html @@ -0,0 +1 @@ + diff --git a/hyper-src/home.html b/hyper-src/home.html new file mode 100644 index 0000000..5238f92 --- /dev/null +++ b/hyper-src/home.html @@ -0,0 +1,108 @@ + + + +
+

Dev Posts

+ + + + +

Web Server

+

Back End - TCP - SSL

+

+ Currently serving you this website! +

+
+ + + +

HTML Templating Engine

+

Front End - Parser Design

+

+ Used to generate this page! +

+
+ + + +

Forte Assembly Language

+

Programming Language - Hackathon

+

+ Radically different machine code. A creative-coding endeavor +

+
+ + + +

Fishbowl

+

Image Encoding - Hardware Rendering

+

Kinematic image processing with GPU acceleration

+
+ + + +

Math Interpreter

+

Parser Design

+

+ Interpret and evaluate plain-text math expressions +

+
+ + +

nd-range

+

Vector Math - Standard Library

+

+ An extension of Rust's 'Range' type + using the Cartesian Product Algorithm +

+
+ + + +

Fractal Explorer

+

Parallel Algorithms - Optimization - Hackathon

+

+ A Mandelbrot Fractal viewer using CPU parallelism + and the derivative bail algorithm +

+
+ + +

Pokédex

+

TKinter - Web APIs - Native UI

+

+ A TKinter app for viewing the original Pokédex, with + stats scraped from online sources +

+
+ + +

Stock Trading A.I.

+

Command Line App - Web APIs

+

+ A simple heuristic trading algorithm +

+
+ +
+
diff --git a/hyper-src/projects/fishbowl.html b/hyper-src/projects/fishbowl.html new file mode 100644 index 0000000..4c876c1 --- /dev/null +++ b/hyper-src/projects/fishbowl.html @@ -0,0 +1,115 @@ + +
+

Fishbowl

+

Vector Math - Standard Library

+
+ +
+

Gallery

+
+
+
+
+ +
+
+ +
+
+

Motivation

+ Physics solvers tend to be very complex software. In the + past, I toyed around with physics solvers for different kinds of geometries, but + struggled to implement these algorithms in a way that was both convincing + and performant. The idea for Fishbowl came from a video by the creator + Pezzza. They describe the high-level implementation details of a Verlet + Integration solver. This approach caught my interest. +
+ +
+ Pezza's work is very impressive, particularly the demonstration at the end + which proves the program is deterministic. However, the focus of Pezzza's + project was to create a performant real-time application. I was instead + interested in pre-rendered images and animations. I decided to expand the + one-off demonstration at the end of Pezzza's video into a fully fledged + app. +

Approach

+ At the center of any physics solver is the physics 'step', a fixed interval of + time where objects move and collide. The physics step of Fishbowl consists of five + distinct phases. + <@code lang="rust"> +// Keep all objects inside the simulation bounds +self.constrain_rect(); +// Sort objects from left-most to right-most. +self.sort(); +// Apply collision impulses +self.collide(); +// Calculate new velocities and accelerations +self.integrate(); +// Finally, advance the simulation clock +self.clock += 1; + + Because the program doesn't run in real time, simulating over one second + may take more or less than a second. Calculating a larger number of shorter + time steps will make the simulation more accurate, but will take longer to + process. Fishbowl uses an number of optimizations to reduce processing time. + By keeping the array of objects sorted from left to right, the number of collision + checks is significantly reduced. The rendering system is decoupled from the simulation, + and the two are pipelined together and run in parallel. +

Rendering

+ The method I used for drawing circles to a frame went through numerous iterations. + Rendering performance is a major bottleneck, and so it was important to do so in + parallel. After writing and benchmarking several software renderers, I landed on + WebGPU. While quite bloated, WebGPU provides a fast, low level, and cross platform + rendering API designed for async programs. This allows Fishbowl to easily integrate + with both my server, which is entirely async, and the simulation, which is mostly + synchronous. While I would have liked to use a bespoke solution, GPU accelerated + rendering is far too quick to realistically consider any other option. +

Try It Out

+ You can try out Fishbowl below, or download the CLI tool from my GitHub. + It works best on images that are roughly square and don't contain any text. + Keep in mind the web version below will be slower and generate lower quality + results than the native application, as it is being run on my home server with + limited resources. +

+ +
+
+ + +
+ +
+
+ + +
+
diff --git a/hyper-src/projects/forte.html b/hyper-src/projects/forte.html new file mode 100644 index 0000000..58fd773 --- /dev/null +++ b/hyper-src/projects/forte.html @@ -0,0 +1,203 @@ + +
+

Forte

+

Programming Language - Hackathon

+
+
+

Motivation

+ In February of 2024, I competed at RowdyHacks during the 9th + annual Hackathon. This was my second time attending, having + + placed second the previous year + . + My understanding of programming and of Rust had grown significantly over + that time, and I wanted to one-up myself. I had just finished my Computer + Organization class, where we learned to program in x86 assembly. The class + made me realize that the design of an assembly language has a profound + impact on every higher level language built on-top of it. As a Rust + programmer who cares a great deal about safety and soundness guarantees, it + troubles me that I have to compile to an inherently unsafe assembly + language. I wanted to find out how feasible it would be to write a new + assembly language designed for easy static analysis and safety guarantees. + I am very satisfied with the result, which I named Forte. +

Specifications

+

Instructions

+ Forte is an assembly language and bytecode for a hypothetical 128-bit + processor. It contains 26 instructions, but no directly exposed registers. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name Mnemonic
Push push
Pop pop
Duplicate dup
Add add
Difference diff
Multiply mul
Divide div
Remainder rem
Bitwise And and
Bitwise Or or
Bitwise Xor xor
Shift Right shr
Shift Left shl
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name (cont.) Mnemonic (cont.)
Branch Equal beq
Branch Unequal bne
Branch Greater bgt
Branch Lesser blt
Function Start fun
Call call
Return ret
Loop loop
Iterate Loop iter
Begin Execution exe
Store sto
Load lod
Stack Length len
+
+

The Three Stacks

+ The stack is an essential part of any assembly language, however it is + often a point of vulnerability. Stack-smashing and stack-overflow + vulnerabilities can very easily allow attackers to overwright function + return addresses, and and execute arbitrary code. This is why Forte uses + three distinct stacks. +

The Program Stack

+ The program stack, or p-stack for short, contains all of the state for the + user-space program. When you push or pop a value in Forte, you are + interacting with the p-stack. What's most interesting about the p-stack is + what it does not contain: pointers. There is no way to jump to an address + on the p-stack, as its contents are considered untrusted. +

The Control Stack

+ In order to call functions, return addresses must be stored somewhere. + While most assembly languages place these on the stack alongside other + values, Forte segregates them into a separate control stack (c-stack for + short.) This is similar to the "shadow stack" option some compilers use, + except implemented at a hardware level. The only way to interact with the + c-stack is through call and return instructions. +

The Function Stack

+ Most assembly languages have a "jump" instruction which moves the program + counter to an arbitrary point in memory. This is useful but not + particularly safe. Forte only allows jumping to valid functions. The + locations of these functions are kept in the function stack (f-stack.) + Functions are added to the f-stack after they are validated during the + warmup phase, which I will discuss next. +

Warmup

+ When a Forte program begins, it does not immediately start executing + instructions. Instead, it validates the programs correctness and builds + the f-stack. Every fun (function start) instruction will + push the address of that instruction to the f-stack. Every instruction that + interacts with the stack will increment or decrement an internal register + accordingly. If this value value falls below zero or above the maximum + stack size, then the program is determined to be unsafe and execution + is cancelled before it begins. The warmup phase will take O(N) time, + where N is the number of instructions. +

Recital

+ After the exe (begin execution) instruction is reached, the + Recital phase begins. The function pointer returns to the address at the + top of the f-stack, which was the last defined function. When the program + counter reaches the exe again, the program has terminated + successfully. This combination of design decisions means that the program + counter can never be lower than the first function address, or larger than + the execution instruction. This makes arbitrary code execution attacks more + difficult to pull off. +

Safety

+ Creating a memory safe assembly language presents much different challenges + from a compiled language like Rust. There are no compile-time checks or + guarantees, any string of bytes could be interpreted as a "program." The core + design principle of Forte is to make unsecure programs impossible (or at least + very difficult) to express, and to make static analysis simple. This is a delicate + balance to strike - limiting Forte's capabilities necessarily makes it more + cumbersome and less performant. +
+ +
diff --git a/hyper-src/projects/fractal-explorer.html b/hyper-src/projects/fractal-explorer.html new file mode 100644 index 0000000..c37bfde --- /dev/null +++ b/hyper-src/projects/fractal-explorer.html @@ -0,0 +1,97 @@ + +
+

Fractal Explorer

+

Parallel Algorithms - Optimization - Hackathon Finalist

+
+
+

Motivation

+ In March of 2023 I attended RowdyHacks 8, my first hackathon. I was + in the middle of my Calculus II class, which I was enjoying a lot. With + Math at the front of my mind, I decided to write a program to visualize + the Mandelbrot Fractal. I had written a similar program in Snap, a visual + block-based programming language, so I was confident I could achieve results + within the 24 hour time limit. I wanted to specifically optimize the program + for speed without using GPU acceleration, because the laptop I was using did + not have one. +

Approach

+ The premise behind the Mandelbrot Fractal is to take a complex number, and + insert it into a recursive function. If the function does not diverge to + infinity, then that number is part of the Mandelbrot Set, and it gets plotted + on the screen. + Calculating the Mandelbrot Set is deceptively simple. The hard part is doing so + with a very large degree of precision, and repeating the calculations millions of + times (prefferably in parallel.) +<@code lang="rust"> +// Snippet from my initial naive algorithm +// +// This will produce the value of one pixel on the screen +pub fn fractal(val: Complex, max_iterations: usize) -> usize { + let mut iterations = 0; + let mut last = Complex::new(0.0, 0.0); + let mut squared = Complex::new(0.0, 0.0); + // Values exceeding 4 usually diverge. If the number + // does not diverge after `max_iterations`, it probably + // never will. + while squared.re + squared.im <= 4.0 && + iterations < max_iterations { + let im = 2.0 * last.re * last.im + val.im; + let re = squared.re - squared.im + val.re; + last = Complex { re, im }; + squared.re = re.powi(2); + squared.im = im.powi(2); + iterations += 1; + } + iterations +} + + While researching the Mandelbrot set, I found a faster solution called the derivative + bail algorithm. This algorithm considers the first derivative of the function rather + than the functions value itself, which is more accurate and performant most of the time. +<@code lang="rust"> +// Snippet from my derivative bail implementation +pub fn get_dbail(val: Complex, max_dvt: f64, max_iter: usize) -> usize { + let max_dvt = max_dvt.powi(2); + let mut it = 0; + let mut last = Complex::new(0.0, 0.0); + let mut squared = Complex::new(0.0, 0.0); + let mut deriv = Complex::new(0.0, 0.0); + while squared.re + squared.im <= 4.0 + && deriv.re.powi(2) + deriv.im.powi(2) <= max_dvt + && it <= max_iter + { + deriv.re = 2.0 * (deriv.re * val.re - deriv.im * val.im); + deriv.im = 2.0 * (deriv.re * val.im + deriv.im * val.re); + let im = 2.0 * last.re * last.im + val.im; + let re = squared.re - squared.im + val.re; + last = Complex { re, im }; + squared.re = re.powi(2); + squared.im = im.powi(2); + it += 1; + } + it +} + + Deciding whether a number does or does not fall into the set is more of a heuristic + than an analytic process. A good algorithm balances accuracy with efficiency. The + most glaring limitation of any calculation is the precision of the floating point + numbers used, especially when zooming in to the fractal. I deliberated the pros and + cons of using a floating point library with very high, or even arbitrary precision. + After extensive benchmarking, I was unable to get any of these to be efficient enough. +

Multi-Threading

+ In order to display the fractal, I needed an array of pixel data I could push to the + screen. The X and Y coordinates represent real and imaginary components respectively. + This means I need to run the function for every pixel on the screen. The Mandelbrot + Fractal is an obvious candidate for multi-threading, because the result of every + calculation is independent, and there are many calculations that need to be done. + I achieve this by breaking the pixel buffer into N partitions, and spawning a thread + to handle each (where N can be changed at runtime). This allowed me to rapidly iterate, + and find the optimal number of threads for the best performance. +

Results

+ I finished the project right before the deadline. This was my first time competing at + a hackathon, and the most code I had ever written in a 24 hour period. I was absolutely + exhausted, with no real idea of what to expect. I presented my project to the judges and + the other hackers at the event. The reception was incredibly positive, and I met many + brilliant people I stay in contact with to this day. My project won 2nd prize overall + at the event, for which I am extremely honored and greatful. +
+
diff --git a/hyper-src/projects/html-templating.html b/hyper-src/projects/html-templating.html new file mode 100644 index 0000000..ef9b019 --- /dev/null +++ b/hyper-src/projects/html-templating.html @@ -0,0 +1,111 @@ + +
+

HTML Templating Engine

+

Front End - Parser Design

+
+
+

Motivation

+ HTML is a powerful tool for creating static web pages, but + is not easily modular or scalable. Front end frameworks like + React provide reusable components. I want to use components + without the overhead of a front-end framework. I want the final output to be statically generated rather than generated on the fly + by the server or the client, minified and with inlined CSS and + JavaScript. +

Approach

+ After looking at some of the HTML parsing libraries, I was + unsatisfied with the approaches most developers took. The DOM + is represented as a tree structure, where each node keeps a + pointer to its children. +<@code lang="rust"> +// From the html_parser crate +pub enum Node { + Text(String), + Element(Element), + Comment(String), +} + +pub struct Element { + pub name: String, + pub attributes: HashMap>, + pub children: Vec, + // other fields omitted +} + + This is bad for cache locality and effectively forces the use + of recursion in order to traverse the DOM. Recursion is undesirable + for performance and robustness reasons. My approach is to create a + flat array of elements. Parsing is done using a non-recursive parser + combinator model. +<@code lang="rust"> +// My implementation (fields omitted) +pub enum HtmlElement { + DocType, + Comment(/* */), + OpenTag { /* */ }, + CloseTag { /* */ }, + Text( /* */ ), + Script { /* */ }, + Style { /* */ }, + Directive { /* */ }, +} + + This approach lends itself to linear iterative algorithms. An + element's children can be represented as a slice of the DOM array, + from the opening tag to its close tag. +

Templates

+ I define a "template" as a user defined element which expands into + a larger piece of HTML. Templates can pass on children and attributes + to the final output. Some special templates can perform other functions, + like inlining a CSS file or syntax highlighting a code block. +<@code lang="html"> + + +

+ <@children/> +

+

+ <@children/> +

+

+ <@children/> +

+
+ + + + Echo! + + + +

+ Echo! +

+

+ Echo! +

+

+ Echo! +

+ + The example above shows how templates can inherit attributes and child + elements from their invocation. Special templates begin with the '@' + symbol, and perform some sort of meta-function. In addition to + <@children/>, <@code/> performs syntax highlighting, and + <@style/> inlines a CSS file. + +

Dependencies

+ The templating engine only relies on two crates for performing syntax + highlighting of code blocks. It uses inkjet, a thin wrapper over + treesitter. Treesitter is an efficient error-tolerant language parser + commonly used in IDEs. While it is fast, it is by far the slowest and + most cumbersome part of the program. Good looking formatted code is + important for my website, and implementing parsers for every language + I use is out of scope, so this is a necessary evil. +<@code lang="toml"> +// Snippet from Cargo.toml +[dependencies] +inkjet = "0.10" +v_htmlescape = "0.15" + +
+
diff --git a/hyper-src/projects/http-server.html b/hyper-src/projects/http-server.html new file mode 100644 index 0000000..29b7774 --- /dev/null +++ b/hyper-src/projects/http-server.html @@ -0,0 +1,62 @@ + +
+

Rust HTTP Server

+

Keywords: Back End - TCP - SSL

+
+
+

Motivation

+ As a systems programmer, I want to understand the technologies + I use at a very low level. Modern web stacks abstract away the + process of handling TCP connections, serving SSL certificates, + and parsing HTTP requests. The best way to understand a technology + is to implement it from the ground up, I studied the HTTP 1.1 + specification and wrote a server to host my website. +

Approach

+ At the most basic level, the server will translate the request URL + into a local file path, and respond with the file matching that path + if it exists. Request URLs are sanitized to prevent accessing files + outside of the server's directory. Two tables are used for rewriting + and routing URLs. This makes handling URLs predictable and robust. +<@code lang="rust"> +// Snippet from the request handling code +pub async fn construct_response(&self, request_url: &str) -> Response { + let sanitized_url = rewrite_url(request_url); + if sanitized_url != request_url { + return Self::redirect(&sanitized_url); + } + + let routed_url = self.perform_routing(&sanitized_url).await; + if let Some(response) = + self.perform_redirecting(&routed_url).await { + return response; + } + match self.get_resource(&routed_url).await { + Some(bytes) => Response::from_data(bytes), + None => Self::not_found(), + } +} + + I use Cloudflare to manage my domain name, DNS records, and SSL certificates. + Cloudflare automatically caches resources, rewrites HTTP requests to HTTPS, + and limits request sizes. This allows my server to only listen on port 443 + and neglect caching server-side. +

Dependencies

+ My goal with this project was to provide a functional server using as few + libraries as possible. The final project directly depends on four crates: +<@code lang=toml> +// Snippet from Cargo.toml +[dependencies] +log = "0.4" +log4rs = "1.3" +tiny_http = "0.12" +tokio = {version = "1", features = ["full"]} + + The first two crates, log and log4rs, provide logging functions which aren't critical to the + server's functionality. The tiny_http crate provides a simple wrapper for the standard + library TCP stream, and performs SSL encryption. It is used as the backbone + for many Rust web frameworks. While I could implement these features myself, I + decided against it for security and browser compatibility reasons. Finally there is + tokio, an async runtime. This is necessary because Rust does not provide a runtime + by default, and building one is out of scope. +
+
diff --git a/hyper-src/projects/math-interpreter.html b/hyper-src/projects/math-interpreter.html new file mode 100644 index 0000000..4bd8d09 --- /dev/null +++ b/hyper-src/projects/math-interpreter.html @@ -0,0 +1 @@ + diff --git a/hyper-src/projects/nd-range.html b/hyper-src/projects/nd-range.html new file mode 100644 index 0000000..38b7150 --- /dev/null +++ b/hyper-src/projects/nd-range.html @@ -0,0 +1,108 @@ + +
+

nd-range

+

Vector Math - Standard Library

+
+
+

Motivation

+ The Rust standard library provides several 'Range' types which + represent integers inside a given bounds (i.e. 1 ≤ n ≤ 100). + Ranges can be iterated over, and used for bounds testing of numbers. + In designing video games, it is often necessary to express 2 or 3 + dimensional quantities or bounds. Problems like iterating over + voxels in a 3D world, or testing for an intersection between rectangles + lend themselves to the idea of Ranges. +<@code lang="rust"> +// A 3D array representing a 128x128x128 voxel world +let world_data = [[[0; 128]; 128]; 128]; +// The '0..128' here is a range from 0 to 127 inclusive +for x in 0..128 { + for y in 0..128 { + for z in 0..128 { + let voxel = &world[x][y][z]; + // Do something with voxel + } + } +} + + However, in these contexts the Range type is not particularly helpful. + I am forced to use three different ranges and a triple nested 'for' loop. + The idea for nd-range is to expand the native Range type so that it + can be used over arbitrary dimensions. This allows us to iterate over + X, Y, and Z coordinates in a single loop. +<@code lang="rust"> +let world_data = [[[0; 128]; 128]; 128]; +for [x, y, z] in nrange!(0..128, 0..128, 0..128) { + let voxel = &world[x][y][z]; + // Do something with voxel +} + +

Approach

+ An nd-range is similar in concept to an axis-aligned bounding box, + or AABB. These are common abstractions used in game development, + and there are well established algorithms that test for overlap. + Iterating over is slightly more complex, requiring some vector + algebra. If we interpret the range of values over each axis as a + vector, we can apply the cartesian product algorithm to find every + position within the bounded space. Below is a diagram from Wikipedia + (CC BY-SA 3.0). + + Because a range is contiguous by definition, generating a cartesian + product is simple and performant. The iterator object has a space + complexity of O(N), where n is the number of dimensions. Rust's const generics + allow the entire struct to exist on the stack, which is a boon to performance. +

Performance

+ My points of comparison are the itertools and cartesian crates, which provide + comparable algorithms. Despite its popularity, I was shocked to discover how + slow the itertools implementation of the cartesian product is. The cartesian-rs + crate is lesser known, and uses a creative macro-based solution. I benchmarked + all three approaches, as well as the nested for-loop base case, over a 100x100x100 + range. + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Implementation Average Time (ns) Error (+/- ns)
+ + itertools + + 1,821,573 31,501
+ + cartesian-rs + + 989,242 50,835
nd-range 968,853 15,792
nested loops 911,853 46,066
+

Pros and Cons

+ My implementation is competitive with, and possibly faster than + its competitors for my use case. While nd-range only works for + ranges of contiguous integers, itertools and cartesian-rs work + for any iterators. However, by restricting my use case I can + extract more performance gains and integrate better with the Rust + standard library. +
+
diff --git a/hyper-src/projects/pokedex.html b/hyper-src/projects/pokedex.html new file mode 100644 index 0000000..4bd8d09 --- /dev/null +++ b/hyper-src/projects/pokedex.html @@ -0,0 +1 @@ + diff --git a/hyper-src/projects/stocktrading.html b/hyper-src/projects/stocktrading.html new file mode 100644 index 0000000..eb937b2 --- /dev/null +++ b/hyper-src/projects/stocktrading.html @@ -0,0 +1 @@ + diff --git a/resources/CascadiaMonoPL.woff2 b/resources/CascadiaMonoPL.woff2 new file mode 100644 index 0000000..fc6ddd3 Binary files /dev/null and b/resources/CascadiaMonoPL.woff2 differ diff --git a/resources/award.svg b/resources/award.svg new file mode 100644 index 0000000..67c760b --- /dev/null +++ b/resources/award.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/resources/cloudflare-icon.svg b/resources/cloudflare-icon.svg new file mode 100644 index 0000000..d014bdc --- /dev/null +++ b/resources/cloudflare-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/favicon.svg b/resources/favicon.svg new file mode 100644 index 0000000..739a2de --- /dev/null +++ b/resources/favicon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/resources/github-icon.svg b/resources/github-icon.svg new file mode 100644 index 0000000..4eca062 --- /dev/null +++ b/resources/github-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/godot-icon.svg b/resources/godot-icon.svg new file mode 100644 index 0000000..9eea112 --- /dev/null +++ b/resources/godot-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/html5-icon.svg b/resources/html5-icon.svg new file mode 100644 index 0000000..c2dda3a --- /dev/null +++ b/resources/html5-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/java-icon.svg b/resources/java-icon.svg new file mode 100644 index 0000000..d4bbd1c --- /dev/null +++ b/resources/java-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/mona_lisa.gif b/resources/mona_lisa.gif new file mode 100644 index 0000000..f64ad45 Binary files /dev/null and b/resources/mona_lisa.gif differ diff --git a/resources/python-icon.svg b/resources/python-icon.svg new file mode 100644 index 0000000..bde3773 --- /dev/null +++ b/resources/python-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/raylib-logo.png b/resources/raylib-logo.png new file mode 100644 index 0000000..0edd29a Binary files /dev/null and b/resources/raylib-logo.png differ diff --git a/resources/rust-icon.svg b/resources/rust-icon.svg new file mode 100644 index 0000000..a1fe0d8 --- /dev/null +++ b/resources/rust-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/starry_night.gif b/resources/starry_night.gif new file mode 100644 index 0000000..f452497 Binary files /dev/null and b/resources/starry_night.gif differ diff --git a/resources/wanderer.gif b/resources/wanderer.gif new file mode 100644 index 0000000..cf3d824 Binary files /dev/null and b/resources/wanderer.gif differ diff --git a/resources/web-assembly-icon.svg b/resources/web-assembly-icon.svg new file mode 100644 index 0000000..64775a9 --- /dev/null +++ b/resources/web-assembly-icon.svg @@ -0,0 +1,5 @@ +web-assembly-icon \ No newline at end of file diff --git a/resources/wgpu-logo.svg b/resources/wgpu-logo.svg new file mode 100644 index 0000000..9e2d340 --- /dev/null +++ b/resources/wgpu-logo.svg @@ -0,0 +1 @@ +gpu \ No newline at end of file diff --git a/resume.pdf b/resume.pdf new file mode 100644 index 0000000..9455efe Binary files /dev/null and b/resume.pdf differ diff --git a/src/forms.rs b/src/forms.rs new file mode 100644 index 0000000..031a02a --- /dev/null +++ b/src/forms.rs @@ -0,0 +1,121 @@ +use std::collections::HashMap; + +type Parse<'a, T> = Option<(T, &'a [u8])>; + +fn parse_byte(i: &[u8], byte: u8) -> Parse { + let first = i.first()?; + if first == &byte { + Some((byte, &i[1..])) + } else { + None + } +} + +fn parse_escaped_codepoint(i: &[u8]) -> Parse { + let (_, i) = parse_byte(i, b'%')?; + let slice = i.get(..2)?; + let i = i.get(2..)?; + let num = u8::from_str_radix(std::str::from_utf8(slice).ok()?, 16).ok()?; + Some((num, i)) +} + +pub fn url_unescape(mut i: &[u8]) -> Option> { + let mut bytes = Vec::with_capacity(i.len()); + while let Some(b) = i.first() { + if b == &b'%' { + let (escaped, new_i) = parse_escaped_codepoint(i)?; + bytes.push(escaped); + i = new_i; + } else { + bytes.push(*b); + i = &i[1..]; + } + } + Some(bytes) +} + +/// Parses form bodies with enctype=x-www-form-urlencoded +/// (default) +pub fn parse_urlencoded(i: &[u8]) -> Option>> { + let mut output = HashMap::new(); + let args = i.split(|b| b == &b'&'); + for arg in args { + let unescaped = url_unescape(arg)?; + let (key, value) = unescaped.split_once(|b| b == &b'=')?; + output.insert(String::from_utf8_lossy(key).to_string(), value.to_vec()); + } + Some(output) +} + +pub fn parse_form(request: &mut tiny_http::Request) -> Option>> { + let content_type = request.headers().iter().find_map(|header| { + if header.field.as_str().to_ascii_lowercase() == "content-type" { + Some(header.value.clone()) + } else { + None + } + })?; + if let Some(s) = content_type + .as_str() + .strip_prefix("multipart/form-data; boundary=") + { + let mut boundary = vec![b'-', b'-']; + boundary.append(&mut s.as_bytes().to_vec()); + return parse_multipart_form(request, boundary); + } else if content_type.as_str() == "application/x-www-form-urlencoded" { + let mut bytes = vec![]; + request.as_reader().read_to_end(&mut bytes).ok()?; + return parse_urlencoded(&bytes); + } else { + log::warn!("Unrecognized form type: {}", content_type.as_str()); + None + } +} + +fn position_of(slice: &[u8], pattern: &[u8]) -> Option { + for index in 0..slice.len() { + if slice[..index].ends_with(pattern) { + return Some(index - pattern.len()); + } + } + None +} + +/// Parses forms with enctype=multipart/form-data +pub fn parse_multipart_form( + request: &mut tiny_http::Request, + boundary: Vec, +) -> Option>> { + let mut bytes = vec![]; + request.as_reader().read_to_end(&mut bytes).unwrap(); + // Strip out cruft to make it easy to parse + let mut bytes = bytes + .strip_prefix(b"--")? + .strip_suffix(b"--\r\n")? + .strip_suffix(boundary.as_slice())?; + + let mut map = HashMap::new(); + + while !bytes.is_empty() { + bytes = &bytes[boundary.len()..]; + // Find next boundary, or end of body + let end = position_of(bytes, &boundary).unwrap_or(bytes.len()); + let slice = &bytes[..end].strip_suffix(b"\r\n")?; + bytes = &bytes[end..]; + // Find end of headers + let end = position_of(slice, b"\r\n\r\n").unwrap_or(0); + let headers = &slice[..end]; + let string = String::from_utf8_lossy(headers); + let name = string + .lines() + .find_map(|line| line.strip_prefix("Content-Disposition: "))? + .split("; ") + .find_map(|arg| arg.strip_prefix("name="))? + .trim(); + let name = name.strip_prefix('"').unwrap_or(name); + let name = name.strip_suffix('"').unwrap_or(name); + let body = &slice[end + 4..]; + map.insert(name.into(), body.to_vec()); + } + Some(map) +} diff --git a/src/get.rs b/src/get.rs new file mode 100644 index 0000000..65176d9 --- /dev/null +++ b/src/get.rs @@ -0,0 +1,106 @@ +use std::{ + collections::HashMap, + path::{Component, Path, PathBuf}, +}; + +use tiny_http::Header; +use tokio::sync::RwLock; + +use crate::util::*; + +/// Makes the uri less spooky - removes leading /, rejects +/// ../ and ./ +pub fn rewrite_url(url: &str) -> String { + let (url, query) = url.split_once('?').unwrap_or((url, "")); + let target_path = Path::new(url); + let mut corrected_path = PathBuf::new(); + for c in target_path.components() { + match c { + Component::Normal(_) | Component::RootDir => { + corrected_path.push(c); + }, + Component::ParentDir => { + corrected_path.pop(); + }, + _ => {}, + } + } + corrected_path.to_string_lossy().to_string() +} + +pub struct Router { + /// Reinterpret A as B server-side + route_table: RwLock>, + /// Send a 301 redirect from A to B + routing_table: RwLock>, +} + +impl Router { + const NOT_FOUND: &'static [u8] = include_bytes!("../hyper-build/404.html"); + + pub fn new() -> Self { + Self { + route_table: RwLock::new(Default::default()), + routing_table: RwLock::new(Default::default()), + } + } + + fn redirect(to: &str) -> Response { + Response::from_data(&[]) + .with_status_code(301) + .with_header(header("Location", to)) + } + + fn not_found() -> Response { + Response::from_data(Self::NOT_FOUND).with_status_code(404) + } + + pub async fn construct_response(&self, request_url: &str) -> Response { + let sanitized_url = rewrite_url(request_url); + if sanitized_url != request_url { + return Self::redirect(&sanitized_url); + } + + let routed_url = self.perform_routing(&sanitized_url).await; + if let Some(response) = self.perform_redirecting(&routed_url).await { + return response; + } + + let mime_type = mime_from_file_ext(&routed_url); + + match self.get_resource(&routed_url).await { + Some(bytes) => Response::from_data(bytes) + .with_header(header("Content-Type", &mime_type)), + None => Self::not_found(), + } + } + + pub async fn create_route(&self, from: &str, to: &str) { + let from = rewrite_url(from); + let to = rewrite_url(to); + let mut w = self.route_table.write().await; + w.insert(from.into(), to.into()); + } + + async fn perform_routing(&self, uri: &str) -> String { + let r = self.route_table.read().await; + r.get(uri).cloned().unwrap_or(uri.to_string()) + } + + pub async fn create_redirect(&self, from: &str, to: &str) { + let from = rewrite_url(from); + let to = rewrite_url(to); + let mut w = self.routing_table.write().await; + w.insert(from.into(), to.into()); + } + + async fn perform_redirecting(&self, uri: &str) -> Option { + let r = self.routing_table.read().await; + r.get(uri).map(|url| Self::redirect(url)) + } + + pub async fn get_resource(&self, uri: &str) -> Option> { + let uri = uri.trim_start_matches('/'); + tokio::fs::read(&uri).await.ok() + } +} diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 0000000..80d2ddd --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,46 @@ +pub fn init() -> log4rs::Handle { + use log4rs::append::console::ConsoleAppender; + use log4rs::append::rolling_file::{ + policy::compound::*, RollingFileAppender, + }; + use log4rs::config::{Appender, Config, Logger, Root}; + use log4rs::encode::pattern::PatternEncoder; + + let stdout = ConsoleAppender::builder() + .encoder(Box::new(PatternEncoder::new("{d(%H:%M)} {l} - {m}{n}"))) + .build(); + + let connections = RollingFileAppender::builder() + .encoder(Box::new(PatternEncoder::new( + "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}", + ))) + .build( + "logs/connections.log", + Box::new(CompoundPolicy::new( + Box::new(trigger::size::SizeTrigger::new(1_000_000)), + Box::new( + roll::fixed_window::FixedWindowRoller::builder() + .base(1) + .build("logs/connections-{}.log", 5) + .unwrap(), + ), + )), + ) + .unwrap(); + + let config = Config::builder() + .appender(Appender::builder().build("stdout", Box::new(stdout))) + .appender(Appender::builder().build("connections", Box::new(connections))) + .logger( + Logger::builder() + .appender("connections") + .build("server", log::LevelFilter::Info), + ) + .build( + Root::builder() + .appender("stdout") + .build(log::LevelFilter::Info), + ) + .unwrap(); + log4rs::init_config(config).unwrap() +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..67ee67c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,130 @@ +#![feature(slice_split_once)] +mod forms; +mod get; +mod logging; +mod post; +mod util; + +use std::{io::Write, sync::Arc}; + +use get::Router; +use post::PostHandler; +use tiny_http::{Request, Server}; + +// Exit process if its already running +fn graceful_cleanup() { + const PID_PATH: &'static str = "./temp/pid"; + match { + let s = std::fs::read_to_string(PID_PATH).unwrap_or("".to_string()); + s.parse::().ok() + } { + Some(last_pid) => { + std::process::Command::new("pkill") + .arg(format!("{}", last_pid)) + .output() + .unwrap(); + }, + None => { + log::warn!("Did not find previous PID"); + }, + }; + let pid = std::process::id(); + let mut file = std::fs::File::create(PID_PATH).unwrap(); + file.write(format!("{}", pid).as_bytes()).unwrap(); +} + +#[tokio::main] +async fn main() { + // graceful_cleanup(); + let _h = logging::init(); + // let ssl = tiny_http::SslConfig { + // certificate: + // include_bytes!("../lgatlin.dev.cert").to_vec(), + // private_key: + // include_bytes!("../../lgatlin.dev.priv").to_vec(), }; + // let server = Server::https("0.0.0.0:443", ssl).unwrap(); + let server = Server::http("0.0.0.0:8080").unwrap(); + + let router = Arc::new(Router::new()); + let post = Arc::new(PostHandler); + router.create_redirect("/", "/home").await; + router + .create_redirect("/favicon.ico", "/resources/favicon.svg") + .await; + + router.create_route("/home", "/hyper-build/home.html").await; + router + .create_route("/projects", "/hyper-build/projects.html") + .await; + router + .create_route("/about", "hyper-build/about.html") + .await; + router + .create_route( + "/projects/http-server", + "hyper-build/projects/http-server.html", + ) + .await; + router + .create_route( + "/projects/html-templating", + "hyper-build/projects/html-templating.html", + ) + .await; + router + .create_route("/projects/forte", "hyper-build/projects/forte.html") + .await; + router + .create_route("/projects/fishbowl", "hyper-build/projects/fishbowl.html") + .await; + router + .create_route( + "/projects/math-interpreter", + "hyper-build/projects/math-interpreter.html", + ) + .await; + router + .create_route("/projects/nd-range", "hyper-build/projects/nd-range.html") + .await; + router + .create_route( + "/projects/fractal-explorer", + "hyper-build/projects/fractal-explorer.html", + ) + .await; + router + .create_route("/projects/pokedex", "hyper-build/projects/pokedex.html") + .await; + router + .create_route( + "/projects/stock-trading", + "hyper-build/projects/stocktrading.html", + ) + .await; + + for request in server.incoming_requests() { + log::info!("{:?} AT {:?}", request.method(), request.url(),); + let router = router.clone(); + let post = post.clone(); + tokio::spawn(async move { + handle_connection(request, router, post).await; + }); + } +} + +async fn handle_connection( + mut request: Request, + route: Arc, + post: Arc, +) { + use tiny_http::*; + let response = match request.method() { + Method::Get | Method::Head => route.construct_response(request.url()).await, + Method::Post => match post.handle_post(&mut request).await { + Some(response) => response, + None => return, + }, + _ => Response::from_string("Error occurred").with_status_code(405), + }; + let _ = request.respond(response); +} diff --git a/src/post.rs b/src/post.rs new file mode 100644 index 0000000..c509d66 --- /dev/null +++ b/src/post.rs @@ -0,0 +1,27 @@ +use crate::util::*; + +pub struct PostHandler; + +impl PostHandler { + async fn fishbowl_endpoint(&self, file: &[u8]) -> Vec { + fishbowl::make_gif(&file, fishbowl::make_quickdraw().await.unwrap()) + .await + .unwrap() + } + + pub async fn handle_post(&self, request: &mut tiny_http::Request) -> Option { + let form = crate::forms::parse_form(request)?; + + match request.url() { + "/projects/fishbowl" => { + let image = form.get("image")?; + let bytes = self.fishbowl_endpoint(&image).await; + return Some(Response::from_data(bytes).with_header(header("Content-Type", "image/gif"))); + } + _ => { + log::warn!("Unhandled POST at {}", request.url()); + return None; + } + } + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..6724b7b --- /dev/null +++ b/src/util.rs @@ -0,0 +1,22 @@ +use std::path::Path; + +use tiny_http::Header; + +pub type Response = tiny_http::Response>>; +pub fn mime_from_file_ext(url: &str) -> &'static str { + let as_path = Path::new(url); + match as_path.extension().unwrap_or_default().as_encoded_bytes() { + b"css" => "text/css", + b"gif" => "image/gif", + b"html" | b"htm" => "text/html", + b"js" | b"mjs" => "text/javascript", + b"svg" => "image/svg+xml", + b"woff2" => "font/woff2", + b"wasm" => "application/wasm", + _ => "application/octet-stream", + } +} + +pub fn header(key: &str, value: &str) -> Header { + Header::from_bytes(key.as_bytes(), value.as_bytes()).unwrap() +} diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..e5c1cef --- /dev/null +++ b/start.sh @@ -0,0 +1,5 @@ +#!/bin/bash +cargo build --release +watchexec -e html,css,js -w hyper-src -w templates -w styles ./html & +./target/release/server + diff --git a/styles/extend.css b/styles/extend.css new file mode 100644 index 0000000..86f5a57 --- /dev/null +++ b/styles/extend.css @@ -0,0 +1,309 @@ +@font-face { + font-family: Cascadia; + src: url("/resources/CascadiaMonoPL.woff2"); +} + +body { + color: #444; + font-size: 1.2em; +} + +.link { + text-decoration: none; + color: #1e90ff; +} + +.link :visited { + color: #1e90ff; +} + +table { + display: block; + margin-top: 1em; + margin-bottom: 1em; + margin-left: auto; + margin-right: auto; + width: 60%; + + text-align: left; + border-collapse: collapse; +} + +th { + padding: 0.25em 0.25em 0.25em 0.25em; + border: 2px solid; + border-collapse: collapse; +} + +td { + padding: 0.25em 0.25em 0.25em 0.25em; + border: 2px solid; + border-collapse: collapse; +} + +/* https://css-loaders.com/spinner/ */ +/* HTML:
*/ +.loading { + width: 512px; + padding: 8px; + aspect-ratio: 1; + border-radius: 50%; + background: #25b09b; + --_m: + conic-gradient(#0000 10%,#000), + linear-gradient(#000 0 0) content-box; + -webkit-mask: var(--_m); + mask: var(--_m); + -webkit-mask-composite: source-out; + mask-composite: subtract; + animation: l3 1s infinite linear; +} +@keyframes l3 {to{transform: rotate(1turn)}} + + +.centered { + display: block; + margin-top: 1em; + margin-bottom: 1em; + margin-left: auto; + margin-right: auto; + width: 50%; +} + +.pure-img-responsive { max-width: 100%; height: auto; +} + +.cascadia { + font-family: Cascadia; +} + +.home-menu { + padding: 0.5em; + text-align: center; + box-shadow: 0 1px 1px rgba(0,0,0, 0.10); +} +.home-menu { + background: #333; +} + +.home-menu a { + color: #ccc; +} + +.home-menu li a:hover, +.home-menu li a:focus { + animation: menu-hover-anim 0.25s ease-out; + animation-fill-mode: forwards; + background: none; + border: none; +} + + +.link { + color: #00f; + text-decoration: none; +} + + +.video { + text-align: center; +} + +.video iframe { + width: 32em; + height: 18em; + frameborder: "0"; +} + +/* +Add transition to containers so they can push in and out. +*/ +#layout, +#menu, +.menu-link { + -webkit-transition: all 0.2s ease-out; + -moz-transition: all 0.2s ease-out; + -ms-transition: all 0.2s ease-out; + -o-transition: all 0.2s ease-out; + transition: all 0.2s ease-out; +} + +/* +This is the parent `
` that contains the menu and the content area. +*/ +#layout { + position: relative; + left: 0; + padding-left: 0; +} + #layout.active #menu { + left: 150px; + width: 150px; + } + + #layout.active .menu-link { + left: 150px; + } +/* +The content `
` is where all your content goes. +*/ +.content { + margin: 0 auto; + padding: 0 2em; + max-width: 800px; + margin-bottom: 50px; + line-height: 1.6em; + text-align: justify; +} + +.header { + margin: 0; + color: #444; + text-align: center; + padding: 2.5em 4em 0; + border-bottom: 1px solid #eee; + } + .header h1 { + margin: 0.2em 0; + font-size: 2.5em; + font-weight: bold; + } + .header h2 { + font-weight: 300; + color: #888; + padding: 0; + margin-top: 0; + } + +.distinct { + background-color: #444; + color: #fff; + padding: 0.5em 0.5em 0.5em 0.5em; +} + +@keyframes card-hover-anim { + to {background-color: #ccc;} +} +@keyframes card-text-hover-anim { + to {color: #222;} +} + +.spaced { + padding: 0.5em 0.5em 0.5em 0.5em; + margin: 1em 0em 1em 0em; +} + +h1 a svg { + vertical-align: middle; +} + +.card { + background-color: #eee; + text-decoration: none; + text-align: left; +} + + .card h1 { + color: #444; + } + .card h2 { + color: #777; + } + .card h3 { + color: #999; + } + .card a { + text-decoration: none; + display: block; + } + + .card:hover { + animation: card-hover-anim 0.25s ease-out; + animation-fill-mode: forwards; + } + + + /* .card:hover h3 { */ + /* animation: card-text-hover-anim 0.25s ease-out; */ + /* animation-fill-mode: forwards; */ + /* } */ + + .card img { + vertical-align: bottom; + float: right; + padding: 0em 0.1em 0em 0.1em; + width: 60px; + } + .card svg { + vertical-align: bottom; + float: right; + padding: 0em 0.1em 0em 0.1em; + width: 3em; + height: auto; + } + + +.content-subhead { + margin: 50px 0 20px 0; + font-weight: 300; + color: #444; +} + +code { + font-family: Cascadia; +} + +.code-block { + /* background-color: #fdf6e3; */ + background-color: #eee; + color: #002b36; + font-family: Cascadia; + font-size: 0.85em; + padding: 0em 0.5em 0em 0.5em; + margin: 1em 0em 1em 0em; + page-break-inside: avoid; + display: block; + overflow: auto; + word-wrap: break-word; + max-width: 100%; +} +.code-block pre { + display: block; + margin: 0 0 0 0; + padding: 0 0 0 0; +} + +.code-block pre code { + font-family: Cascadia; + display: block; + white-space: pre-wrap; +} +.code-block span { + /* background-color: #fdf6e3; */ + background-color: #eee; +} + .keyword { + color: #6c71c4; + } + .constant { + color: #cb4b16; + } + .function { + color: #268bd2; + } + .operator { + color: #6c71c4; + } + .punctuation { + color: #586e75; + } + .string { + color: #859900; + } + .type { + color: #2aa198; + } + .property { + color: #586e75; + } + diff --git a/styles/main.css b/styles/main.css new file mode 100644 index 0000000..acdc431 --- /dev/null +++ b/styles/main.css @@ -0,0 +1,11 @@ +/*! +Pure v3.0.0 +Copyright 2013 Yahoo! +Licensed under the BSD License. +https://github.com/pure-css/pure/blob/master/LICENSE +*/ +/*! +normalize.css v | MIT License | https://necolas.github.io/normalize.css/ +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{display:flex;flex-flow:row wrap;align-content:flex-start}.pure-u{display:inline-block;vertical-align:top}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;user-select:none;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-prefocus,.pure-button-group{word-spacing:-0.43em}.pure-button-group .pure-button{letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:rgba(0,0,0,.8);border:none transparent;background-color:#e6e6e6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:focus,.pure-button:hover{background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000}.pure-button-disabled,.pure-button-disabled:active,.pure-button-disabled:focus,.pure-button-disabled:hover,.pure-button[disabled]{border:none;background-image:none;opacity:.4;cursor:not-allowed;box-shadow:none;pointer-events:none}.pure-button-hidden{display:none}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-button-group .pure-button{margin:0;border-radius:0;border-right:1px solid rgba(0,0,0,.2)}.pure-button-group .pure-button:first-child{border-top-left-radius:2px;border-bottom-left-radius:2px}.pure-button-group .pure-button:last-child{border-top-right-radius:2px;border-bottom-right-radius:2px;border-right:none}.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=color]:focus,.pure-form input[type=date]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=email]:focus,.pure-form input[type=month]:focus,.pure-form input[type=number]:focus,.pure-form input[type=password]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=text]:focus,.pure-form input[type=time]:focus,.pure-form input[type=url]:focus,.pure-form input[type=week]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129fea}.pure-form input:not([type]):focus{outline:0;border-color:#129fea}.pure-form input[type=checkbox]:focus,.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=color][disabled],.pure-form input[type=date][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=email][disabled],.pure-form input[type=month][disabled],.pure-form input[type=number][disabled],.pure-form input[type=password][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=text][disabled],.pure-form input[type=time][disabled],.pure-form input[type=url][disabled],.pure-form input[type=week][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form select:focus:invalid,.pure-form textarea:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=checkbox]:focus:invalid:focus,.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=color],.pure-form-stacked input[type=date],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=email],.pure-form-stacked input[type=file],.pure-form-stacked input[type=month],.pure-form-stacked input[type=number],.pure-form-stacked input[type=password],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=text],.pure-form-stacked input[type=time],.pure-form-stacked input[type=url],.pure-form-stacked input[type=week],.pure-form-stacked label,.pure-form-stacked select,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned select,.pure-form-aligned textarea,.pure-form-message-inline{display:inline-block;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form .pure-input-rounded,.pure-form input.pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-3-4{width:75%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=color],.pure-form input[type=date],.pure-form input[type=datetime-local],.pure-form input[type=datetime],.pure-form input[type=email],.pure-form input[type=month],.pure-form input[type=number],.pure-form input[type=password],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=text],.pure-form input[type=time],.pure-form input[type=url],.pure-form input[type=week],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=color],.pure-group input[type=date],.pure-group input[type=datetime-local],.pure-group input[type=datetime],.pure-group input[type=email],.pure-group input[type=month],.pure-group input[type=number],.pure-group input[type=password],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=text],.pure-group input[type=time],.pure-group input[type=url],.pure-group input[type=week]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0 0}.pure-form-message,.pure-form-message-inline{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;padding:.5em 0}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent;cursor:default}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected>.pure-menu-link,.pure-menu-selected>.pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} \ No newline at end of file diff --git a/temp/pid b/temp/pid new file mode 100644 index 0000000..3fbee24 --- /dev/null +++ b/temp/pid @@ -0,0 +1 @@ +7429 \ No newline at end of file diff --git a/templates/blueprints.html b/templates/blueprints.html new file mode 100644 index 0000000..cb34e9f --- /dev/null +++ b/templates/blueprints.html @@ -0,0 +1,249 @@ + + + + + + + + <loganGatlin/> + + <@style rel="stylesheet" href="styles/main.css" /> + <@style rel="stylesheet" href="styles/extend.css" /> + + + +
+
+ <@children /> +
+
+ + + +
+ + + + + + + + +
+

Page does not exist

+

Error 404

+
+ +
+ + + +
+

This page is under construction

+

Check back later

+ + +
+
+
+ + + + + + + + <@children /> + + + + + + + + + + + @alt + + + + + Rust + + + + + + + + Web Assembly + + + + + + + + Python + + + + + + + + Godot + + + + + + + + + + + + Java + + + + + + + + + + + + + + + + + HTML5 + + + + + + + + + + + + Cloudflare + + + + + + + + + + + WebGPU + + + + + + + + + + + + + + + + + + + + + + gpu + + + + + + + + + + + + + + + + + + View source on GitHub + + + + + + + + +