on master help sign in
Add jammer desk packing CLI with Nix flake for building and running it
commit5f45ec589f7a9f168783a8d409101cae067839bb authoriko <ilyakooo0@gmail.com> dateTue 20 Oct 2026, 01:11 (4 days ago)
53 files changed+8236
flake.lock+61 flake.nix+49 jammer/.gitignore+1 jammer/Cargo.lock+284 jammer/Cargo.toml+16 jammer/README.md+126 jammer/src/aura.rs+204 jammer/src/deskpack.rs+174 jammer/src/jam.rs+351 jammer/src/lib.rs+13 jammer/src/main.rs+525 jammer/src/marks.rs+752 jammer/src/mug.rs+126 jammer/src/noun.rs+246 jammer/src/output.rs+228 jammer/src/pack.rs+379 jammer/src/text.rs+47 jammer/src/treap.rs+190 jammer/tests/aura.rs+78 jammer/tests/cli.rs+906 jammer/tests/core_vectors.rs+286 jammer/tests/deskpack.rs+72 jammer/tests/fixtures/jfix.deskjam jammer/tests/fixtures/jfix/app/hello.hoon+2 jammer/tests/fixtures/jfix/data.json+1 jammer/tests/fixtures/jfix/desk.bill+3 jammer/tests/fixtures/jfix/desk.docket-0+11 jammer/tests/fixtures/jfix/desk.ship+1 jammer/tests/fixtures/jfix/lib/docket.hoon+223 jammer/tests/fixtures/jfix/mar/bill.hoon+34 jammer/tests/fixtures/jfix/mar/css.hoon+25 jammer/tests/fixtures/jfix/mar/docket-0.hoon+25 jammer/tests/fixtures/jfix/mar/hoon.hoon+303 jammer/tests/fixtures/jfix/mar/html.hoon+22 jammer/tests/fixtures/jfix/mar/js.hoon+25 jammer/tests/fixtures/jfix/mar/json.hoon+26 jammer/tests/fixtures/jfix/mar/kelvin.hoon+28 jammer/tests/fixtures/jfix/mar/mime.hoon+32 jammer/tests/fixtures/jfix/mar/noun.hoon+22 jammer/tests/fixtures/jfix/mar/ship.hoon+20 jammer/tests/fixtures/jfix/mar/txt-diff.hoon+16 jammer/tests/fixtures/jfix/mar/txt.hoon+274 jammer/tests/fixtures/jfix/notes.txt+4 jammer/tests/fixtures/jfix/sur/docket.hoon+82 jammer/tests/fixtures/jfix/sys.kelvin+2 jammer/tests/fixtures/jfix/web/app.js+1 jammer/tests/fixtures/jfix/web/index.html+2 jammer/tests/fixtures/jfix/web/style.css+1 jammer/tests/marks.rs+148 jammer/tests/vectors.rs+115 jammer/tests/vectors/auras.txt+1305 jammer/tests/vectors/dockets.txt+136 jammer/tests/vectors/marks.tsv+233
flake.locknew file+61
@@ -0,0 +1,61 @@
1+{
2+ "nodes": {
3+ "flake-utils": {
4+ "inputs": {
5+ "systems": "systems"
6+ },
7+ "locked": {
8+ "lastModified": 1791914995,
9+ "narHash": "sha256-Zf/xfQEAasnwnmEfHqW/4hjDE/s44yn2hD4RuVIZRjA=",
10+ "owner": "numtide",
11+ "repo": "flake-utils",
12+ "rev": "b7a79d3fea1e214f6d668d521cecec0c01e26544",
13+ "type": "github"
14+ },
15+ "original": {
16+ "owner": "numtide",
17+ "repo": "flake-utils",
18+ "type": "github"
19+ }
20+ },
21+ "nixpkgs": {
22+ "locked": {
23+ "lastModified": 1792208399,
24+ "narHash": "sha256-mN15cwddbOOIzzXSSwYUeRfOYeizz47Vy6y3Hzq6DuY=",
25+ "owner": "NixOS",
26+ "repo": "nixpkgs",
27+ "rev": "7a6522c868711d06e3a520bbcdfafe9806f4337f",
28+ "type": "github"
29+ },
30+ "original": {
31+ "owner": "NixOS",
32+ "ref": "nixpkgs-unstable",
33+ "repo": "nixpkgs",
34+ "type": "github"
35+ }
36+ },
37+ "root": {
38+ "inputs": {
39+ "flake-utils": "flake-utils",
40+ "nixpkgs": "nixpkgs"
41+ }
42+ },
43+ "systems": {
44+ "locked": {
45+ "lastModified": 1681028828,
46+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
47+ "owner": "nix-systems",
48+ "repo": "default",
49+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
50+ "type": "github"
51+ },
52+ "original": {
53+ "owner": "nix-systems",
54+ "repo": "default",
55+ "type": "github"
56+ }
57+ }
58+ },
59+ "root": "root",
60+ "version": 7
61+}
flake.nixnew file+49
@@ -0,0 +1,49 @@
1+{
2+ description = "Urbit apps";
3+
4+ inputs = {
5+ nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
6+ flake-utils.url = "github:numtide/flake-utils";
7+ };
8+
9+ outputs =
10+ { nixpkgs, flake-utils, ... }:
11+ flake-utils.lib.eachDefaultSystem (
12+ system:
13+ let
14+ pkgs = nixpkgs.legacyPackages.${system};
15+ cargoToml = builtins.fromTOML (builtins.readFile ./jammer/Cargo.toml);
16+
17+ jammer = pkgs.rustPlatform.buildRustPackage {
18+ pname = cargoToml.package.name;
19+ version = cargoToml.package.version;
20+ src = pkgs.lib.cleanSourceWith {
21+ src = ./jammer;
22+ filter = path: type: !(type == "directory" && baseNameOf path == "target");
23+ };
24+ cargoLock.lockFile = ./jammer/Cargo.lock;
25+ meta = {
26+ description = cargoToml.package.description;
27+ mainProgram = "jammer";
28+ };
29+ };
30+ in
31+ {
32+ packages = {
33+ inherit jammer;
34+ default = jammer;
35+ };
36+
37+ apps.default = flake-utils.lib.mkApp { drv = jammer; };
38+
39+ devShells.default = pkgs.mkShell {
40+ inputsFrom = [ jammer ];
41+ packages = with pkgs; [
42+ rust-analyzer
43+ clippy
44+ rustfmt
45+ ];
46+ };
47+ }
48+ );
49+}
jammer/.gitignorenew file+1
@@ -0,0 +1 @@
1+/target
jammer/Cargo.locknew file+284
@@ -0,0 +1,284 @@
1+# This file is automatically @generated by Cargo.
2+# It is not intended for manual editing.
3+version = 4
4+
5+[[package]]
6+name = "anstream"
7+version = "1.0.0"
8+source = "registry+https://github.com/rust-lang/crates.io-index"
9+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
10+dependencies = [
11+ "anstyle",
12+ "anstyle-parse",
13+ "anstyle-query",
14+ "anstyle-wincon",
15+ "colorchoice",
16+ "is_terminal_polyfill",
17+ "utf8parse",
18+]
19+
20+[[package]]
21+name = "anstyle"
22+version = "1.0.14"
23+source = "registry+https://github.com/rust-lang/crates.io-index"
24+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
25+
26+[[package]]
27+name = "anstyle-parse"
28+version = "1.0.0"
29+source = "registry+https://github.com/rust-lang/crates.io-index"
30+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
31+dependencies = [
32+ "utf8parse",
33+]
34+
35+[[package]]
36+name = "anstyle-query"
37+version = "1.1.5"
38+source = "registry+https://github.com/rust-lang/crates.io-index"
39+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
40+dependencies = [
41+ "windows-sys",
42+]
43+
44+[[package]]
45+name = "anstyle-wincon"
46+version = "3.0.11"
47+source = "registry+https://github.com/rust-lang/crates.io-index"
48+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
49+dependencies = [
50+ "anstyle",
51+ "once_cell_polyfill",
52+ "windows-sys",
53+]
54+
55+[[package]]
56+name = "anyhow"
57+version = "1.0.104"
58+source = "registry+https://github.com/rust-lang/crates.io-index"
59+checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
60+
61+[[package]]
62+name = "bitflags"
63+version = "2.13.2"
64+source = "registry+https://github.com/rust-lang/crates.io-index"
65+checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
66+
67+[[package]]
68+name = "cfg-if"
69+version = "1.0.5"
70+source = "registry+https://github.com/rust-lang/crates.io-index"
71+checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
72+
73+[[package]]
74+name = "clap"
75+version = "4.6.7"
76+source = "registry+https://github.com/rust-lang/crates.io-index"
77+checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946"
78+dependencies = [
79+ "clap_builder",
80+ "clap_derive",
81+]
82+
83+[[package]]
84+name = "clap_builder"
85+version = "4.6.7"
86+source = "registry+https://github.com/rust-lang/crates.io-index"
87+checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d"
88+dependencies = [
89+ "anstream",
90+ "anstyle",
91+ "clap_lex",
92+ "strsim",
93+]
94+
95+[[package]]
96+name = "clap_derive"
97+version = "4.6.7"
98+source = "registry+https://github.com/rust-lang/crates.io-index"
99+checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0"
100+dependencies = [
101+ "heck",
102+ "proc-macro2",
103+ "quote",
104+ "syn",
105+]
106+
107+[[package]]
108+name = "clap_lex"
109+version = "1.1.1"
110+source = "registry+https://github.com/rust-lang/crates.io-index"
111+checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486"
112+
113+[[package]]
114+name = "colorchoice"
115+version = "1.0.5"
116+source = "registry+https://github.com/rust-lang/crates.io-index"
117+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
118+
119+[[package]]
120+name = "errno"
121+version = "0.3.14"
122+source = "registry+https://github.com/rust-lang/crates.io-index"
123+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
124+dependencies = [
125+ "libc",
126+ "windows-sys",
127+]
128+
129+[[package]]
130+name = "fastrand"
131+version = "2.5.0"
132+source = "registry+https://github.com/rust-lang/crates.io-index"
133+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
134+
135+[[package]]
136+name = "getrandom"
137+version = "0.4.3"
138+source = "registry+https://github.com/rust-lang/crates.io-index"
139+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
140+dependencies = [
141+ "cfg-if",
142+ "libc",
143+ "r-efi",
144+]
145+
146+[[package]]
147+name = "heck"
148+version = "0.5.0"
149+source = "registry+https://github.com/rust-lang/crates.io-index"
150+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
151+
152+[[package]]
153+name = "is_terminal_polyfill"
154+version = "1.70.2"
155+source = "registry+https://github.com/rust-lang/crates.io-index"
156+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
157+
158+[[package]]
159+name = "jammer"
160+version = "0.1.0"
161+dependencies = [
162+ "anyhow",
163+ "clap",
164+ "rustix",
165+ "tempfile",
166+]
167+
168+[[package]]
169+name = "libc"
170+version = "0.2.191"
171+source = "registry+https://github.com/rust-lang/crates.io-index"
172+checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
173+
174+[[package]]
175+name = "linux-raw-sys"
176+version = "0.12.1"
177+source = "registry+https://github.com/rust-lang/crates.io-index"
178+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
179+
180+[[package]]
181+name = "once_cell"
182+version = "1.21.4"
183+source = "registry+https://github.com/rust-lang/crates.io-index"
184+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
185+
186+[[package]]
187+name = "once_cell_polyfill"
188+version = "1.70.2"
189+source = "registry+https://github.com/rust-lang/crates.io-index"
190+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
191+
192+[[package]]
193+name = "proc-macro2"
194+version = "1.0.107"
195+source = "registry+https://github.com/rust-lang/crates.io-index"
196+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
197+dependencies = [
198+ "unicode-ident",
199+]
200+
201+[[package]]
202+name = "quote"
203+version = "1.0.47"
204+source = "registry+https://github.com/rust-lang/crates.io-index"
205+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
206+dependencies = [
207+ "proc-macro2",
208+]
209+
210+[[package]]
211+name = "r-efi"
212+version = "6.0.0"
213+source = "registry+https://github.com/rust-lang/crates.io-index"
214+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
215+
216+[[package]]
217+name = "rustix"
218+version = "1.1.5"
219+source = "registry+https://github.com/rust-lang/crates.io-index"
220+checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d"
221+dependencies = [
222+ "bitflags",
223+ "errno",
224+ "libc",
225+ "linux-raw-sys",
226+ "windows-sys",
227+]
228+
229+[[package]]
230+name = "strsim"
231+version = "0.11.1"
232+source = "registry+https://github.com/rust-lang/crates.io-index"
233+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
234+
235+[[package]]
236+name = "syn"
237+version = "3.0.9"
238+source = "registry+https://github.com/rust-lang/crates.io-index"
239+checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
240+dependencies = [
241+ "proc-macro2",
242+ "quote",
243+ "unicode-ident",
244+]
245+
246+[[package]]
247+name = "tempfile"
248+version = "3.27.0"
249+source = "registry+https://github.com/rust-lang/crates.io-index"
250+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
251+dependencies = [
252+ "fastrand",
253+ "getrandom",
254+ "once_cell",
255+ "rustix",
256+ "windows-sys",
257+]
258+
259+[[package]]
260+name = "unicode-ident"
261+version = "1.0.27"
262+source = "registry+https://github.com/rust-lang/crates.io-index"
263+checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
264+
265+[[package]]
266+name = "utf8parse"
267+version = "0.2.2"
268+source = "registry+https://github.com/rust-lang/crates.io-index"
269+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
270+
271+[[package]]
272+name = "windows-link"
273+version = "0.2.1"
274+source = "registry+https://github.com/rust-lang/crates.io-index"
275+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
276+
277+[[package]]
278+name = "windows-sys"
279+version = "0.61.2"
280+source = "registry+https://github.com/rust-lang/crates.io-index"
281+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
282+dependencies = [
283+ "windows-link",
284+]
jammer/Cargo.tomlnew file+16
@@ -0,0 +1,16 @@
1+[package]
2+name = "jammer"
3+version = "0.1.0"
4+edition = "2024"
5+rust-version = "1.88"
6+description = "Pack desk directories into .deskjam files for the deskjam Urbit app, and unpack them"
7+
8+[dependencies]
9+anyhow = "1.0.104"
10+clap = { version = "4.6.7", features = ["derive"] }
11+
12+[dev-dependencies]
13+tempfile = "3.27.0"
14+
15+[target."cfg(unix)".dependencies]
16+rustix = { version = "1", features = ["fs"] }
jammer/README.mdnew file+126
@@ -0,0 +1,126 @@
1+# jammer
2+
3+Pack a desk directory into a `.deskjam` file that you can upload to
4+[deskjam](https://github.com/tinnus-napbus/deskjam), and unpack `.deskjam`
5+files back into directories, without a ship.
6+
7+```console
8+$ jammer jam ./desk -d my-app
9+packed 42 files (100.7 KiB) from ./desk as %my-app into my-app.deskjam (101.8 KiB)
10+
11+$ jammer list my-app.deskjam
12+desk %my-app (42 files)
13+ 6940 %mime /app/my-app/hoon
14+ 16 %mime /desk/bill
15+...
16+
17+$ jammer unjam garden.deskjam # writes ./garden/
18+unpacked 166 of 166 files (421.6 KiB) of %garden into garden
19+```
20+
21+## Install
22+
23+```console
24+cargo install --path .
25+```
26+
27+## Commands
28+
29+- `jammer jam <DIR> [-o FILE] [-d DESK] [-f] [-v]` packs a desk directory,
30+ such as a mounted desk or a repository's `desk/` folder.
31+ - The desk name defaults to the directory's name, as you wrote it.
32+ - The output defaults to `./<desk>.deskjam`. Use `-o -` to write to stdout.
33+ - It will not leave its output inside the desk directory, because a later
34+ `|commit` of a mounted desk would trip over it.
35+ - It replaces an existing file only if that is a `.deskjam`, or with
36+ `--force`.
37+- `jammer unjam <FILE> [-o DIR] [--force]` unpacks into `./<desk>` by
38+ default. It refuses to write into a non-empty directory unless you pass
39+ `--force`. Use `-` as the file to read from stdin.
40+- `jammer list <FILE>` (or `ls`) prints the desk name and each file's size,
41+ mark and path.
42+
43+## What you get
44+
45+**`jam` builds the same desk `|commit` would.**
46+
47+- Files are picked by the same rules vere uses when you `|commit` a mounted
48+ desk:
49+ - Hidden files and directories are skipped.
50+ - A file needs an extension, must not end in `~`, and its name may only
51+ contain `a-z 0-9 - . _ ~`.
52+ - Symlinks are followed.
53+ - `dir/name.ext` becomes `/dir/name/ext`.
54+ - A leading `!` is dropped from each name, undoing the escape a mount uses
55+ for segments like `..`. If two names map to the same path, the one a
56+ mount would write wins.
57+- Every file is stored as a `%mime` page, just as vere sends them on
58+ `|commit`. When deskjam commits the desk, clay converts each page to the
59+ mark named by its extension, using the marks in the target desk.
60+- A new desk therefore needs `sys.kelvin` and a `mar/` file for every
61+ extension other than `.hoon`. jammer tells you when either is missing.
62+ Merging into an existing desk only needs that desk to have the marks.
63+
64+**`unjam` writes what a desk mount writes.**
65+
66+- Desks exported by deskjam contain clay's native pages. jammer renders them
67+ the way each mark's `grow:mime` arm does for `|mount`:
68+ - `hoon`, `js`, `css`, `html`, `svg`, `png`, `jam` and other atom marks:
69+ the raw bytes.
70+ - `txt`, `md` and `pem`: the lines joined.
71+ - `json`: printed exactly like `en:json:html`, including its key order.
72+ - `kelvin`, `bill`, `docket-0`, `ship` and `story`: the same text the
73+ marks print.
74+ - `wasm` and `woff2`: the bytes of the `octs`.
75+ - `noun`, `pill` and similar: the jam of the noun.
76+ - `tape`: the characters.
77+ - `mime`: the raw file contents.
78+- Marks jammer cannot render are reported and skipped. These are XML
79+ (`hymn`, `urb`), `eth-txs`, and marks a mount cannot write either.
80+- Marks are rendered by name, as `%base` and landscape define them. If a
81+ desk redefines one of these marks in its own `mar/`, its mount may differ.
82+ Pages with marks jammer has never heard of are written as the raw bytes of
83+ their atom, which suits most text and binary marks, and jammer says which
84+ marks it guessed.
85+- Untrusted input is handled safely:
86+ - Segments like `..` are written as `!..`, as a mount does, so paths never
87+ escape the output directory.
88+ - Nothing is written through symlinks, and on Unix every step of a path
89+ is taken relative to an already-verified directory, so this cannot be
90+ raced.
91+ - Backreferences let a tiny jam describe enormous files, so each file is
92+ capped at 1 GiB. The whole unpack is capped at the equivalent of 4 GiB,
93+ counting the bytes produced and the nouns walked to produce them. This
94+ is checked before anything is written, and at most one file is held in
95+ memory at a time.
96+
97+## Exit status
98+
99+- `0`: everything was packed or unpacked.
100+- `1`: an error stopped the command.
101+- `3`: it finished, but left some files out. jam does this for files it
102+ could not read; unjam for files it could not render or write. The skipped
103+ files are listed on stderr.
104+
105+## How it was checked
106+
107+- **Serialization:** jam, cue, `mug` and Hoon map construction are
108+ byte-identical to a ship's, checked with vectors from `urbit eval`.
109+- **Real files:** deskjam files exported by real ships re-encode to the
110+ identical bytes.
111+- **On a fake ship running deskjam:**
112+ - Directories packed by jammer and uploaded through deskjam's web UI
113+ produced desks identical to the originals, file for file. The new desks'
114+ clay pages were also identical to those a `|commit` of the same
115+ directory produced.
116+ - `unjam` output for `%base`, `%groups`, `%landscape`, `%webterm` and
117+ `%deskjam` matched the ship's own `|mount` of those desks byte for byte.
118+- **Regression tests:**
119+ - `tests/fixtures/` holds such an export together with the ship's mount of
120+ it.
121+ - `tests/vectors/` holds about 1,500 outputs of the real marks and of
122+ `scow`, generated with `urbit eval`.
123+
124+```console
125+cargo test
126+```
jammer/src/aura.rsnew file+204
@@ -0,0 +1,204 @@
1+//! Hoon's text renderings of atoms (`scow`), as used by marks that print
2+//! nouns into files: `@ud`, `@ux`, `@uv` and `@p`.
3+
4+use crate::mug::murmur3_32;
5+use crate::noun::trim;
6+
7+/// `(scow %ud n)`: decimal, with a `.` every three digits (`1.024`).
8+pub fn ud(atom: &[u8]) -> String {
9+ // Repeatedly divide a base-2^32 number by 10^9.
10+ let atom = trim(atom);
11+ let mut limbs: Vec<u32> = atom
12+ .chunks(4)
13+ .map(|c| {
14+ let mut buf = [0u8; 4];
15+ buf[..c.len()].copy_from_slice(c);
16+ u32::from_le_bytes(buf)
17+ })
18+ .collect();
19+ let mut chunks = Vec::new();
20+ while limbs.iter().any(|&l| l != 0) {
21+ let mut rem = 0u64;
22+ for limb in limbs.iter_mut().rev() {
23+ let cur = (rem << 32) | u64::from(*limb);
24+ *limb = (cur / 1_000_000_000) as u32;
25+ rem = cur % 1_000_000_000;
26+ }
27+ chunks.push(rem as u32);
28+ while limbs.last() == Some(&0) {
29+ limbs.pop();
30+ }
31+ }
32+ let mut digits = match chunks.pop() {
33+ None => return "0".into(),
34+ Some(top) => top.to_string(),
35+ };
36+ for chunk in chunks.iter().rev() {
37+ digits.push_str(&format!("{chunk:09}"));
38+ }
39+ group(&digits, 3)
40+}
41+
42+/// `(scow %ux n)`: `0x` and hexadecimal, with a `.` every four digits.
43+pub fn ux(atom: &[u8]) -> String {
44+ let atom = trim(atom);
45+ let mut digits: String = atom.iter().rev().map(|b| format!("{b:02x}")).collect();
46+ let significant = digits.trim_start_matches('0').len();
47+ digits.drain(..digits.len() - significant);
48+ if digits.is_empty() {
49+ digits.push('0');
50+ }
51+ format!("0x{}", group(&digits, 4))
52+}
53+
54+/// `(scow %uv n)`: `0v` and base-32 (`0-9a-v`), with a `.` every five digits.
55+pub fn uv(atom: &[u8]) -> String {
56+ const DIGITS: &[u8; 32] = b"0123456789abcdefghijklmnopqrstuv";
57+ let atom = trim(atom);
58+ let bits = atom.len() * 8;
59+ let mut digits = Vec::new();
60+ let mut i = 0;
61+ while i < bits {
62+ let mut v = 0u8;
63+ for k in 0..5 {
64+ let bit = i + k;
65+ if bit < bits && (atom[bit / 8] >> (bit % 8)) & 1 == 1 {
66+ v |= 1 << k;
67+ }
68+ }
69+ digits.push(DIGITS[v as usize]);
70+ i += 5;
71+ }
72+ while digits.len() > 1 && digits.last() == Some(&b'0') {
73+ digits.pop();
74+ }
75+ if digits.is_empty() {
76+ digits.push(b'0');
77+ }
78+ digits.reverse();
79+ format!("0v{}", group(std::str::from_utf8(&digits).unwrap(), 5))
80+}
81+
82+/// Insert a `.` every `width` digits, counting from the right.
83+fn group(digits: &str, width: usize) -> String {
84+ let mut out = String::with_capacity(digits.len() + digits.len() / width);
85+ for (i, c) in digits.chars().enumerate() {
86+ if i > 0 && (digits.len() - i).is_multiple_of(width) {
87+ out.push('.');
88+ }
89+ out.push(c);
90+ }
91+ out
92+}
93+
94+const PREFIXES: &str = "dozmarbinwansamlitsighidfidlissogdirwacsabwissib\
95+rigsoldopmodfoglidhopdardorlorhodfolrintogsilmir\
96+holpaslacrovlivdalsatlibtabhanticpidtorbolfosdot\
97+losdilforpilramtirwintadbicdifrocwidbisdasmidlop\
98+rilnardapmolsanlocnovsitnidtipsicropwitnatpanmin\
99+ritpodmottamtolsavposnapnopsomfinfonbanmorworsip\
100+ronnorbotwicsocwatdolmagpicdavbidbaltimtasmallig\
101+sivtagpadsaldivdactansidfabtarmonranniswolmispal\
102+lasdismaprabtobrollatlonnodnavfignomnibpagsopral\
103+bilhaddocridmocpacravripfaltodtiltinhapmicfanpat\
104+taclabmogsimsonpinlomrictapfirhasbosbatpochactid\
105+havsaplindibhosdabbitbarracparloddosbortochilmac\
106+tomdigfilfasmithobharmighinradmashalraglagfadtop\
107+mophabnilnosmilfopfamdatnoldinhatnacrisfotribhoc\
108+nimlarfitwalrapsarnalmoslandondanladdovrivbacpol\
109+laptalpitnambonrostonfodponsovnocsorlavmatmipfip";
110+
111+const SUFFIXES: &str = "zodnecbudwessevpersutletfulpensytdurwepserwylsun\
112+rypsyxdyrnuphebpeglupdepdysputlughecryttyvsydnex\
113+lunmeplutseppesdelsulpedtemledtulmetwenbynhexfeb\
114+pyldulhetmevruttylwydtepbesdexsefwycburderneppur\
115+rysrebdennutsubpetrulsynregtydsupsemwynrecmegnet\
116+secmulnymtevwebsummutnyxrextebfushepbenmuswyxsym\
117+selrucdecwexsyrwetdylmynmesdetbetbeltuxtugmyrpel\
118+syptermebsetdutdegtexsurfeltudnuxruxrenwytnubmed\
119+lytdusnebrumtynseglyxpunresredfunrevrefmectedrus\
120+bexlebduxrynnumpyxrygryxfeptyrtustyclegnemfermer\
121+tenlusnussyltecmexpubrymtucfyllepdebbermughuttun\
122+bylsudpemdevlurdefbusbeprunmelpexdytbyttyplevmyl\
123+wedducfurfexnulluclennerlexrupnedlecrydlydfenwel\
124+nydhusrelrudneshesfetdesretdunlernyrsebhulryllud\
125+remlysfynwerrycsugnysnyllyndyndemluxfedsedbecmun\
126+lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes";
127+
128+fn syllable(table: &str, index: u8) -> &str {
129+ let i = usize::from(index) * 3;
130+ &table[i..i + 3]
131+}
132+
133+/// `(scow %p n)`: a ship name such as `~sampel-palnet`.
134+pub fn p(atom: &[u8]) -> String {
135+ let mut sxz = trim(atom).to_vec();
136+ // fein:ob scrambles atoms of up to 64 bits; comets are left alone.
137+ if sxz.len() <= 8 {
138+ let mut buf = [0u8; 8];
139+ buf[..sxz.len()].copy_from_slice(&sxz);
140+ sxz = fein(u64::from_le_bytes(buf)).to_le_bytes().to_vec();
141+ }
142+ let sxz = trim(&sxz);
143+
144+ if sxz.len() <= 1 {
145+ return format!("~{}", syllable(SUFFIXES, sxz.first().copied().unwrap_or(0)));
146+ }
147+ let words = sxz.len().div_ceil(2);
148+ let mut out = String::from("~");
149+ for imp in (0..words).rev() {
150+ let lo = sxz[2 * imp];
151+ let hi = sxz.get(2 * imp + 1).copied().unwrap_or(0);
152+ out.push_str(syllable(PREFIXES, hi));
153+ out.push_str(syllable(SUFFIXES, lo));
154+ if imp > 0 {
155+ out.push_str(if imp % 4 == 0 { "--" } else { "-" });
156+ }
157+ }
158+ out
159+}
160+
161+/// `fein:ob`: conceal the structure of planet- and moon-sized ship numbers.
162+fn fein(pyn: u64) -> u64 {
163+ if (0x1_0000..=0xffff_ffff).contains(&pyn) {
164+ 0x1_0000 + feis(pyn - 0x1_0000)
165+ } else if pyn >= 0x1_0000_0000 {
166+ (pyn & 0xffff_ffff_0000_0000) | fein(pyn & 0xffff_ffff)
167+ } else {
168+ pyn
169+ }
170+}
171+
172+const RAKU: [u32; 4] = [0xb76d_5eed, 0xee28_1300, 0x85bc_ae01, 0x4b38_7af7];
173+
174+/// `feis:ob`: a four-round Feistel cipher over `[0, 0xffff * 0x10000)`,
175+/// cycle-walked so the result stays in range.
176+fn feis(m: u64) -> u64 {
177+ let c = fe(m);
178+ if c < 0xffff * 0x1_0000 { c } else { fe(c) }
179+}
180+
181+fn fe(m: u64) -> u64 {
182+ const A: u64 = 0xffff;
183+ const B: u64 = 0x1_0000;
184+ const ROUNDS: u64 = 4;
185+ let mut ell = m % A;
186+ let mut arr = m / A;
187+ for j in 1..=ROUNDS {
188+ let key = [(arr & 0xff) as u8, ((arr >> 8) & 0xff) as u8];
189+ let f = u64::from(murmur3_32(&key, RAKU[(j - 1) as usize]));
190+ let tmp = if j % 2 == 1 {
191+ (f + ell) % A
192+ } else {
193+ (f + ell) % B
194+ };
195+ ell = arr;
196+ arr = tmp;
197+ }
198+ // ROUNDS is even.
199+ if arr == A {
200+ arr * A + ell
201+ } else {
202+ ell * A + arr
203+ }
204+}
jammer/src/deskpack.rsnew file+174
@@ -0,0 +1,174 @@
1+//! The `.deskjam` format: the little-endian bytes of
2+//! `(jam [%deskpack-0 desk=@tas (map path page:clay)])`, where a page is
3+//! `[mark noun]`.
4+
5+use std::collections::HashSet;
6+use std::fmt;
7+
8+use crate::jam::{CueError, cue, jam};
9+use crate::noun::{Arena, Noun};
10+use crate::treap::{TreeError, is_valid_treap, map_from_entries, tree_items};
11+
12+/// The `cask` tag deskjam puts in front of the desk.
13+pub const TAG: &str = "deskpack-0";
14+
15+/// A file to pack: its clay path (e.g. `["app", "foo", "hoon"]` for
16+/// `app/foo.hoon`) and its contents.
17+pub struct SourceFile {
18+ pub path: Vec<String>,
19+ pub contents: Vec<u8>,
20+}
21+
22+/// Build a deskjam containing `files`.
23+///
24+/// Every file is stored as a `%mime` page, `[%mime /text/plain [size data]]`,
25+/// which is exactly what vere sends clay when you `|commit` a mounted desk.
26+/// When deskjam commits the pages, clay converts each one to the mark named
27+/// by its path extension, using the marks in the target desk, so the
28+/// resulting desk is identical to one built with `|commit` from the same
29+/// directory.
30+pub fn encode(desk: &str, files: Vec<SourceFile>) -> Vec<u8> {
31+ let mut a = Arena::new();
32+ let mime = a.cord("mime");
33+ let mite = a.path(&["text", "plain"]);
34+ let mut entries = Vec::with_capacity(files.len());
35+ for file in files {
36+ let path = a.path(&file.path);
37+ let size = a.atom_u64(file.contents.len() as u64);
38+ let data = a.atom(&file.contents);
39+ let octs = a.cell(size, data);
40+ let page = a.cell(mite, octs);
41+ let page = a.cell(mime, page);
42+ entries.push((path, page));
43+ }
44+ let map = map_from_entries(&mut a, &entries);
45+ let desk = a.cord(desk);
46+ let tag = a.cord(TAG);
47+ let body = a.cell(desk, map);
48+ let root = a.cell(tag, body);
49+ jam(&a, root)
50+}
51+
52+/// One file of a decoded deskjam.
53+pub struct Page {
54+ /// Path segments as raw atom bytes.
55+ pub path: Vec<Vec<u8>>,
56+ /// The page's mark (raw atom bytes).
57+ pub mark: Vec<u8>,
58+ /// The page's noun, in [`Deskpack::arena`].
59+ pub noun: Noun,
60+}
61+
62+/// A decoded deskjam.
63+pub struct Deskpack {
64+ pub arena: Arena,
65+ pub desk: Vec<u8>,
66+ /// Pages sorted by path.
67+ pub pages: Vec<Page>,
68+ /// Whether the file map satisfies the treap invariants. Deskjam's `;;`
69+ /// rejects files where it does not.
70+ pub canonical_map: bool,
71+ /// Whether set bits follow the jam encoding (a ship ignores them).
72+ pub trailing_garbage: bool,
73+}
74+
75+#[derive(Debug)]
76+pub enum DecodeError {
77+ Cue(CueError),
78+ /// The noun is not `[%deskpack-0 ...]`; carries the tag if printable.
79+ UnknownTag(Option<String>),
80+ Malformed(String),
81+}
82+
83+impl fmt::Display for DecodeError {
84+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85+ match self {
86+ DecodeError::Cue(e) => write!(f, "not a jam file: {e}"),
87+ DecodeError::UnknownTag(Some(tag)) => {
88+ write!(f, "not a deskjam: expected a %{TAG} jam, found %{tag}")
89+ }
90+ DecodeError::UnknownTag(None) => {
91+ write!(f, "not a deskjam: expected a jam of [%{TAG} desk files]")
92+ }
93+ DecodeError::Malformed(why) => write!(f, "malformed deskjam: {why}"),
94+ }
95+ }
96+}
97+
98+impl std::error::Error for DecodeError {}
99+
100+impl From<CueError> for DecodeError {
101+ fn from(e: CueError) -> Self {
102+ DecodeError::Cue(e)
103+ }
104+}
105+
106+fn malformed(why: impl Into<String>) -> DecodeError {
107+ DecodeError::Malformed(why.into())
108+}
109+
110+/// Parse a deskjam file.
111+pub fn decode(bytes: &[u8]) -> Result<Deskpack, DecodeError> {
112+ let mut a = Arena::new();
113+ let cued = cue(&mut a, bytes)?;
114+ let (tag, body) = a.as_cell(cued.noun).ok_or(DecodeError::UnknownTag(None))?;
115+ if !a.is_cord(tag, TAG) {
116+ let tag = a
117+ .atom_bytes(tag)
118+ .and_then(|t| std::str::from_utf8(t).ok())
119+ .filter(|t| t.chars().all(|c| c.is_ascii_graphic()))
120+ .map(str::to_owned);
121+ return Err(DecodeError::UnknownTag(tag));
122+ }
123+ let (desk, map) = a
124+ .as_cell(body)
125+ .ok_or_else(|| malformed("missing desk name"))?;
126+ let desk = a
127+ .atom_bytes(desk)
128+ .ok_or_else(|| malformed("desk name is not an atom"))?
129+ .to_vec();
130+
131+ let items = tree_items(&a, map).map_err(|e| match e {
132+ TreeError::Malformed => malformed("file table is not a map"),
133+ TreeError::SharedSubtree => malformed("file table repeats a path"),
134+ })?;
135+ let mut seen = HashSet::new();
136+ let mut pages = Vec::with_capacity(items.len());
137+ for item in items {
138+ let (path, page) = a
139+ .as_cell(item)
140+ .ok_or_else(|| malformed("file entry is not a pair"))?;
141+ if !seen.insert(path) {
142+ return Err(malformed("file table repeats a path"));
143+ }
144+ let segments = a
145+ .list_items(path)
146+ .ok_or_else(|| malformed("file path is not a list"))?
147+ .into_iter()
148+ .map(|s| a.atom_bytes(s).map(<[u8]>::to_vec))
149+ .collect::<Option<Vec<_>>>()
150+ .ok_or_else(|| malformed("file path has a non-atom segment"))?;
151+ let (mark, noun) = a
152+ .as_cell(page)
153+ .ok_or_else(|| malformed("file page is not [mark noun]"))?;
154+ let mark = a
155+ .atom_bytes(mark)
156+ .ok_or_else(|| malformed("file mark is not an atom"))?
157+ .to_vec();
158+ pages.push(Page {
159+ path: segments,
160+ mark,
161+ noun,
162+ });
163+ }
164+ pages.sort_by(|x, y| x.path.cmp(&y.path));
165+
166+ let canonical_map = is_valid_treap(&a, map, |item| a.as_cell(item).map(|(k, _)| k));
167+ Ok(Deskpack {
168+ arena: a,
169+ desk,
170+ pages,
171+ canonical_map,
172+ trailing_garbage: cued.trailing_garbage,
173+ })
174+}
jammer/src/jam.rsnew file+351
@@ -0,0 +1,351 @@
1+//! `jam` and `cue`: Urbit's bit-level noun serialization.
2+//!
3+//! The encoding is a little-endian bitstream. Every noun starts with a tag:
4+//! `0` atom, `1 0` cell (then head, then tail), `1 1` backreference to the
5+//! bit offset where an equal noun was first encoded. Atoms and backreference
6+//! offsets are length-prefixed with `mat`.
7+//!
8+//! [`jam`] reproduces Hoon's `++jam` exactly (cells are always backreferenced
9+//! when repeated; a repeated atom is re-encoded when it is no longer than the
10+//! offset), so the output is byte-identical to what a ship produces.
11+
12+use std::collections::HashMap;
13+use std::fmt;
14+
15+use crate::noun::{Arena, Noun, View, met0, trim};
16+
17+/// Serialize a noun. The result is the little-endian bytes of the jam atom
18+/// (no trailing zero bytes), i.e. the contents of a `.jam` file.
19+pub fn jam(arena: &Arena, root: Noun) -> Vec<u8> {
20+ jam_counting(arena, root).0
21+}
22+
23+/// [`jam`], also returning how many nouns it visited (the work it did).
24+pub fn jam_counting(arena: &Arena, root: Noun) -> (Vec<u8>, u64) {
25+ let mut out = BitWriter::default();
26+ let mut seen: HashMap<Noun, u64> = HashMap::new();
27+ let mut stack = vec![root];
28+ let mut visited = 0u64;
29+ while let Some(noun) = stack.pop() {
30+ visited += 1;
31+ let pos = out.len;
32+ if let Some(&back) = seen.get(&noun) {
33+ match arena.view(noun) {
34+ View::Atom(bytes) if met0(bytes) <= 64 - u64::from(back.leading_zeros()) => {
35+ out.bit(false);
36+ out.mat(bytes);
37+ }
38+ _ => {
39+ out.bit(true);
40+ out.bit(true);
41+ out.mat(&back.to_le_bytes());
42+ }
43+ }
44+ continue;
45+ }
46+ seen.insert(noun, pos);
47+ match arena.view(noun) {
48+ View::Atom(bytes) => {
49+ out.bit(false);
50+ out.mat(bytes);
51+ }
52+ View::Cell(head, tail) => {
53+ out.bit(true);
54+ out.bit(false);
55+ stack.push(tail);
56+ stack.push(head);
57+ }
58+ }
59+ }
60+ (out.into_bytes(), visited)
61+}
62+
63+/// Why a byte string could not be cued.
64+#[derive(Debug, Clone, PartialEq, Eq)]
65+pub enum CueError {
66+ /// The input ended in the middle of a noun.
67+ Truncated { bit: u64 },
68+ /// An atom length or backreference does not fit in 64 bits.
69+ TooLarge { bit: u64 },
70+ /// A backreference names an offset where no complete noun starts.
71+ BadBackref { bit: u64, target: u64 },
72+}
73+
74+impl fmt::Display for CueError {
75+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76+ match self {
77+ CueError::Truncated { bit } => write!(f, "jam data is truncated (at bit {bit})"),
78+ CueError::TooLarge { bit } => {
79+ write!(
80+ f,
81+ "jam data has an impossibly large length prefix (at bit {bit})"
82+ )
83+ }
84+ CueError::BadBackref { bit, target } => write!(
85+ f,
86+ "jam data has a backreference to bit {target}, where no noun starts (at bit {bit})"
87+ ),
88+ }
89+ }
90+}
91+
92+impl std::error::Error for CueError {}
93+
94+/// Result of [`cue`]: the noun plus how much of the input it occupied.
95+pub struct Cued {
96+ pub noun: Noun,
97+ /// Bits consumed by the encoding.
98+ pub bits_read: u64,
99+ /// Whether any set bits follow the encoding (Hoon ignores them).
100+ pub trailing_garbage: bool,
101+}
102+
103+/// Most nouns [`cue`] will decode. A noun can be encoded in four bits, so
104+/// without a bound a small hostile file could demand gigabytes of memory;
105+/// real deskjams decode to a few thousand nouns per megabyte.
106+pub const MAX_NOUNS: usize = 1 << 24;
107+
108+/// Deserialize a noun from the little-endian bytes of a jam atom.
109+///
110+/// Hostile input is fine: everything is bounds-checked, nothing recurses,
111+/// and memory use is bounded by the input size and [`MAX_NOUNS`]
112+/// (backreferences share structure instead of copying it).
113+pub fn cue(arena: &mut Arena, bytes: &[u8]) -> Result<Cued, CueError> {
114+ cue_with_limit(arena, bytes, MAX_NOUNS)
115+}
116+
117+/// [`cue`], decoding at most `max_nouns` nouns.
118+pub fn cue_with_limit(arena: &mut Arena, bytes: &[u8], max_nouns: usize) -> Result<Cued, CueError> {
119+ enum Frame {
120+ /// Decoding the head of the cell whose memo slot is given.
121+ Head(usize),
122+ Tail(usize, Noun),
123+ }
124+ let mut input = BitReader::new(bytes);
125+ // Every noun decoded so far, by the bit offset where it starts. Nouns
126+ // start in increasing order, so this stays sorted; a cell's slot is
127+ // filled in once both halves are done.
128+ let mut memo: Vec<(u64, Option<Noun>)> = Vec::new();
129+ let mut stack: Vec<Frame> = Vec::new();
130+ loop {
131+ if memo.len() >= max_nouns {
132+ return Err(CueError::TooLarge { bit: input.pos });
133+ }
134+ let pos = input.pos;
135+ let mut done = if !input.bit()? {
136+ let atom = input.rub()?;
137+ let noun = arena.atom(&atom);
138+ memo.push((pos, Some(noun)));
139+ noun
140+ } else if !input.bit()? {
141+ memo.push((pos, None));
142+ stack.push(Frame::Head(memo.len() - 1));
143+ continue;
144+ } else {
145+ let target = input.rub()?;
146+ let target = trim(&target);
147+ if target.len() > 8 {
148+ return Err(CueError::BadBackref {
149+ bit: pos,
150+ target: u64::MAX,
151+ });
152+ }
153+ let mut buf = [0u8; 8];
154+ buf[..target.len()].copy_from_slice(target);
155+ let target = u64::from_le_bytes(buf);
156+ memo.binary_search_by_key(&target, |&(at, _)| at)
157+ .ok()
158+ .and_then(|slot| memo[slot].1)
159+ .ok_or(CueError::BadBackref { bit: pos, target })?
160+ };
161+ loop {
162+ match stack.pop() {
163+ None => {
164+ let trailing_garbage = input.any_set_after();
165+ return Ok(Cued {
166+ noun: done,
167+ bits_read: input.pos,
168+ trailing_garbage,
169+ });
170+ }
171+ Some(Frame::Head(slot)) => {
172+ stack.push(Frame::Tail(slot, done));
173+ break;
174+ }
175+ Some(Frame::Tail(slot, head)) => {
176+ done = arena.cell(head, done);
177+ memo[slot].1 = Some(done);
178+ }
179+ }
180+ }
181+ }
182+}
183+
184+#[derive(Default)]
185+struct BitWriter {
186+ buf: Vec<u8>,
187+ /// Number of bits written; `buf` always holds exactly `ceil(len / 8)` bytes.
188+ len: u64,
189+}
190+
191+impl BitWriter {
192+ fn bit(&mut self, bit: bool) {
193+ let shift = self.len % 8;
194+ if shift == 0 {
195+ self.buf.push(0);
196+ }
197+ if bit {
198+ *self.buf.last_mut().unwrap() |= 1 << shift;
199+ }
200+ self.len += 1;
201+ }
202+
203+ /// Write the low `count` bits of `value`, least significant first.
204+ fn bits(&mut self, value: u64, count: u32) {
205+ for i in 0..count {
206+ self.bit((value >> i) & 1 == 1);
207+ }
208+ }
209+
210+ /// Write the `met0(atom)` significant bits of an atom.
211+ fn atom_bits(&mut self, atom: &[u8]) {
212+ let atom = trim(atom);
213+ let width = met0(atom);
214+ if width == 0 {
215+ return;
216+ }
217+ let shift = (self.len % 8) as u32;
218+ if shift == 0 {
219+ self.buf.extend_from_slice(atom);
220+ } else {
221+ let mut carry = u16::from(self.buf.pop().unwrap());
222+ for &byte in atom {
223+ let merged = carry | (u16::from(byte) << shift);
224+ self.buf.push(merged as u8);
225+ carry = merged >> 8;
226+ }
227+ if self.buf.len() < (self.len + width).div_ceil(8) as usize {
228+ self.buf.push(carry as u8);
229+ }
230+ }
231+ self.len += width;
232+ debug_assert_eq!(self.buf.len() as u64, self.len.div_ceil(8));
233+ }
234+
235+ /// Hoon's `mat`: self-delimiting atom encoding.
236+ fn mat(&mut self, atom: &[u8]) {
237+ let width = met0(atom);
238+ if width == 0 {
239+ self.bit(true);
240+ return;
241+ }
242+ let width_width = 64 - width.leading_zeros();
243+ self.bits(0, width_width);
244+ self.bit(true);
245+ self.bits(width, width_width - 1);
246+ self.atom_bits(atom);
247+ }
248+
249+ fn into_bytes(self) -> Vec<u8> {
250+ let mut buf = self.buf;
251+ let len = trim(&buf).len();
252+ buf.truncate(len);
253+ buf
254+ }
255+}
256+
257+struct BitReader<'a> {
258+ buf: &'a [u8],
259+ pos: u64,
260+ end: u64,
261+}
262+
263+impl<'a> BitReader<'a> {
264+ fn new(buf: &'a [u8]) -> Self {
265+ BitReader {
266+ buf,
267+ pos: 0,
268+ end: buf.len() as u64 * 8,
269+ }
270+ }
271+
272+ fn bit(&mut self) -> Result<bool, CueError> {
273+ if self.pos >= self.end {
274+ return Err(CueError::Truncated { bit: self.pos });
275+ }
276+ let bit = (self.buf[(self.pos / 8) as usize] >> (self.pos % 8)) & 1 == 1;
277+ self.pos += 1;
278+ Ok(bit)
279+ }
280+
281+ /// Read `count <= 64` bits as a little-endian integer. Like Hoon's
282+ /// `cut`, bits past the end of the input read as zero.
283+ fn bits(&mut self, count: u32) -> Result<u64, CueError> {
284+ let mut value = 0u64;
285+ for i in 0..count {
286+ if self.pos < self.end && (self.buf[(self.pos / 8) as usize] >> (self.pos % 8)) & 1 == 1
287+ {
288+ value |= 1 << i;
289+ }
290+ self.pos += 1;
291+ }
292+ Ok(value)
293+ }
294+
295+ /// Read `width` bits as a little-endian byte string. Bits past the end
296+ /// of the input read as zero (and are not allocated).
297+ fn atom(&mut self, width: u64) -> Result<Vec<u8>, CueError> {
298+ let start_bit = self.pos;
299+ let after = start_bit
300+ .checked_add(width)
301+ .ok_or(CueError::TooLarge { bit: start_bit })?;
302+ let present = width.min(self.end.saturating_sub(start_bit));
303+ let mut out = Vec::with_capacity(present.div_ceil(8) as usize);
304+ if present > 0 {
305+ let start = (start_bit / 8) as usize;
306+ let shift = (start_bit % 8) as u32;
307+ let len = present.div_ceil(8) as usize;
308+ for i in 0..len {
309+ let lo = self.buf[start + i] >> shift;
310+ let hi = match self.buf.get(start + i + 1) {
311+ Some(&next) if shift > 0 => next << (8 - shift),
312+ _ => 0,
313+ };
314+ out.push(lo | hi);
315+ }
316+ let spare = (len as u64 * 8 - present) as u32;
317+ if spare > 0 {
318+ *out.last_mut().unwrap() &= 0xff >> spare;
319+ }
320+ }
321+ self.pos = after;
322+ Ok(out)
323+ }
324+
325+ /// Hoon's `rub`: decode a `mat`-encoded atom.
326+ fn rub(&mut self) -> Result<Vec<u8>, CueError> {
327+ let start = self.pos;
328+ let mut zeros = 0u32;
329+ while !self.bit()? {
330+ zeros += 1;
331+ // vere's cue gives up at 64 (its lengths are under 2^63 bits).
332+ if zeros >= 64 {
333+ return Err(CueError::TooLarge { bit: start });
334+ }
335+ }
336+ if zeros == 0 {
337+ return Ok(Vec::new());
338+ }
339+ let width = (1u64 << (zeros - 1)) | self.bits(zeros - 1)?;
340+ self.atom(width)
341+ }
342+
343+ fn any_set_after(&self) -> bool {
344+ if self.pos >= self.end {
345+ return false;
346+ }
347+ let byte = (self.pos / 8) as usize;
348+ let shift = (self.pos % 8) as u32;
349+ (self.buf[byte] >> shift) != 0 || self.buf[byte + 1..].iter().any(|&b| b != 0)
350+ }
351+}
jammer/src/lib.rsnew file+13
@@ -0,0 +1,13 @@
1+//! Pack desk directories into `.deskjam` files for the deskjam Urbit app,
2+//! and unpack `.deskjam` files back into directories.
3+
4+pub mod aura;
5+pub mod deskpack;
6+pub mod jam;
7+pub mod marks;
8+pub mod mug;
9+pub mod noun;
10+pub mod output;
11+pub mod pack;
12+pub mod text;
13+pub mod treap;
jammer/src/main.rsnew file+525
@@ -0,0 +1,525 @@
1+use std::collections::{BTreeSet, HashMap, HashSet};
2+use std::fs;
3+use std::io::{self, IsTerminal, Read, Write};
4+use std::path::{Component, Path, PathBuf};
5+use std::process::ExitCode;
6+
7+use anyhow::{Context, Result, bail};
8+use clap::{Parser, Subcommand};
9+
10+use jammer::deskpack::{self, Deskpack, Page};
11+use jammer::marks::{self, MarkError};
12+use jammer::noun::Noun;
13+use jammer::output::{WriteError, Writer};
14+use jammer::pack;
15+use jammer::text::printable;
16+
17+#[derive(Parser)]
18+#[command(
19+ version,
20+ about = "Pack desk directories into .deskjam files for the deskjam Urbit app, and unpack them",
21+ after_help = "Exit status: 0 on success, 1 on error, 3 if some files were left out."
22+)]
23+struct Cli {
24+ #[command(subcommand)]
25+ command: Command,
26+}
27+
28+#[derive(Subcommand)]
29+enum Command {
30+ /// Pack a desk directory into a .deskjam file
31+ ///
32+ /// Files are selected exactly like `|commit` selects them from a mounted
33+ /// desk, and stored so that deskjam builds the same desk `|commit` would.
34+ Jam {
35+ /// The desk directory (a mounted desk, or a repo's desk folder)
36+ dir: PathBuf,
37+ /// Output file [default: ./<desk>.deskjam]; `-` writes to stdout
38+ #[arg(short, long)]
39+ output: Option<PathBuf>,
40+ /// Desk name recorded in the file [default: the directory's name]
41+ #[arg(short, long)]
42+ desk: Option<String>,
43+ /// Replace an existing output file that is not a .deskjam
44+ #[arg(short, long)]
45+ force: bool,
46+ /// Print every file that is packed or skipped
47+ #[arg(short, long)]
48+ verbose: bool,
49+ },
50+ /// Unpack a .deskjam file into a directory
51+ ///
52+ /// Each file is converted to the bytes a desk mount would write for it.
53+ Unjam {
54+ /// The .deskjam file; `-` reads stdin
55+ file: PathBuf,
56+ /// Output directory [default: ./<desk>]
57+ #[arg(short, long)]
58+ output: Option<PathBuf>,
59+ /// Write into an existing, non-empty directory, overwriting files
60+ #[arg(short, long)]
61+ force: bool,
62+ /// Print every file as it is written
63+ #[arg(short, long)]
64+ verbose: bool,
65+ },
66+ /// List the files in a .deskjam file
67+ #[command(visible_alias = "ls")]
68+ List {
69+ /// The .deskjam file; `-` reads stdin
70+ file: PathBuf,
71+ },
72+}
73+
74+/// Exit status when the command worked but left some files out.
75+const PARTIAL: u8 = 3;
76+
77+/// Most `unjam` will write in total. A deskjam is untrusted input, and
78+/// backreferences let a few kilobytes describe terabytes of files.
79+const MAX_TOTAL_SIZE: u64 = 4 << 30;
80+
81+/// deskjam's uploads go through vere's HTTP server, which rejects request
82+/// bodies over 512 MiB.
83+const MAX_UPLOAD_SIZE: u64 = 512 << 20;
84+
85+fn main() -> ExitCode {
86+ let cli = Cli::parse();
87+ let result = match cli.command {
88+ Command::Jam {
89+ dir,
90+ output,
91+ desk,
92+ force,
93+ verbose,
94+ } => jam(&dir, output, desk, force, verbose),
95+ Command::Unjam {
96+ file,
97+ output,
98+ force,
99+ verbose,
100+ } => unjam(&file, output, force, verbose),
101+ Command::List { file } => list(&file),
102+ };
103+ match result {
104+ Ok(true) => ExitCode::SUCCESS,
105+ Ok(false) => ExitCode::from(PARTIAL),
106+ // The reader of our output went away (e.g. `jammer list x | head`).
107+ Err(e)
108+ if e.chain().any(|c| {
109+ c.downcast_ref::<io::Error>()
110+ .is_some_and(|e| e.kind() == io::ErrorKind::BrokenPipe)
111+ }) =>
112+ {
113+ ExitCode::SUCCESS
114+ }
115+ Err(e) => {
116+ eprintln!("jammer: {e:#}");
117+ ExitCode::FAILURE
118+ }
119+ }
120+}
121+
122+fn is_stdio(path: &Path) -> bool {
123+ path.as_os_str() == "-"
124+}
125+
126+/// A local path, made safe to print.
127+fn shown(path: &Path) -> String {
128+ printable(path.to_string_lossy().as_bytes())
129+}
130+
131+/// The desk name implied by a directory: its last component as the user
132+/// wrote it (not where a symlink leads), so `jammer jam my-app` names the
133+/// desk `%my-app` even if `my-app` links to `repo/desk`.
134+fn desk_name_for(dir: &Path) -> Result<String> {
135+ let absolute = std::path::absolute(dir)?;
136+ let mut names = Vec::new();
137+ for component in absolute.components() {
138+ match component {
139+ Component::Normal(name) => names.push(name),
140+ Component::ParentDir => {
141+ names.pop();
142+ }
143+ _ => {}
144+ }
145+ }
146+ let name = names.last().and_then(|n| n.to_str()).unwrap_or_default();
147+ if !pack::is_term(name) {
148+ bail!(
149+ "the directory name {:?} is not a valid desk name; pass one with --desk",
150+ printable(name.as_bytes())
151+ );
152+ }
153+ Ok(name.to_owned())
154+}
155+
156+/// Whether `output` would land inside `dir`, where a later `|commit` of the
157+/// mounted desk would try (and fail) to commit it.
158+fn is_inside(dir: &Path, output: &Path) -> bool {
159+ let parent = match output.parent() {
160+ Some(parent) if !parent.as_os_str().is_empty() => parent,
161+ _ => Path::new("."),
162+ };
163+ match (fs::canonicalize(dir), fs::canonicalize(parent)) {
164+ (Ok(dir), Ok(parent)) => parent.starts_with(dir),
165+ _ => false,
166+ }
167+}
168+
169+/// Returns Ok(true) if every file the rules select was packed.
170+fn jam(
171+ dir: &Path,
172+ output: Option<PathBuf>,
173+ desk: Option<String>,
174+ force: bool,
175+ verbose: bool,
176+) -> Result<bool> {
177+ let dir_meta = fs::metadata(dir).with_context(|| format!("cannot read {}", shown(dir)))?;
178+ if !dir_meta.is_dir() {
179+ bail!("{} is not a directory", shown(dir));
180+ }
181+ let desk = match desk {
182+ Some(desk) if pack::is_term(&desk) => desk,
183+ Some(desk) => bail!(
184+ "{:?} is not a valid desk name (lowercase letters, digits and hyphens, starting with a letter)",
185+ printable(desk.as_bytes())
186+ ),
187+ None => desk_name_for(dir)?,
188+ };
189+ let explicit = output.is_some();
190+ let output = output.unwrap_or_else(|| PathBuf::from(format!("{desk}.deskjam")));
191+ if is_stdio(&output) {
192+ if io::stdout().is_terminal() {
193+ bail!("refusing to write a deskjam to the terminal; redirect stdout or pass -o FILE");
194+ }
195+ } else {
196+ if is_inside(dir, &output) {
197+ if !explicit {
198+ bail!(
199+ "refusing to write {} inside the desk directory, where |commit would try to commit it; pass -o with a path outside it",
200+ shown(&output)
201+ );
202+ }
203+ eprintln!(
204+ "warning: {} is inside the desk directory; if the desk is mounted, |commit will fail until it is removed",
205+ shown(&output)
206+ );
207+ }
208+ let is_deskjam = output.extension().is_some_and(|ext| ext == "deskjam");
209+ if output.exists() && !is_deskjam && !force {
210+ bail!(
211+ "{} exists and is not a .deskjam file; pass --force to replace it",
212+ shown(&output)
213+ );
214+ }
215+ }
216+ // Never pack a previous output of this command into the desk.
217+ let exclude = if is_stdio(&output) {
218+ None
219+ } else {
220+ fs::canonicalize(&output).ok()
221+ };
222+
223+ let collected = pack::collect(dir, exclude.as_deref())
224+ .with_context(|| format!("cannot read {}", shown(dir)))?;
225+ let mut complete = true;
226+ for skipped in &collected.skipped {
227+ if verbose || skipped.reason.is_surprising() {
228+ eprintln!("skipping {}: {}", shown(&skipped.path), skipped.reason);
229+ }
230+ complete &= !skipped.reason.is_problem();
231+ }
232+ if verbose {
233+ for file in &collected.files {
234+ eprintln!("adding /{}", printable(file.path.join("/").as_bytes()));
235+ }
236+ }
237+ if collected.files.is_empty() {
238+ bail!("no files to pack in {}", shown(dir));
239+ }
240+ for (odd, count) in &collected.odd_directories {
241+ eprintln!(
242+ "note: packing {count} file(s) under {}, although that name is not a valid clay path segment (|commit does the same)",
243+ shown(odd)
244+ );
245+ }
246+ for path in pack::unparsable_config(&collected.files) {
247+ eprintln!(
248+ "warning: /{path} contains carriage returns or tabs, which clay cannot parse; deskjam would fail to commit it"
249+ );
250+ }
251+ let missing = pack::missing_marks(&collected.files);
252+ if !missing.is_empty() {
253+ let list: Vec<String> = missing.iter().map(|m| format!("%{m}")).collect();
254+ eprintln!(
255+ "note: no mark definition under mar/ for {}; deskjam can only import such files into a desk that has these marks",
256+ list.join(", ")
257+ );
258+ }
259+ let has_kelvin = collected.files.iter().any(|f| f.path == ["sys", "kelvin"]);
260+ let count = collected.files.len();
261+ let size: usize = collected.files.iter().map(|f| f.contents.len()).sum();
262+ let bytes = deskpack::encode(&desk, collected.files);
263+
264+ if is_stdio(&output) {
265+ io::stdout()
266+ .lock()
267+ .write_all(&bytes)
268+ .context("cannot write to stdout")?;
269+ } else {
270+ fs::write(&output, &bytes).with_context(|| format!("cannot write {}", shown(&output)))?;
271+ }
272+ eprintln!(
273+ "packed {count} files ({}) from {} as %{desk} into {} ({})",
274+ human(size as u64),
275+ shown(dir),
276+ if is_stdio(&output) {
277+ "stdout".into()
278+ } else {
279+ shown(&output)
280+ },
281+ human(bytes.len() as u64),
282+ );
283+ if !has_kelvin {
284+ eprintln!(
285+ "note: there is no sys.kelvin, so deskjam can only merge these files into an existing desk, not create a new desk from them"
286+ );
287+ }
288+ if bytes.len() as u64 > MAX_UPLOAD_SIZE {
289+ eprintln!(
290+ "note: the file is larger than {}, the most a ship accepts in an upload; serve it over HTTP and use deskjam's import from URL instead",
291+ human(MAX_UPLOAD_SIZE)
292+ );
293+ }
294+ Ok(complete)
295+}
296+
297+fn read_input(file: &Path) -> Result<Vec<u8>> {
298+ if is_stdio(file) {
299+ let mut bytes = Vec::new();
300+ io::stdin()
301+ .lock()
302+ .read_to_end(&mut bytes)
303+ .context("cannot read stdin")?;
304+ Ok(bytes)
305+ } else {
306+ fs::read(file).with_context(|| format!("cannot read {}", shown(file)))
307+ }
308+}
309+
310+fn load(file: &Path) -> Result<Deskpack> {
311+ let bytes = read_input(file)?;
312+ let pack =
313+ deskpack::decode(&bytes).with_context(|| format!("cannot unpack {}", shown(file)))?;
314+ if !pack.canonical_map {
315+ eprintln!(
316+ "warning: the file table is not a canonical Hoon map; deskjam would reject this file"
317+ );
318+ }
319+ if pack.trailing_garbage {
320+ eprintln!("warning: ignoring data after the end of the jam");
321+ }
322+ Ok(pack)
323+}
324+
325+fn clay_path(segments: &[Vec<u8>]) -> String {
326+ segments
327+ .iter()
328+ .map(|s| format!("/{}", printable(s)))
329+ .collect()
330+}
331+
332+/// What rendering one page found, and what it cost.
333+struct Known {
334+ result: Result<usize, MarkError>,
335+ cost: u64,
336+}
337+
338+/// Bounds the work of rendering a deskjam's pages by [`MAX_TOTAL_SIZE`],
339+/// counting both the bytes produced and the nouns walked (see
340+/// [`marks::Rendered`]). Pages sharing one noun, which deskjams can do at
341+/// no cost, are rendered once here, and failures are remembered so they
342+/// are not rendered again.
343+struct Budget {
344+ spent: u64,
345+ /// Whether successful pages will be rendered again to be written, in
346+ /// which case each one pays for that too.
347+ again: bool,
348+ known: HashMap<(Vec<u8>, Noun), Known>,
349+}
350+
351+impl Budget {
352+ fn new(again: bool) -> Self {
353+ Budget {
354+ spent: 0,
355+ again,
356+ known: HashMap::new(),
357+ }
358+ }
359+
360+ fn exhausted(&self) -> bool {
361+ self.spent > MAX_TOTAL_SIZE
362+ }
363+
364+ /// The size of a page's file, charging the work it takes.
365+ fn size(&mut self, pack: &Deskpack, page: &Page) -> Result<usize, MarkError> {
366+ if let Some(declared) = marks::declared_size(&pack.arena, &page.mark, page.noun) {
367+ self.spent += declared.as_ref().map_or(0, |&size| size as u64);
368+ return declared;
369+ }
370+ let key = (page.mark.clone(), page.noun);
371+ if let Some(known) = self.known.get(&key) {
372+ if self.again && known.result.is_ok() {
373+ self.spent += known.cost;
374+ }
375+ return known.result.clone();
376+ }
377+ let rendered = marks::render(&pack.arena, &page.mark, page.noun);
378+ self.spent += rendered.cost;
379+ let result = rendered.result.map(|bytes| bytes.len());
380+ let known = Known {
381+ result: result.clone(),
382+ cost: rendered.cost,
383+ };
384+ self.known.insert(key, known);
385+ result
386+ }
387+
388+ /// Why a page already failed to render, if it did.
389+ fn failure(&self, page: &Page) -> Option<MarkError> {
390+ let known = self.known.get(&(page.mark.clone(), page.noun))?;
391+ known.result.clone().err()
392+ }
393+}
394+
395+/// Returns Ok(true) if every file was written.
396+fn unjam(file: &Path, output: Option<PathBuf>, force: bool, verbose: bool) -> Result<bool> {
397+ let pack = load(file)?;
398+ let desk = printable(&pack.desk);
399+ let root = match output {
400+ Some(dir) => dir,
401+ None if pack::is_term(&desk) => PathBuf::from(&desk),
402+ None => bail!("the desk name {desk:?} is not usable as a directory name; pass --output"),
403+ };
404+ // Size everything up before writing anything.
405+ let mut budget = Budget::new(true);
406+ for page in &pack.pages {
407+ let _ = budget.size(&pack, page);
408+ if budget.exhausted() {
409+ bail!(
410+ "refusing to unpack: the files add up to more than {} (counting the work of rendering them)",
411+ human(MAX_TOTAL_SIZE)
412+ );
413+ }
414+ }
415+ let mut writer = Writer::new(&root, force)
416+ .with_context(|| format!("cannot unpack into {}", shown(&root)))?;
417+
418+ let mut complete = true;
419+ let mut seen = HashSet::new();
420+ let mut guessed = BTreeSet::new();
421+ let mut count = 0usize;
422+ let mut size = 0u64;
423+ for page in &pack.pages {
424+ let shown_path = clay_path(&page.path);
425+ let mut skip = |why: &dyn std::fmt::Display| {
426+ eprintln!("skipping {shown_path}: {why}");
427+ complete = false;
428+ };
429+ let components = match marks::unix_path(&page.path) {
430+ Ok(components) => components,
431+ Err(why) => {
432+ skip(&why);
433+ continue;
434+ }
435+ };
436+ if !seen.insert(components.clone()) {
437+ skip(&format!(
438+ "another file already maps to {}",
439+ printable(components.join("/").as_bytes())
440+ ));
441+ continue;
442+ }
443+ let rendered = match budget.failure(page) {
444+ Some(why) => Err(why),
445+ None => marks::to_bytes(&pack.arena, &page.mark, page.noun),
446+ };
447+ let contents = match rendered {
448+ Ok(contents) => contents,
449+ Err(why) => {
450+ skip(&why);
451+ continue;
452+ }
453+ };
454+ if !marks::is_known(&page.mark) {
455+ guessed.insert(printable(&page.mark));
456+ }
457+ match writer.write(&components, &contents) {
458+ Ok(path) => {
459+ if verbose {
460+ eprintln!("wrote {}", shown(&path));
461+ }
462+ count += 1;
463+ size += contents.len() as u64;
464+ }
465+ Err(WriteError::Conflict(why)) => skip(&why),
466+ Err(WriteError::Io(e)) => skip(&e),
467+ }
468+ }
469+ if !guessed.is_empty() {
470+ let list: Vec<String> = guessed.iter().map(|m| format!("%{m}")).collect();
471+ eprintln!(
472+ "note: jammer does not know the marks {}; it wrote their files as the raw bytes of each page, which is right for most text and binary marks",
473+ list.join(", ")
474+ );
475+ }
476+ eprintln!(
477+ "unpacked {count} of {} files ({}) of %{desk} into {}",
478+ pack.pages.len(),
479+ human(size),
480+ shown(&root)
481+ );
482+ Ok(complete)
483+}
484+
485+fn list(file: &Path) -> Result<bool> {
486+ let pack = load(file)?;
487+ let mut out = io::stdout().lock();
488+ writeln!(
489+ out,
490+ "desk %{} ({} files)",
491+ printable(&pack.desk),
492+ pack.pages.len()
493+ )?;
494+ let mut budget = Budget::new(false);
495+ for page in &pack.pages {
496+ // Stop sizing once a hostile file has made us do too much work.
497+ let size = if budget.exhausted() {
498+ None
499+ } else {
500+ budget.size(&pack, page).ok()
501+ };
502+ let size = size.map_or_else(|| "?".to_owned(), |size| size.to_string());
503+ writeln!(
504+ out,
505+ "{size:>10} %{:<10} {}",
506+ printable(&page.mark),
507+ clay_path(&page.path)
508+ )?;
509+ }
510+ Ok(true)
511+}
512+
513+fn human(bytes: u64) -> String {
514+ const UNITS: [&str; 4] = ["KiB", "MiB", "GiB", "TiB"];
515+ if bytes < 1024 {
516+ return format!("{bytes} B");
517+ }
518+ let mut value = bytes as f64 / 1024.0;
519+ let mut unit = 0;
520+ while value >= 1024.0 && unit + 1 < UNITS.len() {
521+ value /= 1024.0;
522+ unit += 1;
523+ }
524+ format!("{value:.1} {}", UNITS[unit])
525+}
jammer/src/marks.rsnew file+752
@@ -0,0 +1,752 @@
1+//! Turning clay pages back into file contents, like a desk mount does with
2+//! each mark's `grow:mime` arm.
3+
4+use std::collections::{HashMap, HashSet};
5+use std::fmt;
6+
7+use crate::aura;
8+use crate::jam::jam_counting;
9+use crate::noun::{Arena, Noun, View, cmp_atoms, trim};
10+use crate::output::is_safe_component;
11+use crate::text::printable;
12+
13+/// Why a page could not be turned into file contents.
14+#[derive(Debug, Clone, PartialEq, Eq)]
15+pub enum MarkError {
16+ /// The noun does not have the shape the mark requires.
17+ BadNoun(String),
18+ /// A mark whose pages jammer cannot render.
19+ Unsupported(String),
20+ /// The file would be larger than [`MAX_FILE_SIZE`].
21+ TooLarge,
22+}
23+
24+/// Largest file `to_bytes` will produce. Backreferences let a small deskjam
25+/// describe enormous files, so untrusted input must be bounded.
26+pub const MAX_FILE_SIZE: usize = 1 << 30;
27+
28+impl fmt::Display for MarkError {
29+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30+ match self {
31+ MarkError::BadNoun(mark) => write!(f, "contents are not a valid %{mark} noun"),
32+ MarkError::Unsupported(mark) => {
33+ write!(f, "jammer cannot turn a %{mark} page into a file")
34+ }
35+ MarkError::TooLarge => write!(f, "file would be larger than {} bytes", MAX_FILE_SIZE),
36+ }
37+ }
38+}
39+
40+impl std::error::Error for MarkError {}
41+
42+/// The relative unix path a desk mount uses for a clay path:
43+/// `/app/foo/hoon` becomes `app/foo.hoon`. Like vere, segments that are
44+/// empty, `.` or `..`, or that start with `!`, are written with a `!` in
45+/// front (which `|commit` strips again), so they cannot escape the
46+/// directory. Segments need not be valid `@ta`s (`|commit` accepts directory
47+/// names like `Upper`), but they must be UTF-8 without separators or
48+/// control characters.
49+pub fn unix_path(segments: &[Vec<u8>]) -> Result<Vec<String>, String> {
50+ let mut parts = Vec::with_capacity(segments.len());
51+ for segment in segments {
52+ let text = std::str::from_utf8(segment)
53+ .ok()
54+ .map(|s| match s {
55+ "" | "." | ".." => format!("!{s}"),
56+ s if s.starts_with('!') => format!("!{s}"),
57+ s => s.to_owned(),
58+ })
59+ .filter(|s| is_safe_component(s))
60+ .ok_or_else(|| {
61+ format!(
62+ "path segment {:?} cannot be a file name",
63+ String::from_utf8_lossy(segment)
64+ )
65+ })?;
66+ parts.push(text);
67+ }
68+ match parts.len() {
69+ 0 => Err("empty path".into()),
70+ 1 => Err("path has no file extension".into()),
71+ _ => {
72+ let mark = parts.pop().unwrap();
73+ let stem = parts.pop().unwrap();
74+ parts.push(format!("{stem}.{mark}"));
75+ Ok(parts)
76+ }
77+ }
78+}
79+
80+/// Marks in `%base` and landscape whose files are not just the noun's bytes,
81+/// and which jammer does not render: XML (`manx`) and CSV printers, and
82+/// marks with no file form at all (a desk mount crashes on them).
83+const UNRENDERABLE: &[&[u8]] = &[
84+ b"hymn",
85+ b"urb",
86+ b"snip",
87+ b"eth-txs",
88+ b"acme-order",
89+ b"aqua-effect",
90+ b"azimuth-update",
91+ b"belt",
92+ b"blit",
93+ b"dill-belt",
94+ b"dill-blit",
95+ b"dns-address",
96+ b"dns-binding",
97+ b"dns-complete",
98+ b"dns-request",
99+ b"drum-put",
100+ b"egg-any",
101+ b"eth-watcher-poke",
102+ b"helm-moon-breach",
103+ b"herm-task",
104+ b"httr",
105+ b"json-rpc-response",
106+ b"kiln-approve-merge",
107+ b"kiln-bump",
108+ b"kiln-install",
109+ b"kiln-jump",
110+ b"kiln-jump-ask",
111+ b"kiln-jump-opt",
112+ b"kiln-pause",
113+ b"kiln-pikes",
114+ b"kiln-resume",
115+ b"kiln-revive",
116+ b"kiln-suspend",
117+ b"kiln-sync",
118+ b"kiln-sync-update",
119+ b"kiln-uninstall",
120+ b"kiln-unsync",
121+ b"language-server-rpc-notification",
122+ b"language-server-rpc-request",
123+ b"language-server-rpc-response",
124+ b"lens-command",
125+ b"loob",
126+ b"mass",
127+ b"path",
128+ b"purl",
129+ b"sole-action",
130+ b"sole-effect",
131+ b"spider-stop",
132+ b"story-diff",
133+ b"tang",
134+ b"ted-eval",
135+ b"thread-done",
136+ b"thread-fail",
137+ b"txt-diff",
138+ b"urbit",
139+ b"verb-event",
140+ b"verb-event-plus",
141+ b"vere",
142+ b"vere-update",
143+ b"version",
144+];
145+
146+/// Marks whose files are simply the bytes of their atom (`grow:mime` is
147+/// `(as-octs atom)`), from `%base`, landscape and `%groups`.
148+const RAW_BYTES: &[&[u8]] = &[
149+ b"hoon", b"js", b"css", b"html", b"htm", b"xhtml", b"svg", b"png", b"ico", b"map", b"jam",
150+ b"xml", b"udon", b"umd", b"atom", b"helm-hi", b"release",
151+];
152+
153+/// Whether jammer knows how pages of `mark` render, rather than guessing
154+/// that the file is the bytes of the page's atom (as it does for marks it
155+/// has never heard of).
156+pub fn is_known(mark: &[u8]) -> bool {
157+ RAW_BYTES.contains(&mark)
158+ || UNRENDERABLE.contains(&mark)
159+ || matches!(
160+ mark,
161+ b"mime"
162+ | b"wasm"
163+ | b"woff2"
164+ | b"txt"
165+ | b"md"
166+ | b"pem"
167+ | b"tape"
168+ | b"story"
169+ | b"json"
170+ | b"kelvin"
171+ | b"bill"
172+ | b"docket-0"
173+ | b"ship"
174+ | b"run-check"
175+ | b"noun"
176+ | b"noun-autodiff"
177+ | b"azimuth-snapshot"
178+ | b"eth-logs"
179+ | b"pill"
180+ )
181+}
182+
183+/// Why a renderer failed; [`to_bytes`] adds the mark name.
184+enum Fail {
185+ Bad,
186+ TooLarge,
187+}
188+
189+/// The cost of walking one noun, in bytes of output. Walking is what a
190+/// hostile page can make expensive without producing anything (a long list
191+/// of empty lines, a big map that turns out to be malformed).
192+pub const VISIT_COST: u64 = 64;
193+
194+/// A file under construction that refuses to grow past [`MAX_FILE_SIZE`],
195+/// so hostile nouns (say, a list repeating one big atom) cannot make a
196+/// renderer allocate more than that. It also counts the nouns the renderer
197+/// walks, so callers can bound the total work.
198+#[derive(Default)]
199+struct Out {
200+ bytes: Vec<u8>,
201+ visits: u64,
202+}
203+
204+impl Out {
205+ fn put(&mut self, bytes: &[u8]) -> Result<(), Fail> {
206+ if self.bytes.len() + bytes.len() > MAX_FILE_SIZE {
207+ return Err(Fail::TooLarge);
208+ }
209+ self.bytes.extend_from_slice(bytes);
210+ Ok(())
211+ }
212+
213+ fn atom(&mut self, arena: &Arena, noun: Noun) -> Result<(), Fail> {
214+ self.put(arena.atom_bytes(noun).ok_or(Fail::Bad)?)
215+ }
216+
217+ fn cost(&self) -> u64 {
218+ self.bytes.len() as u64 + VISIT_COST * self.visits
219+ }
220+
221+ /// The elements of a null-terminated list.
222+ fn list(&mut self, arena: &Arena, mut noun: Noun) -> Result<Vec<Noun>, Fail> {
223+ let mut items = Vec::new();
224+ loop {
225+ self.visits += 1;
226+ match arena.view(noun) {
227+ View::Atom([]) => return Ok(items),
228+ View::Atom(_) => return Err(Fail::Bad),
229+ View::Cell(head, tail) => {
230+ items.push(head);
231+ noun = tail;
232+ }
233+ }
234+ }
235+ }
236+
237+ /// The items of a map or set tree in `tap` order (right subtree, node,
238+ /// left subtree). A repeated subtree is malformed, which also keeps
239+ /// the walk linear.
240+ fn tap(&mut self, arena: &Arena, tree: Noun) -> Result<Vec<Noun>, Fail> {
241+ let mut items = Vec::new();
242+ let mut seen = HashSet::new();
243+ let mut stack = Vec::new();
244+ let mut node = tree;
245+ loop {
246+ loop {
247+ self.visits += 1;
248+ match arena.view(node) {
249+ View::Atom([]) => break,
250+ View::Atom(_) => return Err(Fail::Bad),
251+ View::Cell(_, children) => {
252+ if !seen.insert(node) {
253+ return Err(Fail::Bad);
254+ }
255+ stack.push(node);
256+ node = arena.as_cell(children).ok_or(Fail::Bad)?.1;
257+ }
258+ }
259+ }
260+ let Some(top) = stack.pop() else { break };
261+ let (item, children) = arena.as_cell(top).ok_or(Fail::Bad)?;
262+ items.push(item);
263+ node = arena.as_cell(children).ok_or(Fail::Bad)?.0;
264+ }
265+ Ok(items)
266+ }
267+}
268+
269+/// Largest atom printed as `@ud` (617 digits): decimal conversion is
270+/// quadratic, and the numbers marks print (kelvins, versions) are tiny.
271+const MAX_UD_BYTES: usize = 256;
272+
273+fn ud(arena: &Arena, noun: Noun) -> Result<String, Fail> {
274+ let atom = arena.atom_bytes(noun).ok_or(Fail::Bad)?;
275+ if atom.len() > MAX_UD_BYTES {
276+ return Err(Fail::TooLarge);
277+ }
278+ Ok(aura::ud(atom))
279+}
280+
281+/// `@ux`, `@uv` and `@p` text is at most a few times the atom's size.
282+fn other_aura(arena: &Arena, noun: Noun, print: fn(&[u8]) -> String) -> Result<String, Fail> {
283+ let atom = arena.atom_bytes(noun).ok_or(Fail::Bad)?;
284+ if atom.len() > MAX_FILE_SIZE / 4 {
285+ return Err(Fail::TooLarge);
286+ }
287+ Ok(print(atom))
288+}
289+
290+/// A rendered page, and what rendering it cost: the bytes produced (as far
291+/// as it got) plus [`VISIT_COST`] for each noun walked, whether or not it
292+/// succeeded.
293+pub struct Rendered {
294+ pub result: Result<Vec<u8>, MarkError>,
295+ pub cost: u64,
296+}
297+
298+/// File contents for a page with the given mark.
299+pub fn to_bytes(arena: &Arena, mark: &[u8], noun: Noun) -> Result<Vec<u8>, MarkError> {
300+ render(arena, mark, noun).result
301+}
302+
303+/// [`to_bytes`], with the cost of rendering.
304+pub fn render(arena: &Arena, mark: &[u8], noun: Noun) -> Rendered {
305+ let name = || printable(mark);
306+ let octs = |octs| {
307+ let result = octs_bytes(arena, octs).map_err(|e| fail(e, name));
308+ let cost = result.as_ref().map_or(0, |bytes| bytes.len() as u64);
309+ Rendered { result, cost }
310+ };
311+ let unsupported = || Rendered {
312+ result: Err(MarkError::Unsupported(name())),
313+ cost: 0,
314+ };
315+ let mut out = Out::default();
316+ let rendered = match mark {
317+ b"mime" => match arena.as_cell(noun) {
318+ Some((_, data)) => return octs(data),
319+ None => Err(Fail::Bad),
320+ },
321+ // Binary marks whose noun is `octs`.
322+ b"wasm" | b"woff2" => return octs(noun),
323+ b"txt" | b"md" | b"pem" => txt(arena, noun, &mut out),
324+ b"tape" => tape(arena, noun, &mut out),
325+ b"story" => story(arena, noun, &mut out),
326+ b"json" => json(arena, noun, &mut out),
327+ b"kelvin" => kelvin(arena, noun, &mut out),
328+ // These two print through tapes, and `crip` drops NUL bytes.
329+ b"bill" => bill(arena, noun, &mut out),
330+ b"docket-0" => docket(arena, noun, &mut out),
331+ b"ship" | b"run-check" => {
332+ other_aura(arena, noun, aura::p).and_then(|text| out.put(text.as_bytes()))
333+ }
334+ // Generic noun marks write the noun's jam.
335+ b"noun" | b"noun-autodiff" | b"azimuth-snapshot" | b"eth-logs" | b"pill" => {
336+ let (bytes, visits) = jam_counting(arena, noun);
337+ out.bytes = bytes;
338+ // jam hashes every noun it visits: count those visits as dearer.
339+ out.visits = 4 * visits;
340+ Ok(())
341+ }
342+ _ if UNRENDERABLE.contains(&mark) => return unsupported(),
343+ // Most marks (hoon, js, css, html, svg, png, ...) store the file
344+ // itself as an atom.
345+ _ => match arena.atom_bytes(noun) {
346+ Some(bytes) => out.put(bytes),
347+ None => return unsupported(),
348+ },
349+ };
350+ let cost = out.cost();
351+ let result = match rendered {
352+ Err(e) => Err(fail(e, name)),
353+ Ok(()) if out.bytes.len() > MAX_FILE_SIZE => Err(MarkError::TooLarge),
354+ Ok(()) => {
355+ let mut bytes = out.bytes;
356+ if matches!(mark, b"bill" | b"docket-0") {
357+ bytes.retain(|&b| b != 0);
358+ }
359+ Ok(bytes)
360+ }
361+ };
362+ Rendered { result, cost }
363+}
364+
365+fn fail(fail: Fail, name: impl Fn() -> String) -> MarkError {
366+ match fail {
367+ Fail::Bad => MarkError::BadNoun(name()),
368+ Fail::TooLarge => MarkError::TooLarge,
369+ }
370+}
371+
372+/// The size an `octs`-based page (all pages jammer writes) declares, found
373+/// without rendering it; `None` for other pages. Too-large sizes are
374+/// reported as [`MarkError::TooLarge`].
375+pub fn declared_size(arena: &Arena, mark: &[u8], noun: Noun) -> Option<Result<usize, MarkError>> {
376+ let octs = match mark {
377+ b"mime" => arena.as_cell(noun)?.1,
378+ b"wasm" | b"woff2" => noun,
379+ _ => return None,
380+ };
381+ match octs_parts(arena, octs) {
382+ Ok((size, _)) => Some(Ok(size)),
383+ Err(Fail::TooLarge) => Some(Err(MarkError::TooLarge)),
384+ Err(Fail::Bad) => None,
385+ }
386+}
387+
388+/// The size of the file [`to_bytes`] would produce, cheaply where possible.
389+pub fn size(arena: &Arena, mark: &[u8], noun: Noun) -> Result<usize, MarkError> {
390+ declared_size(arena, mark, noun)
391+ .unwrap_or_else(|| to_bytes(arena, mark, noun).map(|bytes| bytes.len()))
392+}
393+
394+/// `[size data]`: the first `size` bytes of `data`, zero-padded, as vere
395+/// writes a `mime`'s `octs`.
396+fn octs_bytes(arena: &Arena, octs: Noun) -> Result<Vec<u8>, Fail> {
397+ let (size, data) = octs_parts(arena, octs)?;
398+ let mut out = data[..data.len().min(size)].to_vec();
399+ out.resize(size, 0);
400+ Ok(out)
401+}
402+
403+fn octs_parts(arena: &Arena, octs: Noun) -> Result<(usize, &[u8]), Fail> {
404+ let (size, data) = arena.as_cell(octs).ok_or(Fail::Bad)?;
405+ let data = arena.atom_bytes(data).ok_or(Fail::Bad)?;
406+ match arena.as_u64(size) {
407+ Some(size) if size <= MAX_FILE_SIZE as u64 => Ok((size as usize, data)),
408+ Some(_) => Err(Fail::TooLarge),
409+ None if arena.is_atom(size) => Err(Fail::TooLarge),
410+ None => Err(Fail::Bad),
411+ }
412+}
413+
414+/// `%txt`: a `wain`, printed by `of-wain` (lines joined by newlines).
415+fn txt(arena: &Arena, noun: Noun, out: &mut Out) -> Result<(), Fail> {
416+ for (i, line) in out.list(arena, noun)?.into_iter().enumerate() {
417+ if i > 0 {
418+ out.put(b"\n")?;
419+ }
420+ out.atom(arena, line)?;
421+ }
422+ Ok(())
423+}
424+
425+/// `%tape`: a list of characters, concatenated (`rap 3`).
426+fn tape(arena: &Arena, noun: Noun, out: &mut Out) -> Result<(), Fail> {
427+ for char in out.list(arena, noun)? {
428+ out.atom(arena, char)?;
429+ }
430+ Ok(())
431+}
432+
433+/// `to-wain` as ships run it (the `leer` jet): split at newlines, where a
434+/// final newline ends the last line rather than starting an empty one, and
435+/// each line is an atom (losing trailing NUL bytes).
436+fn to_wain(text: &[u8]) -> Vec<&[u8]> {
437+ let text = trim(text);
438+ let mut lines = Vec::new();
439+ let mut start = 0;
440+ while start < text.len() {
441+ let end = text[start..]
442+ .iter()
443+ .position(|&b| b == b'\n')
444+ .map_or(text.len(), |i| start + i);
445+ lines.push(trim(&text[start..end]));
446+ start = end + 1;
447+ }
448+ lines
449+}
450+
451+/// `%story`: commit messages, `(jug tako [title=@t body=@t])`, printed by
452+/// `of-wain`. Each chapter is `commit: <tako>` and its proses (`title`, blank
453+/// line, `body`, through `to-wain`), proses separated by `|||` and chapters
454+/// by `---`.
455+fn story(arena: &Arena, noun: Noun, out: &mut Out) -> Result<(), Fail> {
456+ let mut first_line = true;
457+ let mut line = |out: &mut Out, text: &[u8]| {
458+ if !first_line {
459+ out.put(b"\n")?;
460+ }
461+ first_line = false;
462+ out.put(text)
463+ };
464+ let mut first_chapter = true;
465+ for chapter in out.tap(arena, noun)? {
466+ let (tako, proses) = arena.as_cell(chapter).ok_or(Fail::Bad)?;
467+ let proses = out.tap(arena, proses)?;
468+ if proses.is_empty() {
469+ continue;
470+ }
471+ if !first_chapter {
472+ line(out, b"---")?;
473+ }
474+ first_chapter = false;
475+ let tako = other_aura(arena, tako, aura::uv)?;
476+ line(out, format!("commit: {tako}").as_bytes())?;
477+ for (i, prose) in proses.into_iter().enumerate() {
478+ if i > 0 {
479+ line(out, b"|||")?;
480+ }
481+ let (title, body) = arena.as_cell(prose).ok_or(Fail::Bad)?;
482+ let title = arena.atom_bytes(title).ok_or(Fail::Bad)?;
483+ let body = arena.atom_bytes(body).ok_or(Fail::Bad)?;
484+ // `(rap 3 title '\0a\0a' body ~)`
485+ if title.len() + body.len() > MAX_FILE_SIZE {
486+ return Err(Fail::TooLarge);
487+ }
488+ let text = [title, b"\n\n", body].concat();
489+ for part in to_wain(&text) {
490+ line(out, part)?;
491+ }
492+ }
493+ }
494+ Ok(())
495+}
496+
497+/// `%kelvin`: a `waft` (one `[lal num]` weft, or `[[%1 ~] (set weft)]`),
498+/// printed one `[%lal num]` per line, highest first.
499+fn kelvin(arena: &Arena, noun: Noun, out: &mut Out) -> Result<(), Fail> {
500+ let (head, tail) = arena.as_cell(noun).ok_or(Fail::Bad)?;
501+ let wefts = if arena.is_atom(head) {
502+ vec![noun]
503+ } else {
504+ out.tap(arena, tail)?
505+ };
506+ let mut wefts = wefts
507+ .into_iter()
508+ .map(|weft| {
509+ let (lal, num) = arena.as_cell(weft).ok_or(Fail::Bad)?;
510+ arena.atom_bytes(lal).ok_or(Fail::Bad)?;
511+ arena.atom_bytes(num).ok_or(Fail::Bad)?;
512+ Ok((lal, num))
513+ })
514+ .collect::<Result<Vec<_>, Fail>>()?;
515+ let order = |a: Noun, b: Noun| {
516+ if a == b {
517+ std::cmp::Ordering::Equal
518+ } else {
519+ cmp_atoms(arena.atom_bytes(a).unwrap(), arena.atom_bytes(b).unwrap())
520+ }
521+ };
522+ wefts.sort_by(|a, b| order(b.0, a.0).then_with(|| order(b.1, a.1)));
523+ // Many wefts can share one number; print each number once.
524+ let mut printed: HashMap<Noun, String> = HashMap::new();
525+ for (lal, num) in wefts {
526+ let num = match printed.get(&num) {
527+ Some(text) => text,
528+ None => printed.entry(num).or_insert(ud(arena, num)?),
529+ };
530+ out.put(b"[%")?;
531+ out.atom(arena, lal)?;
532+ out.put(b" ")?;
533+ out.put(num.as_bytes())?;
534+ out.put(b"]\n")?;
535+ }
536+ Ok(())
537+}
538+
539+/// `%bill`: a list of agent names, printed as a `:~` list.
540+fn bill(arena: &Arena, noun: Noun, out: &mut Out) -> Result<(), Fail> {
541+ let dudes = out.list(arena, noun)?;
542+ if dudes.is_empty() {
543+ return out.put(b"~\n");
544+ }
545+ for (i, dude) in dudes.into_iter().enumerate() {
546+ out.put(if i == 0 { b":~ %" } else { b" %" })?;
547+ out.atom(arena, dude)?;
548+ out.put(b"\n")?;
549+ }
550+ out.put(b"==\n")
551+}
552+
553+/// `%docket-0`: landscape's app metadata, printed by `spit-docket`.
554+fn docket(arena: &Arena, noun: Noun, out: &mut Out) -> Result<(), Fail> {
555+ let fields = tuple(arena, noun, 9).ok_or(Fail::Bad)?;
556+ let [
557+ tag,
558+ title,
559+ info,
560+ color,
561+ href,
562+ image,
563+ version,
564+ website,
565+ license,
566+ ] = fields[..]
567+ else {
568+ return Err(Fail::Bad);
569+ };
570+ if arena.as_u64(tag) != Some(1) {
571+ return Err(Fail::Bad);
572+ }
573+ let [major, minor, patch] = tuple(arena, version, 3).ok_or(Fail::Bad)?[..] else {
574+ return Err(Fail::Bad);
575+ };
576+ let (kind, rest) = arena.as_cell(href).ok_or(Fail::Bad)?;
577+ let quoted = |out: &mut Out, key: &str, value: Noun| {
578+ out.put(format!("\n {key}+'").as_bytes())?;
579+ out.atom(arena, value)?;
580+ out.put(b"'")
581+ };
582+
583+ out.put(b":~")?;
584+ quoted(out, "title", title)?;
585+ quoted(out, "info", info)?;
586+ let color = other_aura(arena, color, aura::ux)?;
587+ out.put(format!("\n color+{color}").as_bytes())?;
588+ let version = format!(
589+ "\n version+[{} {} {}]",
590+ ud(arena, major)?,
591+ ud(arena, minor)?,
592+ ud(arena, patch)?
593+ );
594+ out.put(version.as_bytes())?;
595+ quoted(out, "website", website)?;
596+ quoted(out, "license", license)?;
597+ // image: (unit url)
598+ if !arena.is_cord(image, "") {
599+ let (null, url) = arena.as_cell(image).ok_or(Fail::Bad)?;
600+ if !arena.is_cord(null, "") {
601+ return Err(Fail::Bad);
602+ }
603+ quoted(out, "image", url)?;
604+ }
605+ if arena.is_cord(kind, "site") {
606+ out.put(b"\n site+")?;
607+ spud(arena, rest, out)?;
608+ } else if arena.is_cord(kind, "glob") {
609+ let (base, reference) = arena.as_cell(rest).ok_or(Fail::Bad)?;
610+ let (hash, location) = arena.as_cell(reference).ok_or(Fail::Bad)?;
611+ let (how, place) = arena.as_cell(location).ok_or(Fail::Bad)?;
612+ let hash = other_aura(arena, hash, aura::uv)?;
613+ quoted(out, "base", base)?;
614+ if arena.is_cord(how, "http") {
615+ out.put(b"\n glob-http+['")?;
616+ out.atom(arena, place)?;
617+ out.put(format!("' {hash}]").as_bytes())?;
618+ } else if arena.is_cord(how, "ames") {
619+ let ship = other_aura(arena, place, aura::p)?;
620+ out.put(format!("\n glob-ames+[{ship} {hash}]").as_bytes())?;
621+ } else {
622+ return Err(Fail::Bad);
623+ }
624+ } else {
625+ return Err(Fail::Bad);
626+ }
627+ out.put(b"\n==")
628+}
629+
630+/// Split a noun into an `n`-tuple `[a b ... z]`.
631+fn tuple(arena: &Arena, mut noun: Noun, n: usize) -> Option<Vec<Noun>> {
632+ let mut items = Vec::with_capacity(n);
633+ for _ in 1..n {
634+ let (head, tail) = arena.as_cell(noun)?;
635+ items.push(head);
636+ noun = tail;
637+ }
638+ items.push(noun);
639+ Some(items)
640+}
641+
642+/// `spud`: render a path as `/a/b/c` (`/` for the empty path).
643+fn spud(arena: &Arena, path: Noun, out: &mut Out) -> Result<(), Fail> {
644+ let segments = out.list(arena, path)?;
645+ if segments.is_empty() {
646+ return out.put(b"/");
647+ }
648+ for segment in segments {
649+ out.put(b"/")?;
650+ out.atom(arena, segment)?;
651+ }
652+ Ok(())
653+}
654+
655+/// `%json`: printed by `en:json:html`.
656+fn json(arena: &Arena, noun: Noun, out: &mut Out) -> Result<(), Fail> {
657+ enum Task {
658+ Value(Noun),
659+ Text(&'static [u8]),
660+ Key(Noun),
661+ }
662+ let mut tasks = vec![Task::Value(noun)];
663+ while let Some(task) = tasks.pop() {
664+ let value = match task {
665+ Task::Text(text) => {
666+ out.put(text)?;
667+ continue;
668+ }
669+ Task::Key(key) => {
670+ json_string(out, arena.atom_bytes(key).ok_or(Fail::Bad)?)?;
671+ continue;
672+ }
673+ Task::Value(value) => value,
674+ };
675+ out.visits += 1;
676+ let Some((tag, body)) = arena.as_cell(value) else {
677+ // `~` is null; no other atom is valid json.
678+ if arena.is_cord(value, "") {
679+ out.put(b"null")?;
680+ continue;
681+ }
682+ return Err(Fail::Bad);
683+ };
684+ match arena.atom_bytes(tag).ok_or(Fail::Bad)? {
685+ b"a" => {
686+ let items = out.list(arena, body)?;
687+ if items.is_empty() {
688+ out.put(b"[]")?;
689+ continue;
690+ }
691+ out.put(b"[")?;
692+ tasks.push(Task::Text(b"]"));
693+ for (i, &item) in items.iter().enumerate().rev() {
694+ tasks.push(Task::Value(item));
695+ if i > 0 {
696+ tasks.push(Task::Text(b","));
697+ }
698+ }
699+ }
700+ b"b" => match arena.as_u64(body) {
701+ Some(0) => out.put(b"true")?,
702+ Some(1) => out.put(b"false")?,
703+ _ => return Err(Fail::Bad),
704+ },
705+ b"n" => out.atom(arena, body)?,
706+ b"s" => json_string(out, arena.atom_bytes(body).ok_or(Fail::Bad)?)?,
707+ b"o" => {
708+ // `~(tap by map)` order: right subtree, node, left subtree.
709+ let entries = out.tap(arena, body)?;
710+ if entries.is_empty() {
711+ out.put(b"{}")?;
712+ continue;
713+ }
714+ out.put(b"{")?;
715+ tasks.push(Task::Text(b"}"));
716+ for (i, &entry) in entries.iter().enumerate().rev() {
717+ let (key, item) = arena.as_cell(entry).ok_or(Fail::Bad)?;
718+ tasks.push(Task::Value(item));
719+ tasks.push(Task::Text(b":"));
720+ tasks.push(Task::Key(key));
721+ if i > 0 {
722+ tasks.push(Task::Text(b","));
723+ }
724+ }
725+ }
726+ _ => return Err(Fail::Bad),
727+ }
728+ }
729+ Ok(())
730+}
731+
732+/// `scap:en:json:html`: a JSON string literal. Bytes above 0x1f pass through
733+/// untouched (so UTF-8 stays UTF-8); newline, quote and backslash get short
734+/// escapes and other control bytes `\u00xx`.
735+fn json_string(out: &mut Out, text: &[u8]) -> Result<(), Fail> {
736+ out.put(b"\"")?;
737+ // Escaping at most sextuples a byte; check the budget per chunk.
738+ for chunk in text.chunks(1 << 16) {
739+ let mut escaped = Vec::with_capacity(chunk.len() + 16);
740+ for &byte in chunk {
741+ match byte {
742+ b'\n' => escaped.extend_from_slice(b"\\n"),
743+ b'"' => escaped.extend_from_slice(b"\\\""),
744+ b'\\' => escaped.extend_from_slice(b"\\\\"),
745+ 0..=0x1f | 0x7f => escaped.extend_from_slice(format!("\\u{byte:04x}").as_bytes()),
746+ _ => escaped.push(byte),
747+ }
748+ }
749+ out.put(&escaped)?;
750+ }
751+ out.put(b"\"")
752+}
jammer/src/mug.rsnew file+126
@@ -0,0 +1,126 @@
1+//! Hoon's `mug` (31-bit murmur3-based noun hash) and the `gor`/`mor`
2+//! orderings derived from it, which determine the shape of Hoon maps and sets.
3+
4+use std::cmp::Ordering;
5+
6+use crate::noun::{Arena, Noun, View, trim};
7+
8+/// MurmurHash3 x86 32-bit (Hoon's `muk`, vere's `MurmurHash3_x86_32`).
9+pub fn murmur3_32(data: &[u8], seed: u32) -> u32 {
10+ const C1: u32 = 0xcc9e_2d51;
11+ const C2: u32 = 0x1b87_3593;
12+ let mut h = seed;
13+ let (blocks, tail) = data.as_chunks::<4>();
14+ for block in blocks {
15+ let mut k = u32::from_le_bytes(*block);
16+ k = k.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2);
17+ h ^= k;
18+ h = h.rotate_left(13).wrapping_mul(5).wrapping_add(0xe654_6b64);
19+ }
20+ if !tail.is_empty() {
21+ let mut k = 0u32;
22+ for (i, &byte) in tail.iter().enumerate() {
23+ k |= (byte as u32) << (8 * i);
24+ }
25+ k = k.wrapping_mul(C1).rotate_left(15).wrapping_mul(C2);
26+ h ^= k;
27+ }
28+ h ^= data.len() as u32;
29+ h ^= h >> 16;
30+ h = h.wrapping_mul(0x85eb_ca6b);
31+ h ^= h >> 13;
32+ h = h.wrapping_mul(0xc2b2_ae35);
33+ h ^= h >> 16;
34+ h
35+}
36+
37+/// Hoon's `mum`: fold murmur3 to 31 bits, retrying with the next seed while
38+/// the result is zero, and fall back to `fal` after eight attempts.
39+fn mum(seed: u32, fal: u32, key: &[u8]) -> u32 {
40+ for i in 0..8 {
41+ let haz = murmur3_32(key, seed.wrapping_add(i));
42+ let ham = (haz >> 31) ^ (haz & 0x7fff_ffff);
43+ if ham != 0 {
44+ return ham;
45+ }
46+ }
47+ fal
48+}
49+
50+/// `mug` of an atom given its little-endian bytes.
51+pub fn mug_atom(bytes: &[u8]) -> u32 {
52+ mum(0xcafe_babe, 0x7fff, trim(bytes))
53+}
54+
55+/// `mug` of a cell given the mugs of its head and tail:
56+/// `(mum 0xdead.beef 0xfffe (cat 5 head tail))`.
57+pub fn mug_cell(head: u32, tail: u32) -> u32 {
58+ let both = (head as u64) | ((tail as u64) << 32);
59+ mum(0xdead_beef, 0xfffe, trim(&both.to_le_bytes()))
60+}
61+
62+impl Arena {
63+ /// Hoon's `mug`, memoized per node.
64+ pub fn mug(&self, noun: Noun) -> u32 {
65+ let cached = self.mugs[noun.index()].get();
66+ if cached != 0 {
67+ return cached;
68+ }
69+ let mut stack = vec![noun];
70+ while let Some(&top) = stack.last() {
71+ if self.mugs[top.index()].get() != 0 {
72+ stack.pop();
73+ continue;
74+ }
75+ let mug = match self.view(top) {
76+ View::Atom(bytes) => mug_atom(bytes),
77+ View::Cell(h, t) => {
78+ let (mh, mt) = (self.mugs[h.index()].get(), self.mugs[t.index()].get());
79+ if mh == 0 || mt == 0 {
80+ if mh == 0 {
81+ stack.push(h);
82+ }
83+ if mt == 0 {
84+ stack.push(t);
85+ }
86+ continue;
87+ }
88+ mug_cell(mh, mt)
89+ }
90+ };
91+ self.mugs[top.index()].set(mug);
92+ stack.pop();
93+ }
94+ self.mugs[noun.index()].get()
95+ }
96+
97+ /// `mug` of the atom whose value is `(mug noun)`; the priority used by `mor`.
98+ fn double_mug(&self, noun: Noun) -> u32 {
99+ mug_atom(&self.mug(noun).to_le_bytes())
100+ }
101+
102+ /// Hoon's `gor`: order by mug, ties broken by `dor`. This is the key order
103+ /// of map and set treaps (the left subtree holds `gor`-smaller keys).
104+ pub fn gor(&self, a: Noun, b: Noun) -> bool {
105+ let (c, d) = (self.mug(a), self.mug(b));
106+ if c == d { self.dor(a, b) } else { c < d }
107+ }
108+
109+ /// Hoon's `mor`: order by double mug, ties broken by `dor`. This is the
110+ /// heap order of treaps (a node is `mor`-before its children).
111+ pub fn mor(&self, a: Noun, b: Noun) -> bool {
112+ let (c, d) = (self.double_mug(a), self.double_mug(b));
113+ if c == d { self.dor(a, b) } else { c < d }
114+ }
115+
116+ /// `gor` as a total order, for sorting.
117+ pub fn gor_cmp(&self, a: Noun, b: Noun) -> Ordering {
118+ if a == b {
119+ Ordering::Equal
120+ } else if self.gor(a, b) {
121+ Ordering::Less
122+ } else {
123+ Ordering::Greater
124+ }
125+ }
126+}
jammer/src/noun.rsnew file+246
@@ -0,0 +1,246 @@
1+//! Nouns stored in a hash-consed arena.
2+//!
3+//! Every structurally distinct noun is stored exactly once, so two [`Noun`]
4+//! handles from the same [`Arena`] are equal iff the nouns they denote are
5+//! equal. That makes jam's backreference table, Hoon's `dor` ordering and
6+//! duplicate detection O(1) per node, and lets every traversal be iterative:
7+//! desk files decode to lists with hundreds of thousands of elements, so
8+//! nothing here may recurse on noun depth.
9+
10+use std::cell::Cell;
11+use std::cmp::Ordering;
12+use std::collections::HashMap;
13+use std::collections::hash_map::Entry;
14+use std::rc::Rc;
15+
16+/// Handle to a noun inside an [`Arena`].
17+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
18+pub struct Noun(u32);
19+
20+/// The atom `0` (`~`), which every arena stores first.
21+pub const NULL: Noun = Noun(0);
22+
23+impl Noun {
24+ pub(crate) fn index(self) -> usize {
25+ self.0 as usize
26+ }
27+}
28+
29+/// Borrowed view of a noun.
30+#[derive(Copy, Clone, Debug)]
31+pub enum View<'a> {
32+ /// Little-endian bytes of the atom, without trailing zero bytes
33+ /// (so the atom `0` is the empty slice).
34+ Atom(&'a [u8]),
35+ Cell(Noun, Noun),
36+}
37+
38+enum Node {
39+ Atom(Rc<[u8]>),
40+ Cell(Noun, Noun),
41+}
42+
43+/// Storage for nouns. Nouns are never freed individually; drop the arena.
44+pub struct Arena {
45+ nodes: Vec<Node>,
46+ /// Memoized `mug` per node, 0 = not computed yet (a mug is never 0).
47+ pub(crate) mugs: Vec<Cell<u32>>,
48+ atoms: HashMap<Rc<[u8]>, Noun>,
49+ cells: HashMap<(Noun, Noun), Noun>,
50+}
51+
52+impl Default for Arena {
53+ fn default() -> Self {
54+ Self::new()
55+ }
56+}
57+
58+impl Arena {
59+ pub fn new() -> Self {
60+ let mut arena = Arena {
61+ nodes: Vec::new(),
62+ mugs: Vec::new(),
63+ atoms: HashMap::new(),
64+ cells: HashMap::new(),
65+ };
66+ // `~` is everywhere; give it a fixed handle.
67+ let null: Rc<[u8]> = Rc::from(&[][..]);
68+ let n = arena.push(Node::Atom(null.clone()));
69+ arena.atoms.insert(null, n);
70+ arena
71+ }
72+
73+ /// Number of distinct nouns stored.
74+ pub fn len(&self) -> usize {
75+ self.nodes.len()
76+ }
77+
78+ pub fn is_empty(&self) -> bool {
79+ self.nodes.is_empty()
80+ }
81+
82+ fn push(&mut self, node: Node) -> Noun {
83+ let id = u32::try_from(self.nodes.len()).expect("noun arena exhausted");
84+ self.nodes.push(node);
85+ self.mugs.push(Cell::new(0));
86+ Noun(id)
87+ }
88+
89+ /// The atom whose little-endian bytes are `bytes` (trailing zero bytes
90+ /// are insignificant, as in `as-octs`/`met`).
91+ pub fn atom(&mut self, bytes: &[u8]) -> Noun {
92+ let bytes = trim(bytes);
93+ if bytes.is_empty() {
94+ return NULL;
95+ }
96+ if let Some(&n) = self.atoms.get(bytes) {
97+ return n;
98+ }
99+ let rc: Rc<[u8]> = Rc::from(bytes);
100+ let n = self.push(Node::Atom(rc.clone()));
101+ self.atoms.insert(rc, n);
102+ n
103+ }
104+
105+ pub fn atom_u64(&mut self, value: u64) -> Noun {
106+ self.atom(&value.to_le_bytes())
107+ }
108+
109+ /// A cord (`@t`): the UTF-8 bytes of `text` as a little-endian atom.
110+ pub fn cord(&mut self, text: &str) -> Noun {
111+ self.atom(text.as_bytes())
112+ }
113+
114+ pub fn cell(&mut self, head: Noun, tail: Noun) -> Noun {
115+ let next = self.nodes.len();
116+ match self.cells.entry((head, tail)) {
117+ Entry::Occupied(entry) => *entry.get(),
118+ Entry::Vacant(entry) => {
119+ let n = Noun(u32::try_from(next).expect("noun arena exhausted"));
120+ entry.insert(n);
121+ self.nodes.push(Node::Cell(head, tail));
122+ self.mugs.push(Cell::new(0));
123+ n
124+ }
125+ }
126+ }
127+
128+ /// A null-terminated list `~[a b c]`.
129+ pub fn list(&mut self, items: &[Noun]) -> Noun {
130+ let mut list = self.atom(&[]);
131+ for &item in items.iter().rev() {
132+ list = self.cell(item, list);
133+ }
134+ list
135+ }
136+
137+ /// A path (`(list @ta)`) from its segments.
138+ pub fn path<S: AsRef<str>>(&mut self, segments: &[S]) -> Noun {
139+ let items: Vec<Noun> = segments.iter().map(|s| self.cord(s.as_ref())).collect();
140+ self.list(&items)
141+ }
142+
143+ pub fn view(&self, noun: Noun) -> View<'_> {
144+ match &self.nodes[noun.index()] {
145+ Node::Atom(bytes) => View::Atom(bytes),
146+ Node::Cell(h, t) => View::Cell(*h, *t),
147+ }
148+ }
149+
150+ pub fn atom_bytes(&self, noun: Noun) -> Option<&[u8]> {
151+ match self.view(noun) {
152+ View::Atom(bytes) => Some(bytes),
153+ View::Cell(..) => None,
154+ }
155+ }
156+
157+ pub fn as_cell(&self, noun: Noun) -> Option<(Noun, Noun)> {
158+ match self.view(noun) {
159+ View::Atom(_) => None,
160+ View::Cell(h, t) => Some((h, t)),
161+ }
162+ }
163+
164+ pub fn is_atom(&self, noun: Noun) -> bool {
165+ matches!(self.view(noun), View::Atom(_))
166+ }
167+
168+ /// The atom's value if it is an atom that fits in a `u64`.
169+ pub fn as_u64(&self, noun: Noun) -> Option<u64> {
170+ let bytes = self.atom_bytes(noun)?;
171+ if bytes.len() > 8 {
172+ return None;
173+ }
174+ let mut buf = [0u8; 8];
175+ buf[..bytes.len()].copy_from_slice(bytes);
176+ Some(u64::from_le_bytes(buf))
177+ }
178+
179+ /// Whether `noun` is the atom spelled by `text` (e.g. a mark or tag).
180+ pub fn is_cord(&self, noun: Noun, text: &str) -> bool {
181+ self.atom_bytes(noun) == Some(trim(text.as_bytes()))
182+ }
183+
184+ /// Elements of a null-terminated list, or `None` if `noun` is not one.
185+ pub fn list_items(&self, mut noun: Noun) -> Option<Vec<Noun>> {
186+ let mut items = Vec::new();
187+ loop {
188+ match self.view(noun) {
189+ View::Atom([]) => return Some(items),
190+ View::Atom(_) => return None,
191+ View::Cell(h, t) => {
192+ items.push(h);
193+ noun = t;
194+ }
195+ }
196+ }
197+ }
198+
199+ /// Hoon's `dor`: total "tree order" on nouns (atoms before cells, atoms by
200+ /// value, cells by head then tail). Returns true when `a` sorts at or
201+ /// before `b`.
202+ pub fn dor(&self, mut a: Noun, mut b: Noun) -> bool {
203+ loop {
204+ if a == b {
205+ return true;
206+ }
207+ match (self.view(a), self.view(b)) {
208+ (View::Atom(x), View::Atom(y)) => return cmp_atoms(x, y) == Ordering::Less,
209+ (View::Atom(_), View::Cell(..)) => return true,
210+ (View::Cell(..), View::Atom(_)) => return false,
211+ (View::Cell(ah, at), View::Cell(bh, bt)) => {
212+ if ah == bh {
213+ a = at;
214+ b = bt;
215+ } else {
216+ a = ah;
217+ b = bh;
218+ }
219+ }
220+ }
221+ }
222+ }
223+}
224+
225+/// Strip insignificant (high) zero bytes from a little-endian atom.
226+pub fn trim(bytes: &[u8]) -> &[u8] {
227+ let len = bytes.iter().rposition(|&b| b != 0).map_or(0, |i| i + 1);
228+ &bytes[..len]
229+}
230+
231+/// Bit length of a little-endian atom (`met 0`).
232+pub fn met0(bytes: &[u8]) -> u64 {
233+ let bytes = trim(bytes);
234+ match bytes.last() {
235+ None => 0,
236+ Some(&top) => (bytes.len() as u64 - 1) * 8 + (8 - top.leading_zeros() as u64),
237+ }
238+}
239+
240+/// Numeric comparison of two little-endian atoms.
241+pub fn cmp_atoms(a: &[u8], b: &[u8]) -> Ordering {
242+ let (a, b) = (trim(a), trim(b));
243+ a.len()
244+ .cmp(&b.len())
245+ .then_with(|| a.iter().rev().cmp(b.iter().rev()))
246+}
jammer/src/output.rsnew file+228
@@ -0,0 +1,228 @@
1+//! Writing unpacked files without ever leaving the output directory.
2+//!
3+//! Paths come from untrusted deskjam files, and the output directory may be
4+//! shared with other users, so every component is checked to be a plain
5+//! name and nothing is followed through a symlink. On Unix each step is
6+//! taken relative to an open handle of the directory verified in the
7+//! previous step (`openat` with `O_NOFOLLOW`), so the checks cannot be raced.
8+
9+use std::collections::HashSet;
10+use std::fmt;
11+use std::fs;
12+use std::io::{self, Write};
13+use std::path::{Path, PathBuf};
14+
15+/// Whether `name` is safe to use as a single path component: a plain name
16+/// that cannot climb out of its directory, with no control characters (and,
17+/// on Windows, none of the names and characters Windows reserves).
18+pub fn is_safe_component(name: &str) -> bool {
19+ !name.is_empty()
20+ && name != "."
21+ && name != ".."
22+ && !name.contains(['/', '\\'])
23+ && !name.chars().any(char::is_control)
24+ && (cfg!(unix) || is_portable(name))
25+}
26+
27+/// Windows rejects these characters, trailing dots and spaces, and device
28+/// names such as `aux` or `com1.txt`.
29+fn is_portable(name: &str) -> bool {
30+ const DEVICES: &[&str] = &[
31+ "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8",
32+ "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9",
33+ ];
34+ let stem = name.split('.').next().unwrap_or(name).to_ascii_lowercase();
35+ !name.contains([':', '*', '?', '"', '<', '>', '|'])
36+ && !name.ends_with(['.', ' '])
37+ && !DEVICES.contains(&stem.trim_end())
38+}
39+
40+/// Why one file could not be written.
41+#[derive(Debug)]
42+pub enum WriteError {
43+ /// Something else is already where the file or one of its directories
44+ /// should go (a file written earlier in this run, a symlink, ...).
45+ Conflict(String),
46+ Io(io::Error),
47+}
48+
49+impl fmt::Display for WriteError {
50+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51+ match self {
52+ WriteError::Conflict(why) => f.write_str(why),
53+ WriteError::Io(e) => write!(f, "{e}"),
54+ }
55+ }
56+}
57+
58+impl std::error::Error for WriteError {}
59+
60+impl From<io::Error> for WriteError {
61+ fn from(e: io::Error) -> Self {
62+ WriteError::Io(e)
63+ }
64+}
65+
66+/// Writes files under one output directory.
67+pub struct Writer {
68+ root: PathBuf,
69+ overwrite: bool,
70+ #[cfg(unix)]
71+ root_fd: rustix::fd::OwnedFd,
72+ /// `(device, inode)` of every file written, so two paths that name the
73+ /// same file (as on case-insensitive filesystems) are caught.
74+ #[cfg_attr(not(unix), allow(dead_code))]
75+ written: HashSet<(u64, u64)>,
76+}
77+
78+impl Writer {
79+ /// Use `root` as the output directory: create it if missing, otherwise
80+ /// insist that it is a directory (a symlink to one is fine) and, unless
81+ /// `overwrite`, empty. With `overwrite`, existing files are replaced.
82+ pub fn new(root: &Path, overwrite: bool) -> io::Result<Writer> {
83+ match fs::metadata(root) {
84+ Err(e) if e.kind() == io::ErrorKind::NotFound => fs::create_dir_all(root)?,
85+ Err(e) => return Err(e),
86+ Ok(meta) if !meta.is_dir() => {
87+ return Err(io::Error::other("it exists and is not a directory"));
88+ }
89+ Ok(_) => {
90+ if !overwrite && fs::read_dir(root)?.next().is_some() {
91+ return Err(io::Error::other(
92+ "it is not empty (use --force to write into it anyway)",
93+ ));
94+ }
95+ }
96+ }
97+ Ok(Writer {
98+ root: root.to_path_buf(),
99+ overwrite,
100+ #[cfg(unix)]
101+ root_fd: {
102+ use rustix::fs::{CWD, Mode, OFlags, openat};
103+ openat(
104+ CWD,
105+ root,
106+ OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
107+ Mode::empty(),
108+ )?
109+ },
110+ written: HashSet::new(),
111+ })
112+ }
113+
114+ /// Write `contents` to `root/components...`, creating directories as
115+ /// needed. Returns the path written.
116+ pub fn write(&mut self, components: &[String], contents: &[u8]) -> Result<PathBuf, WriteError> {
117+ let (name, dirs) = components
118+ .split_last()
119+ .ok_or_else(|| WriteError::Conflict("empty path".into()))?;
120+ if let Some(bad) = components.iter().find(|c| !is_safe_component(c)) {
121+ return Err(WriteError::Conflict(format!(
122+ "{} cannot be a file name",
123+ crate::text::printable(bad.as_bytes())
124+ )));
125+ }
126+ let path: PathBuf = std::iter::once(self.root.as_path())
127+ .chain(components.iter().map(Path::new))
128+ .collect();
129+ let mut file = self.open(dirs, name, &path)?;
130+ file.write_all(contents)?;
131+ Ok(path)
132+ }
133+
134+ #[cfg(unix)]
135+ fn open(&mut self, dirs: &[String], name: &str, path: &Path) -> Result<fs::File, WriteError> {
136+ use rustix::fs::{FileType, Mode, OFlags, fstat, ftruncate, mkdirat, openat};
137+ use rustix::io::Errno;
138+
139+ let mut dir: Option<rustix::fd::OwnedFd> = None;
140+ let mut at = self.root.clone();
141+ for component in dirs {
142+ at.push(component);
143+ let parent = dir.as_ref().unwrap_or(&self.root_fd);
144+ match mkdirat(parent, component.as_str(), Mode::from_raw_mode(0o777)) {
145+ Ok(()) | Err(Errno::EXIST) => {}
146+ Err(e) => return Err(WriteError::Io(e.into())),
147+ }
148+ let flags = OFlags::RDONLY | OFlags::DIRECTORY | OFlags::NOFOLLOW | OFlags::CLOEXEC;
149+ dir = Some(
150+ match openat(parent, component.as_str(), flags, Mode::empty()) {
151+ Ok(fd) => fd,
152+ Err(Errno::NOTDIR | Errno::LOOP) => {
153+ return Err(conflict(&at, "exists and is not a directory"));
154+ }
155+ Err(e) => return Err(WriteError::Io(e.into())),
156+ },
157+ );
158+ }
159+ let parent = dir.as_ref().unwrap_or(&self.root_fd);
160+ let mut flags = OFlags::WRONLY | OFlags::CREATE | OFlags::NOFOLLOW | OFlags::CLOEXEC;
161+ if !self.overwrite {
162+ flags |= OFlags::EXCL;
163+ }
164+ let fd = match openat(parent, name, flags, Mode::from_raw_mode(0o666)) {
165+ Ok(fd) => fd,
166+ Err(Errno::EXIST) => return Err(conflict(path, "was already written")),
167+ Err(Errno::LOOP | Errno::ISDIR) => {
168+ return Err(conflict(path, "exists and is not a regular file"));
169+ }
170+ Err(e) => return Err(WriteError::Io(e.into())),
171+ };
172+ let stat = fstat(&fd).map_err(io::Error::from)?;
173+ if !FileType::from_raw_mode(stat.st_mode).is_file() {
174+ return Err(conflict(path, "exists and is not a regular file"));
175+ }
176+ if !self
177+ .written
178+ .insert((stat.st_dev as u64, stat.st_ino as u64))
179+ {
180+ return Err(conflict(path, "was already written"));
181+ }
182+ if self.overwrite {
183+ ftruncate(&fd, 0).map_err(io::Error::from)?;
184+ }
185+ Ok(fs::File::from(fd))
186+ }
187+
188+ /// Without `openat`, check each step and accept the race.
189+ #[cfg(not(unix))]
190+ fn open(&mut self, dirs: &[String], _name: &str, path: &Path) -> Result<fs::File, WriteError> {
191+ let mut at = self.root.clone();
192+ for component in dirs {
193+ at.push(component);
194+ match fs::symlink_metadata(&at) {
195+ Ok(meta) if meta.is_dir() => {}
196+ Ok(_) => return Err(conflict(&at, "exists and is not a directory")),
197+ Err(e) if e.kind() == io::ErrorKind::NotFound => fs::create_dir(&at)?,
198+ Err(e) => return Err(e.into()),
199+ }
200+ }
201+ if self.overwrite {
202+ if let Ok(meta) = fs::symlink_metadata(path)
203+ && !meta.is_file()
204+ {
205+ return Err(conflict(path, "exists and is not a regular file"));
206+ }
207+ return Ok(fs::File::create(path)?);
208+ }
209+ match fs::OpenOptions::new()
210+ .write(true)
211+ .create_new(true)
212+ .open(path)
213+ {
214+ Ok(file) => Ok(file),
215+ Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
216+ Err(conflict(path, "was already written"))
217+ }
218+ Err(e) => Err(e.into()),
219+ }
220+ }
221+}
222+
223+fn conflict(path: &Path, why: &str) -> WriteError {
224+ WriteError::Conflict(format!(
225+ "{} {why}",
226+ crate::text::printable(path.to_string_lossy().as_bytes())
227+ ))
228+}
jammer/src/pack.rsnew file+379
@@ -0,0 +1,379 @@
1+//! Selecting a desk directory's files the way vere does when you `|commit`
2+//! a mounted desk (`_unix_update_dir` in vere's `io/unix.c`):
3+//!
4+//! - names starting with `.` are ignored, files and directories alike;
5+//! - symlinks are followed;
6+//! - a file is ignored unless its name contains a `.`, does not end in `~`,
7+//! and is a valid `@ta` (`a-z 0-9 - . _ ~`) as a whole;
8+//! - directory names are not checked;
9+//! - `dir/name.ext` becomes the clay path `/dir/name/ext`, splitting the
10+//! file name at its last `.`;
11+//! - one leading `!` is dropped from every path segment (and from a file
12+//! name before checking it), undoing the escape a desk mount writes for
13+//! segments like `..` (see [`crate::marks::unix_path`]).
14+//!
15+//! Where vere would produce a commit that clay rejects (an empty
16+//! extension), hang (a FIFO) or loop forever (a symlink cycle), the file is
17+//! skipped with a warning instead.
18+
19+use std::collections::{BTreeMap, BTreeSet, HashSet};
20+use std::fmt;
21+use std::fs;
22+use std::io;
23+use std::path::{Path, PathBuf};
24+
25+use crate::deskpack::SourceFile;
26+
27+/// Directories nested deeper than this are not entered (a backstop against
28+/// symlink cycles the device/inode check cannot see, such as on Windows).
29+const MAX_DEPTH: usize = 128;
30+
31+/// Why a directory entry was left out.
32+#[derive(Debug, Clone, PartialEq, Eq)]
33+pub enum SkipReason {
34+ /// The name starts with `.`.
35+ Hidden,
36+ /// The file name has no `.`, so there is no mark for it.
37+ NoExtension,
38+ /// The file name ends in `~` (an editor backup).
39+ Backup,
40+ /// The file name is not a valid `@ta`.
41+ InvalidName,
42+ /// Another file has the same clay path (e.g. `!a.txt` and `a.txt`).
43+ SamePath(PathBuf),
44+ /// The output file of this very command.
45+ Output,
46+ /// The file name ends in `.`; clay would reject the whole desk.
47+ EmptyExtension,
48+ /// The name is not valid UTF-8.
49+ NotUtf8,
50+ /// A FIFO, socket or device.
51+ NotAFile,
52+ /// It could not be read (a dangling symlink, no permission, ...).
53+ Unreadable(String),
54+ /// A symlink to a directory that contains it, or absurdly deep nesting.
55+ SymlinkLoop,
56+}
57+
58+impl SkipReason {
59+ /// Whether to mention the skip even when not verbose: vere skips the
60+ /// first few kinds silently too, and they are common in repositories.
61+ pub fn is_surprising(&self) -> bool {
62+ !matches!(
63+ self,
64+ SkipReason::Hidden | SkipReason::NoExtension | SkipReason::Backup | SkipReason::Output
65+ )
66+ }
67+
68+ /// Whether the file was left out because something is wrong with it,
69+ /// rather than by the rules `|commit` follows. jam then exits non-zero.
70+ pub fn is_problem(&self) -> bool {
71+ matches!(
72+ self,
73+ SkipReason::EmptyExtension
74+ | SkipReason::NotUtf8
75+ | SkipReason::NotAFile
76+ | SkipReason::Unreadable(_)
77+ | SkipReason::SymlinkLoop
78+ )
79+ }
80+}
81+
82+impl fmt::Display for SkipReason {
83+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84+ match self {
85+ SkipReason::Hidden => f.write_str("hidden"),
86+ SkipReason::NoExtension => f.write_str("no file extension"),
87+ SkipReason::Backup => f.write_str("backup file"),
88+ SkipReason::InvalidName => {
89+ f.write_str("not a valid clay file name (allowed: a-z 0-9 - . _ ~)")
90+ }
91+ SkipReason::SamePath(kept) => write!(
92+ f,
93+ "it has the same clay path as {}",
94+ crate::text::printable(kept.to_string_lossy().as_bytes())
95+ ),
96+ SkipReason::Output => f.write_str("it is the output file"),
97+ SkipReason::EmptyExtension => f.write_str("empty file extension"),
98+ SkipReason::NotUtf8 => f.write_str("name is not valid UTF-8"),
99+ SkipReason::NotAFile => f.write_str("not a regular file"),
100+ SkipReason::Unreadable(why) => write!(f, "cannot read it: {why}"),
101+ SkipReason::SymlinkLoop => f.write_str("symlink loop"),
102+ }
103+ }
104+}
105+
106+pub struct Skipped {
107+ pub path: PathBuf,
108+ pub reason: SkipReason,
109+}
110+
111+pub struct Collected {
112+ /// Files sorted by clay path.
113+ pub files: Vec<SourceFile>,
114+ pub skipped: Vec<Skipped>,
115+ /// Directories whose names are not valid `@ta`s, with the number of
116+ /// files packed below each. `|commit` includes these too, but such
117+ /// paths are unusual.
118+ pub odd_directories: BTreeMap<PathBuf, usize>,
119+}
120+
121+/// vere's `_unix_string_to_knot`: drop one leading `!`.
122+fn unescape(name: &str) -> &str {
123+ name.strip_prefix('!').unwrap_or(name)
124+}
125+
126+/// Whether `name` is how a desk mount spells the segment it stands for
127+/// (vere's `_unix_knot_to_string`), as opposed to another spelling that
128+/// `|commit` reads the same way.
129+fn is_canonical(name: &str) -> bool {
130+ let knot = unescape(name);
131+ let escaped = matches!(knot, "" | "." | "..") || knot.starts_with('!');
132+ name.starts_with('!') == escaped
133+}
134+
135+/// Whether `text` is a valid `@ta` (knot): `[a-z0-9-._~]*`.
136+pub fn is_knot(text: &str) -> bool {
137+ text.bytes()
138+ .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b"-._~".contains(&b))
139+}
140+
141+/// Whether `text` is a valid `@tas` (term), as desk names must be:
142+/// a lowercase letter followed by lowercase letters, digits and hyphens.
143+pub fn is_term(text: &str) -> bool {
144+ let mut bytes = text.bytes();
145+ bytes.next().is_some_and(|b| b.is_ascii_lowercase())
146+ && bytes.all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-')
147+}
148+
149+#[cfg(unix)]
150+fn dir_id(meta: &fs::Metadata) -> Option<(u64, u64)> {
151+ use std::os::unix::fs::MetadataExt;
152+ Some((meta.dev(), meta.ino()))
153+}
154+
155+#[cfg(not(unix))]
156+fn dir_id(_meta: &fs::Metadata) -> Option<(u64, u64)> {
157+ None
158+}
159+
160+/// Collect the files under `dir` with their clay paths. `exclude` (a
161+/// canonical path) is never collected.
162+pub fn collect(dir: &Path, exclude: Option<&Path>) -> io::Result<Collected> {
163+ struct Pending {
164+ path: PathBuf,
165+ clay: Vec<String>,
166+ /// Directories from the root down to this one, to detect cycles.
167+ ancestors: Vec<(u64, u64)>,
168+ /// The outermost directory on the way here whose name is not a knot.
169+ odd: Option<PathBuf>,
170+ canonical: bool,
171+ }
172+ struct Found {
173+ file: SourceFile,
174+ path: PathBuf,
175+ odd: Option<PathBuf>,
176+ canonical: bool,
177+ }
178+ let mut found = Vec::new();
179+ let mut skipped = Vec::new();
180+ let root_meta = fs::metadata(dir)?;
181+ let mut pending = vec![Pending {
182+ path: dir.to_path_buf(),
183+ clay: Vec::new(),
184+ ancestors: dir_id(&root_meta).into_iter().collect(),
185+ odd: None,
186+ canonical: true,
187+ }];
188+ while let Some(current) = pending.pop() {
189+ let entries =
190+ fs::read_dir(&current.path).and_then(|entries| entries.collect::<io::Result<Vec<_>>>());
191+ let mut entries = match entries {
192+ Ok(entries) => entries,
193+ Err(e) if current.clay.is_empty() => {
194+ return Err(io::Error::new(
195+ e.kind(),
196+ format!("{}: {e}", current.path.display()),
197+ ));
198+ }
199+ Err(e) => {
200+ skipped.push(Skipped {
201+ path: current.path,
202+ reason: SkipReason::Unreadable(e.to_string()),
203+ });
204+ continue;
205+ }
206+ };
207+ entries.sort_by_key(|e| e.file_name());
208+ for entry in entries {
209+ let path = entry.path();
210+ let mut skip = |reason| {
211+ skipped.push(Skipped {
212+ path: path.clone(),
213+ reason,
214+ })
215+ };
216+ let name = entry.file_name();
217+ let Some(name) = name.to_str() else {
218+ skip(SkipReason::NotUtf8);
219+ continue;
220+ };
221+ if name.starts_with('.') {
222+ skip(SkipReason::Hidden);
223+ continue;
224+ }
225+ let meta = match fs::metadata(&path) {
226+ Ok(meta) => meta,
227+ Err(e) => {
228+ skip(SkipReason::Unreadable(e.to_string()));
229+ continue;
230+ }
231+ };
232+ if meta.is_dir() {
233+ let id = dir_id(&meta);
234+ if current.clay.len() >= MAX_DEPTH
235+ || id.is_some_and(|id| current.ancestors.contains(&id))
236+ {
237+ skip(SkipReason::SymlinkLoop);
238+ continue;
239+ }
240+ let mut clay = current.clay.clone();
241+ clay.push(unescape(name).to_owned());
242+ let mut ancestors = current.ancestors.clone();
243+ ancestors.extend(id);
244+ let odd = match &current.odd {
245+ Some(odd) => Some(odd.clone()),
246+ None if !is_knot(unescape(name)) => Some(path.clone()),
247+ None => None,
248+ };
249+ pending.push(Pending {
250+ path,
251+ clay,
252+ ancestors,
253+ odd,
254+ canonical: current.canonical && is_canonical(name),
255+ });
256+ continue;
257+ }
258+ let Some((stem, ext)) = name.rsplit_once('.') else {
259+ skip(SkipReason::NoExtension);
260+ continue;
261+ };
262+ if name.ends_with('~') {
263+ skip(SkipReason::Backup);
264+ continue;
265+ }
266+ if !is_knot(unescape(name)) {
267+ skip(SkipReason::InvalidName);
268+ continue;
269+ }
270+ if ext.is_empty() {
271+ skip(SkipReason::EmptyExtension);
272+ continue;
273+ }
274+ if !meta.is_file() {
275+ skip(SkipReason::NotAFile);
276+ continue;
277+ }
278+ if exclude.is_some_and(|ex| fs::canonicalize(&path).is_ok_and(|p| p == ex)) {
279+ skip(SkipReason::Output);
280+ continue;
281+ }
282+ let contents = match fs::read(&path) {
283+ Ok(contents) => contents,
284+ Err(e) => {
285+ skip(SkipReason::Unreadable(e.to_string()));
286+ continue;
287+ }
288+ };
289+ let mut clay = current.clay.clone();
290+ clay.push(unescape(stem).to_owned());
291+ clay.push(unescape(ext).to_owned());
292+ found.push(Found {
293+ file: SourceFile {
294+ path: clay,
295+ contents,
296+ },
297+ odd: current.odd.clone(),
298+ canonical: current.canonical && is_canonical(stem) && is_canonical(ext),
299+ path,
300+ });
301+ }
302+ }
303+
304+ // Two spellings of one clay path: keep the one a mount would write.
305+ found.sort_by(|a, b| a.file.path.cmp(&b.file.path));
306+ let mut files: Vec<SourceFile> = Vec::with_capacity(found.len());
307+ let mut odd_directories = BTreeMap::new();
308+ let mut group: Vec<Found> = Vec::new();
309+ let mut found = found.into_iter().peekable();
310+ while let Some(next) = found.next() {
311+ group.push(next);
312+ if found
313+ .peek()
314+ .is_some_and(|f| f.file.path == group[0].file.path)
315+ {
316+ continue;
317+ }
318+ let keep = group.iter().position(|f| f.canonical).unwrap_or(0);
319+ let kept = group.swap_remove(keep);
320+ for other in group.drain(..) {
321+ skipped.push(Skipped {
322+ path: other.path,
323+ reason: SkipReason::SamePath(kept.path.clone()),
324+ });
325+ }
326+ if let Some(odd) = kept.odd {
327+ *odd_directories.entry(odd).or_insert(0) += 1;
328+ }
329+ files.push(kept.file);
330+ }
331+ skipped.sort_by(|a, b| a.path.cmp(&b.path));
332+ Ok(Collected {
333+ files,
334+ skipped,
335+ odd_directories,
336+ })
337+}
338+
339+/// Files clay parses as Hoon (`sys.kelvin`, `desk.bill`, `desk.docket-0`)
340+/// that contain carriage returns or tabs, which Hoon's parser rejects: a
341+/// commit including them fails.
342+pub fn unparsable_config(files: &[SourceFile]) -> Vec<String> {
343+ files
344+ .iter()
345+ .filter(|f| {
346+ matches!(
347+ f.path.last().map(String::as_str),
348+ Some("kelvin" | "bill" | "docket-0")
349+ ) && f.contents.iter().any(|&b| b == b'\r' || b == b'\t')
350+ })
351+ .map(|f| f.path.join("/"))
352+ .collect()
353+}
354+
355+/// Marks used by `files` that have no definition among them. Clay builds
356+/// marks from the desk itself, so a new desk made from these files could not
357+/// convert them. Clay converts `%hoon`, `%mime` and `%noun` without a mark
358+/// file, and `%jam` with `mar/mime.hoon`.
359+pub fn missing_marks(files: &[SourceFile]) -> BTreeSet<String> {
360+ // `/mar/foo/bar/hoon` defines %foo-bar (as does `/mar/foo-bar/hoon`).
361+ let defined: HashSet<String> = files
362+ .iter()
363+ .filter_map(|f| match f.path.as_slice() {
364+ [mar, name @ .., ext] if mar == "mar" && ext == "hoon" && !name.is_empty() => {
365+ Some(name.join("-"))
366+ }
367+ _ => None,
368+ })
369+ .collect();
370+ let used: BTreeSet<&String> = files.iter().filter_map(|f| f.path.last()).collect();
371+ used.into_iter()
372+ .filter(|mark| !matches!(mark.as_str(), "hoon" | "mime" | "noun"))
373+ .filter(|mark| {
374+ !defined.contains(mark.as_str())
375+ && !(mark.as_str() == "jam" && defined.contains("mime"))
376+ })
377+ .cloned()
378+ .collect()
379+}
jammer/src/text.rsnew file+47
@@ -0,0 +1,47 @@
1+//! Printing untrusted names (from deskjam files or the filesystem).
2+
3+/// `bytes` as text that is safe to print to a terminal: control characters
4+/// and invisible formatting characters (bidi overrides, zero-width spaces)
5+/// are escaped, so hostile names cannot rewrite or disguise output.
6+pub fn printable(bytes: &[u8]) -> String {
7+ let mut out = String::new();
8+ for c in String::from_utf8_lossy(bytes).chars() {
9+ if c.is_control() || is_format(c) {
10+ out.extend(c.escape_unicode());
11+ } else {
12+ out.push(c);
13+ }
14+ }
15+ out
16+}
17+
18+/// Unicode format characters (general category Cf) that change how text is
19+/// displayed without being visible themselves.
20+fn is_format(c: char) -> bool {
21+ matches!(
22+ c,
23+ '\u{00ad}'
24+ | '\u{061c}'
25+ | '\u{180e}'
26+ | '\u{200b}'..='\u{200f}'
27+ | '\u{202a}'..='\u{202e}'
28+ | '\u{2060}'..='\u{206f}'
29+ | '\u{feff}'
30+ | '\u{fff9}'..='\u{fffb}'
31+ | '\u{e0001}'
32+ | '\u{e0020}'..='\u{e007f}'
33+ )
34+}
35+
36+#[cfg(test)]
37+mod tests {
38+ use super::printable;
39+
40+ #[test]
41+ fn escapes_terminal_tricks() {
42+ assert_eq!(printable(b"ok/name.txt"), "ok/name.txt");
43+ assert_eq!(printable(b"x\x1b[31m"), "x\\u{1b}[31m");
44+ assert_eq!(printable("a\u{202e}b".as_bytes()), "a\\u{202e}b");
45+ assert_eq!(printable(&[0x61, 0xff]), "a\u{fffd}");
46+ }
47+}
jammer/src/treap.rsnew file+190
@@ -0,0 +1,190 @@
1+//! Hoon maps and sets.
2+//!
3+//! Both are treaps: a binary search tree ordered by [`Arena::gor`] whose
4+//! nodes are heap-ordered by [`Arena::mor`]. A node is `[n=item l=tree r=tree]`
5+//! and the empty tree is `~`. Since both orders are total, the treap for a
6+//! given key set is unique, so building it directly produces exactly the noun
7+//! a ship builds with `put:by`/`gas:by` (and passes the `apt:by` check that
8+//! `;;` runs for map molds).
9+
10+use std::collections::HashSet;
11+use std::fmt;
12+
13+use crate::noun::{Arena, Noun, View};
14+
15+/// Build `(map k v)` from `[key value]` entries, like `(malt entries)`.
16+/// If a key repeats, the last value wins (as with `gas:by`).
17+pub fn map_from_entries(arena: &mut Arena, entries: &[(Noun, Noun)]) -> Noun {
18+ let items = entries
19+ .iter()
20+ .map(|&(key, value)| (key, arena.cell(key, value)))
21+ .collect();
22+ build(arena, items)
23+}
24+
25+/// Build `(set item)` from members, like `(silt items)`.
26+pub fn set_from_items(arena: &mut Arena, items: &[Noun]) -> Noun {
27+ build(arena, items.iter().map(|&item| (item, item)).collect())
28+}
29+
30+/// `items` are `(key, node item)` pairs.
31+fn build(arena: &mut Arena, mut items: Vec<(Noun, Noun)>) -> Noun {
32+ // Stable sort, then keep the last of each run of equal keys.
33+ items.sort_by(|a, b| arena.gor_cmp(a.0, b.0));
34+ let mut unique: Vec<(Noun, Noun)> = Vec::with_capacity(items.len());
35+ for item in items {
36+ match unique.last_mut() {
37+ Some(last) if last.0 == item.0 => *last = item,
38+ _ => unique.push(item),
39+ }
40+ }
41+
42+ // Cartesian tree over the gor-sorted keys, heap-ordered by mor.
43+ const NONE: usize = usize::MAX;
44+ let mut left = vec![NONE; unique.len()];
45+ let mut right = vec![NONE; unique.len()];
46+ let mut spine: Vec<usize> = Vec::new();
47+ for i in 0..unique.len() {
48+ let mut last = NONE;
49+ while let Some(&top) = spine.last() {
50+ if !arena.mor(unique[i].0, unique[top].0) {
51+ break;
52+ }
53+ last = top;
54+ spine.pop();
55+ }
56+ left[i] = last;
57+ if let Some(&top) = spine.last() {
58+ right[top] = i;
59+ }
60+ spine.push(i);
61+ }
62+
63+ // Emit nouns bottom-up (post-order, explicit stack).
64+ let root = spine.first().copied().unwrap_or(NONE);
65+ let null = arena.atom(&[]);
66+ let mut built = vec![null; unique.len()];
67+ let mut stack = vec![(root, false)];
68+ while let Some((i, children_done)) = stack.pop() {
69+ if i == NONE {
70+ continue;
71+ }
72+ if !children_done {
73+ stack.push((i, true));
74+ stack.push((left[i], false));
75+ stack.push((right[i], false));
76+ continue;
77+ }
78+ let l = if left[i] == NONE {
79+ null
80+ } else {
81+ built[left[i]]
82+ };
83+ let r = if right[i] == NONE {
84+ null
85+ } else {
86+ built[right[i]]
87+ };
88+ let children = arena.cell(l, r);
89+ built[i] = arena.cell(unique[i].1, children);
90+ }
91+ if root == NONE { null } else { built[root] }
92+}
93+
94+/// A noun that is not shaped like a tree.
95+#[derive(Debug, Clone, PartialEq, Eq)]
96+pub enum TreeError {
97+ /// A node is neither `~` nor `[n l r]`.
98+ Malformed,
99+ /// The same subtree occurs twice, so keys repeat (and naive traversal
100+ /// could take exponential time).
101+ SharedSubtree,
102+}
103+
104+impl fmt::Display for TreeError {
105+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106+ match self {
107+ TreeError::Malformed => write!(f, "not a well-formed map/set tree"),
108+ TreeError::SharedSubtree => write!(f, "map/set tree contains a repeated subtree"),
109+ }
110+ }
111+}
112+
113+impl std::error::Error for TreeError {}
114+
115+/// The node items (`n`) of a tree, in pre-order.
116+pub fn tree_items(arena: &Arena, tree: Noun) -> Result<Vec<Noun>, TreeError> {
117+ let mut items = Vec::new();
118+ let mut visited = HashSet::new();
119+ let mut stack = vec![tree];
120+ while let Some(node) = stack.pop() {
121+ match arena.view(node) {
122+ View::Atom([]) => {}
123+ View::Atom(_) => return Err(TreeError::Malformed),
124+ View::Cell(item, children) => {
125+ if !visited.insert(node) {
126+ return Err(TreeError::SharedSubtree);
127+ }
128+ let (l, r) = arena.as_cell(children).ok_or(TreeError::Malformed)?;
129+ items.push(item);
130+ stack.push(r);
131+ stack.push(l);
132+ }
133+ }
134+ }
135+ Ok(items)
136+}
137+
138+/// Hoon's `apt:by` / `apt:in`: whether a well-formed tree satisfies the treap
139+/// invariants. `key` extracts the ordering key from a node item (the head of
140+/// a map entry, or the item itself for sets). Returns false for malformed
141+/// trees.
142+pub fn is_valid_treap(arena: &Arena, tree: Noun, key: impl Fn(Noun) -> Option<Noun>) -> bool {
143+ // (node, upper bound from a left descent, lower bound from a right descent)
144+ let mut stack: Vec<(Noun, Option<Noun>, Option<Noun>)> = vec![(tree, None, None)];
145+ let node_key = |node: Noun| -> Option<(Noun, Noun, Noun)> {
146+ let (item, children) = arena.as_cell(node)?;
147+ let (l, r) = arena.as_cell(children)?;
148+ Some((key(item)?, l, r))
149+ };
150+ let is_null = |n: Noun| arena.atom_bytes(n) == Some(&[]);
151+ let mut visited = HashSet::new();
152+ while let Some((node, upper, lower)) = stack.pop() {
153+ if is_null(node) {
154+ continue;
155+ }
156+ if !visited.insert(node) {
157+ return false;
158+ }
159+ let Some((k, l, r)) = node_key(node) else {
160+ return false;
161+ };
162+ if let Some(u) = upper
163+ && (k == u || !arena.gor(k, u))
164+ {
165+ return false;
166+ }
167+ if let Some(w) = lower
168+ && (w == k || !arena.gor(w, k))
169+ {
170+ return false;
171+ }
172+ for (child, is_left) in [(l, true), (r, false)] {
173+ if is_null(child) {
174+ continue;
175+ }
176+ let Some((ck, _, _)) = node_key(child) else {
177+ return false;
178+ };
179+ if ck == k || !arena.mor(k, ck) {
180+ return false;
181+ }
182+ if is_left {
183+ stack.push((child, Some(k), lower));
184+ } else {
185+ stack.push((child, upper, Some(k)));
186+ }
187+ }
188+ }
189+ true
190+}
jammer/tests/aura.rsnew file+78
@@ -0,0 +1,78 @@
1+//! Vectors from `(scow %p ...)`, `(scow %ud ...)` etc. on a ship.
2+
3+use jammer::aura::{p, ud, uv, ux};
4+
5+fn le(hex: &str) -> Vec<u8> {
6+ let hex = if hex.len() % 2 == 1 {
7+ format!("0{hex}")
8+ } else {
9+ hex.to_string()
10+ };
11+ let mut bytes: Vec<u8> = (0..hex.len())
12+ .step_by(2)
13+ .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
14+ .collect();
15+ bytes.reverse();
16+ bytes
17+}
18+
19+#[test]
20+fn ships() {
21+ for (hex, name) in [
22+ ("0", "~zod"),
23+ ("1", "~nec"),
24+ ("ff", "~fes"),
25+ ("100", "~marzod"),
26+ ("101", "~marnec"),
27+ ("ffff", "~fipfes"),
28+ ("10000", "~dapnep-ronmyl"),
29+ ("10001", "~milrys-soglec"),
30+ ("7f123456", "~watrup-worrun"),
31+ ("ffffffff", "~dostec-risfen"),
32+ ("100000000", "~doznec-dozzod-dozzod"),
33+ ("7e710000deadbeef", "~mister-dozzod-biltyc-mitfyl"),
34+ ("7e71817100000000", "~mister-dister-dozzod-dozzod"),
35+ ("ffffffffffffffff", "~fipfes-fipfes-dostec-risfen"),
36+ ("10000000000000000", "~doznec--dozzod-dozzod-dozzod-dozzod"),
37+ (
38+ "deadbeefcafebabe123456789abcdef0",
39+ "~ribmug-hilmun-masnev-lodlev--doprut-posfel-tilbyt-riblyr",
40+ ),
41+ ("123456", "~figsut-fanrut"),
42+ ] {
43+ assert_eq!(p(&le(hex)), name, "@p of 0x{hex}");
44+ }
45+}
46+
47+#[test]
48+fn numbers() {
49+ for (hex, d, x, v) in [
50+ ("0", "0", "0x0", "0v0"),
51+ ("1", "1", "0x1", "0v1"),
52+ ("9", "9", "0x9", "0v9"),
53+ ("a", "10", "0xa", "0va"),
54+ ("1f", "31", "0x1f", "0vv"),
55+ ("20", "32", "0x20", "0v10"),
56+ ("3e7", "999", "0x3e7", "0vv7"),
57+ ("3e8", "1.000", "0x3e8", "0vv8"),
58+ ("400", "1.024", "0x400", "0v100"),
59+ ("75bcd15", "123.456.789", "0x75b.cd15", "0v3.lnj8l"),
60+ (
61+ "10000000000000000",
62+ "18.446.744.073.709.551.616",
63+ "0x1.0000.0000.0000.0000",
64+ "0vg00.00000.00000",
65+ ),
66+ (
67+ "10000000000000000000000000",
68+ "1.267.650.600.228.229.401.496.703.205.376",
69+ "0x10.0000.0000.0000.0000.0000.0000",
70+ "0v1.00000.00000.00000.00000",
71+ ),
72+ ] {
73+ let atom = le(hex);
74+ assert_eq!(ud(&atom), d);
75+ assert_eq!(ux(&atom), x);
76+ assert_eq!(uv(&atom), v);
77+ }
78+}
jammer/tests/cli.rsnew file+906
@@ -0,0 +1,906 @@
1+//! End-to-end tests of the `jammer` binary.
2+//!
3+//! `fixtures/jfix.deskjam` was exported by deskjam from a fake ship, and
4+//! `fixtures/jfix/` is what that ship wrote when the same desk was mounted
5+//! (`|mount %jfix`), so unjam must reproduce it byte for byte.
6+
7+use std::collections::BTreeMap;
8+use std::fs;
9+use std::io::Write;
10+use std::path::{Path, PathBuf};
11+use std::process::{Command, Output, Stdio};
12+
13+use jammer::deskpack::{SourceFile, decode, encode};
14+
15+fn jammer(args: &[&str]) -> Output {
16+ Command::new(env!("CARGO_BIN_EXE_jammer"))
17+ .args(args)
18+ .output()
19+ .unwrap()
20+}
21+
22+fn fixture(name: &str) -> PathBuf {
23+ Path::new(env!("CARGO_MANIFEST_DIR"))
24+ .join("tests/fixtures")
25+ .join(name)
26+}
27+
28+/// All files under `dir`, by relative path.
29+fn tree(dir: &Path) -> BTreeMap<String, Vec<u8>> {
30+ let mut files = BTreeMap::new();
31+ let mut pending = vec![dir.to_path_buf()];
32+ while let Some(d) = pending.pop() {
33+ for entry in fs::read_dir(&d).unwrap() {
34+ let path = entry.unwrap().path();
35+ if path.is_dir() {
36+ pending.push(path);
37+ } else {
38+ let rel = path
39+ .strip_prefix(dir)
40+ .unwrap()
41+ .to_string_lossy()
42+ .into_owned();
43+ files.insert(rel, fs::read(&path).unwrap());
44+ }
45+ }
46+ }
47+ files
48+}
49+
50+fn stderr(out: &Output) -> String {
51+ String::from_utf8_lossy(&out.stderr).into_owned()
52+}
53+
54+#[test]
55+fn unjam_reproduces_a_ship_mount() {
56+ let tmp = tempfile::tempdir().unwrap();
57+ let out_dir = tmp.path().join("out");
58+ let out = jammer(&[
59+ "unjam",
60+ fixture("jfix.deskjam").to_str().unwrap(),
61+ "-o",
62+ out_dir.to_str().unwrap(),
63+ ]);
64+ assert!(out.status.success(), "{}", stderr(&out));
65+ assert_eq!(tree(&out_dir), tree(&fixture("jfix")));
66+}
67+
68+#[test]
69+fn jam_then_unjam_round_trips() {
70+ let tmp = tempfile::tempdir().unwrap();
71+ let file = tmp.path().join("jfix.deskjam");
72+ let out = jammer(&[
73+ "jam",
74+ fixture("jfix").to_str().unwrap(),
75+ "-o",
76+ file.to_str().unwrap(),
77+ ]);
78+ assert!(out.status.success(), "{}", stderr(&out));
79+ let pack = decode(&fs::read(&file).unwrap()).unwrap();
80+ assert_eq!(pack.desk, b"jfix");
81+ assert!(pack.canonical_map);
82+ assert!(pack.pages.iter().all(|p| p.mark == b"mime"));
83+
84+ let out_dir = tmp.path().join("out");
85+ let out = jammer(&[
86+ "unjam",
87+ file.to_str().unwrap(),
88+ "-o",
89+ out_dir.to_str().unwrap(),
90+ ]);
91+ assert!(out.status.success(), "{}", stderr(&out));
92+ assert_eq!(tree(&out_dir), tree(&fixture("jfix")));
93+}
94+
95+#[test]
96+fn default_output_names() {
97+ let tmp = tempfile::tempdir().unwrap();
98+ let out = Command::new(env!("CARGO_BIN_EXE_jammer"))
99+ .args(["jam", fixture("jfix").to_str().unwrap()])
100+ .current_dir(tmp.path())
101+ .output()
102+ .unwrap();
103+ assert!(out.status.success(), "{}", stderr(&out));
104+ assert!(tmp.path().join("jfix.deskjam").is_file());
105+ let out = Command::new(env!("CARGO_BIN_EXE_jammer"))
106+ .args(["unjam", "jfix.deskjam"])
107+ .current_dir(tmp.path())
108+ .output()
109+ .unwrap();
110+ assert!(out.status.success(), "{}", stderr(&out));
111+ assert_eq!(tree(&tmp.path().join("jfix")), tree(&fixture("jfix")));
112+}
113+
114+#[test]
115+fn list_shows_marks_and_sizes() {
116+ let out = jammer(&["list", fixture("jfix.deskjam").to_str().unwrap()]);
117+ assert!(out.status.success());
118+ let text = String::from_utf8(out.stdout).unwrap();
119+ assert!(text.starts_with("desk %jfix (25 files)\n"), "{text}");
120+ assert!(
121+ text.contains(" 307 %docket-0 /desk/docket-0\n"),
122+ "{text}"
123+ );
124+ assert!(
125+ text.contains(" 24 %kelvin /sys/kelvin\n"),
126+ "{text}"
127+ );
128+}
129+
130+#[test]
131+fn unjam_refuses_to_clobber_without_force() {
132+ let tmp = tempfile::tempdir().unwrap();
133+ let out_dir = tmp.path().join("out");
134+ fs::create_dir(&out_dir).unwrap();
135+ fs::write(out_dir.join("keep.txt"), "mine").unwrap();
136+ let file = fixture("jfix.deskjam");
137+ let out = jammer(&[
138+ "unjam",
139+ file.to_str().unwrap(),
140+ "-o",
141+ out_dir.to_str().unwrap(),
142+ ]);
143+ assert!(!out.status.success());
144+ assert!(stderr(&out).contains("not empty"), "{}", stderr(&out));
145+ let out = jammer(&[
146+ "unjam",
147+ file.to_str().unwrap(),
148+ "-o",
149+ out_dir.to_str().unwrap(),
150+ "--force",
151+ ]);
152+ assert!(out.status.success(), "{}", stderr(&out));
153+ assert_eq!(fs::read(out_dir.join("keep.txt")).unwrap(), b"mine");
154+ assert!(out_dir.join("desk.docket-0").is_file());
155+}
156+
157+#[test]
158+fn unjam_never_leaves_the_output_directory() {
159+ let tmp = tempfile::tempdir().unwrap();
160+ let path = |segments: &[&str]| segments.iter().map(|s| s.to_string()).collect();
161+ let evil = encode(
162+ "evil",
163+ vec![
164+ SourceFile {
165+ path: path(&["..", "escape", "txt"]),
166+ contents: b"x".to_vec(),
167+ },
168+ SourceFile {
169+ path: path(&["a/b", "txt"]),
170+ contents: b"x".to_vec(),
171+ },
172+ SourceFile {
173+ path: path(&["", "txt"]),
174+ contents: b"x".to_vec(),
175+ },
176+ SourceFile {
177+ path: path(&["Upper", "txt"]),
178+ contents: b"x".to_vec(),
179+ },
180+ SourceFile {
181+ path: path(&["ok", "txt"]),
182+ contents: b"fine".to_vec(),
183+ },
184+ ],
185+ );
186+ let file = tmp.path().join("evil.deskjam");
187+ fs::write(&file, evil).unwrap();
188+ let out_dir = tmp.path().join("box").join("out");
189+ let out = jammer(&[
190+ "unjam",
191+ file.to_str().unwrap(),
192+ "-o",
193+ out_dir.to_str().unwrap(),
194+ ]);
195+ assert_eq!(out.status.code(), Some(3), "{}", stderr(&out));
196+ // Like a desk mount, `..` and the empty segment are escaped with `!`, and
197+ // `Upper` (not a valid @ta) is kept; only `a/b` cannot be a file name.
198+ assert_eq!(
199+ tree(&tmp.path().join("box")).keys().collect::<Vec<_>>(),
200+ [
201+ "out/!../escape.txt",
202+ "out/!.txt",
203+ "out/Upper.txt",
204+ "out/ok.txt"
205+ ]
206+ );
207+ assert!(!tmp.path().join("box/escape.txt").exists());
208+}
209+
210+#[test]
211+fn unjam_does_not_write_through_symlinks() {
212+ let tmp = tempfile::tempdir().unwrap();
213+ let outside = tmp.path().join("outside");
214+ fs::create_dir(&outside).unwrap();
215+ let out_dir = tmp.path().join("out");
216+ fs::create_dir(&out_dir).unwrap();
217+ #[cfg(unix)]
218+ std::os::unix::fs::symlink(&outside, out_dir.join("web")).unwrap();
219+ #[cfg(unix)]
220+ {
221+ let out = jammer(&[
222+ "unjam",
223+ fixture("jfix.deskjam").to_str().unwrap(),
224+ "-o",
225+ out_dir.to_str().unwrap(),
226+ "--force",
227+ ]);
228+ assert!(!out.status.success());
229+ assert!(fs::read_dir(&outside).unwrap().next().is_none());
230+ }
231+}
232+
233+/// The files vere picks up on `|commit`, as observed on a ship.
234+#[test]
235+fn jam_selects_files_like_commit() {
236+ let tmp = tempfile::tempdir().unwrap();
237+ let dir = tmp.path().join("rules");
238+ for sub in [".dotdir", "a.b", "sub/deep", "Upper", "emptydir"] {
239+ fs::create_dir_all(dir.join(sub)).unwrap();
240+ }
241+ for name in [
242+ "README",
243+ "README.md",
244+ ".hidden.txt",
245+ ".dotdir/x.txt",
246+ "foo.txt~",
247+ "foo~.txt",
248+ "foo.bar.txt",
249+ "a.b/c.txt",
250+ "foo.",
251+ "Foo.txt",
252+ "foo bar.txt",
253+ "under_score.txt",
254+ "sub/deep/x.txt",
255+ "Upper/x.txt",
256+ "x.TXT",
257+ "#emacs#.txt",
258+ "empty.txt",
259+ ] {
260+ fs::write(dir.join(name), if name == "empty.txt" { "" } else { "a" }).unwrap();
261+ }
262+ #[cfg(unix)]
263+ {
264+ std::os::unix::fs::symlink("foo.bar.txt", dir.join("link.txt")).unwrap();
265+ std::os::unix::fs::symlink("sub", dir.join("linkdir")).unwrap();
266+ std::os::unix::fs::symlink("..", dir.join("sub/deep/loop")).unwrap();
267+ std::os::unix::fs::symlink("nowhere", dir.join("dangling.txt")).unwrap();
268+ }
269+ let file = tmp.path().join("rules.deskjam");
270+ let out = jammer(&["jam", dir.to_str().unwrap(), "-o", file.to_str().unwrap()]);
271+ // `foo.` (and on Unix the dangling link and the loop) are problems.
272+ assert_eq!(out.status.code(), Some(3), "{}", stderr(&out));
273+ let pack = decode(&fs::read(&file).unwrap()).unwrap();
274+ let paths: Vec<String> = pack
275+ .pages
276+ .iter()
277+ .map(|p| {
278+ p.path
279+ .iter()
280+ .map(|s| format!("/{}", String::from_utf8_lossy(s)))
281+ .collect()
282+ })
283+ .collect();
284+ let mut expected = vec![
285+ "/Upper/x/txt",
286+ "/a.b/c/txt",
287+ "/empty/txt",
288+ "/foo.bar/txt",
289+ "/foo~/txt",
290+ "/sub/deep/x/txt",
291+ "/under_score/txt",
292+ ];
293+ if cfg!(unix) {
294+ expected.extend(["/link/txt", "/linkdir/deep/x/txt"]);
295+ }
296+ expected.sort();
297+ assert_eq!(paths, expected);
298+ let err = stderr(&out);
299+ assert!(err.contains("empty file extension"), "{err}");
300+ assert!(err.contains("not a valid clay file name"), "{err}");
301+ assert!(
302+ !err.contains("README:"),
303+ "files vere skips silently are only listed with -v: {err}"
304+ );
305+}
306+
307+#[test]
308+fn jam_never_packs_its_own_output() {
309+ let tmp = tempfile::tempdir().unwrap();
310+ let dir = tmp.path().join("desk");
311+ fs::create_dir(&dir).unwrap();
312+ fs::write(dir.join("a.txt"), "a").unwrap();
313+ let run = |args: &[&str]| {
314+ Command::new(env!("CARGO_BIN_EXE_jammer"))
315+ .args(args)
316+ .current_dir(&dir)
317+ .output()
318+ .unwrap()
319+ };
320+ // By default, refuse to leave the output where |commit would find it.
321+ let out = run(&["jam", "."]);
322+ assert!(!out.status.success());
323+ assert!(
324+ stderr(&out).contains("inside the desk directory"),
325+ "{}",
326+ stderr(&out)
327+ );
328+ // Asked for explicitly, warn, and never pack the previous output.
329+ for _ in 0..2 {
330+ let out = run(&["jam", ".", "-o", "desk.deskjam"]);
331+ assert!(out.status.success(), "{}", stderr(&out));
332+ assert!(stderr(&out).contains("warning:"), "{}", stderr(&out));
333+ }
334+ let pack = decode(&fs::read(dir.join("desk.deskjam")).unwrap()).unwrap();
335+ assert_eq!(pack.pages.len(), 1);
336+}
337+
338+#[test]
339+fn jam_checks_desk_names() {
340+ let tmp = tempfile::tempdir().unwrap();
341+ let dir = tmp.path().join("Bad_Name");
342+ fs::create_dir(&dir).unwrap();
343+ fs::write(dir.join("a.txt"), "a").unwrap();
344+ let file = tmp.path().join("x.deskjam");
345+ let out = jammer(&["jam", dir.to_str().unwrap(), "-o", file.to_str().unwrap()]);
346+ assert!(!out.status.success());
347+ assert!(stderr(&out).contains("--desk"), "{}", stderr(&out));
348+ let out = jammer(&[
349+ "jam",
350+ dir.to_str().unwrap(),
351+ "-o",
352+ file.to_str().unwrap(),
353+ "-d",
354+ "UPPER",
355+ ]);
356+ assert!(!out.status.success());
357+ let out = jammer(&[
358+ "jam",
359+ dir.to_str().unwrap(),
360+ "-o",
361+ file.to_str().unwrap(),
362+ "-d",
363+ "good-name",
364+ ]);
365+ assert!(out.status.success(), "{}", stderr(&out));
366+ assert_eq!(
367+ decode(&fs::read(&file).unwrap()).unwrap().desk,
368+ b"good-name"
369+ );
370+}
371+
372+#[test]
373+fn pipes_through_stdout_and_stdin() {
374+ let tmp = tempfile::tempdir().unwrap();
375+ let jam = jammer(&["jam", fixture("jfix").to_str().unwrap(), "-o", "-"]);
376+ assert!(jam.status.success(), "{}", stderr(&jam));
377+ let out_dir = tmp.path().join("out");
378+ let mut child = Command::new(env!("CARGO_BIN_EXE_jammer"))
379+ .args(["unjam", "-", "-o", out_dir.to_str().unwrap()])
380+ .stdin(Stdio::piped())
381+ .stderr(Stdio::piped())
382+ .spawn()
383+ .unwrap();
384+ child.stdin.take().unwrap().write_all(&jam.stdout).unwrap();
385+ let out = child.wait_with_output().unwrap();
386+ assert!(out.status.success(), "{}", stderr(&out));
387+ assert_eq!(tree(&out_dir), tree(&fixture("jfix")));
388+}
389+
390+#[test]
391+fn rejects_files_that_are_not_deskjams() {
392+ let tmp = tempfile::tempdir().unwrap();
393+ let file = tmp.path().join("junk.deskjam");
394+ fs::write(&file, b"hello, world").unwrap();
395+ let out = jammer(&["list", file.to_str().unwrap()]);
396+ assert!(!out.status.success());
397+ assert!(stderr(&out).contains("cannot unpack"), "{}", stderr(&out));
398+ let out = jammer(&["unjam", tmp.path().to_str().unwrap()]);
399+ assert!(!out.status.success());
400+}
401+
402+/// A few hundred bytes describing 20 GiB of files.
403+#[test]
404+fn unjam_refuses_decompression_bombs() {
405+ use jammer::jam::jam;
406+ use jammer::noun::Arena;
407+ use jammer::treap::map_from_entries;
408+
409+ let mut a = Arena::new();
410+ let mime = a.cord("mime");
411+ let mite = a.path(&["text", "plain"]);
412+ let size = a.atom_u64(1 << 30);
413+ let data = a.cord("x");
414+ let octs = a.cell(size, data);
415+ let page = a.cell(mite, octs);
416+ let page = a.cell(mime, page);
417+ let entries: Vec<_> = (0..20)
418+ .map(|i| (a.path(&[format!("f{i}"), "txt".to_string()]), page))
419+ .collect();
420+ let map = map_from_entries(&mut a, &entries);
421+ let desk = a.cord("bomb");
422+ let tag = a.cord("deskpack-0");
423+ let body = a.cell(desk, map);
424+ let root = a.cell(tag, body);
425+
426+ let tmp = tempfile::tempdir().unwrap();
427+ let file = tmp.path().join("bomb.deskjam");
428+ fs::write(&file, jam(&a, root)).unwrap();
429+ assert!(fs::metadata(&file).unwrap().len() < 1000);
430+ let out_dir = tmp.path().join("out");
431+ let out = jammer(&[
432+ "unjam",
433+ file.to_str().unwrap(),
434+ "-o",
435+ out_dir.to_str().unwrap(),
436+ ]);
437+ assert!(!out.status.success());
438+ assert!(stderr(&out).contains("refusing"), "{}", stderr(&out));
439+ assert!(!out_dir.exists());
440+ let out = jammer(&["list", file.to_str().unwrap()]);
441+ assert!(out.status.success());
442+ assert!(
443+ String::from_utf8(out.stdout)
444+ .unwrap()
445+ .contains(" ? %mime")
446+ );
447+}
448+
449+/// vere escapes the segments `''`, `.`, `..` and `!...` with a leading `!` on
450+/// mount and strips one `!` on `|commit`; both directions were checked on a
451+/// ship.
452+#[test]
453+fn bang_escapes_round_trip_like_a_mount() {
454+ let tmp = tempfile::tempdir().unwrap();
455+ let dir = tmp.path().join("bang");
456+ for sub in ["!..", "sub/!"] {
457+ fs::create_dir_all(dir.join(sub)).unwrap();
458+ }
459+ for (name, text) in [
460+ ("!../y.txt", "y"),
461+ ("sub/!/q.txt", "q"),
462+ ("!.txt", "e"),
463+ ("!!x.txt", "x"),
464+ ("!bang.txt", "escaped spelling"),
465+ ("bang.txt", "mount spelling"),
466+ ] {
467+ fs::write(dir.join(name), text).unwrap();
468+ }
469+ let file = tmp.path().join("bang.deskjam");
470+ let out = jammer(&["jam", dir.to_str().unwrap(), "-o", file.to_str().unwrap()]);
471+ assert!(out.status.success(), "{}", stderr(&out));
472+ let err = stderr(&out);
473+ assert!(
474+ err.contains("!bang.txt: it has the same clay path as"),
475+ "{err}"
476+ );
477+ assert!(err.contains("!!x.txt: not a valid clay file name"), "{err}");
478+
479+ let pack = decode(&fs::read(&file).unwrap()).unwrap();
480+ let paths: Vec<Vec<String>> = pack
481+ .pages
482+ .iter()
483+ .map(|p| {
484+ p.path
485+ .iter()
486+ .map(|s| String::from_utf8_lossy(s).into_owned())
487+ .collect()
488+ })
489+ .collect();
490+ let expected: Vec<Vec<&str>> = vec![
491+ vec!["", "txt"],
492+ vec!["..", "y", "txt"],
493+ vec!["bang", "txt"],
494+ vec!["sub", "", "q", "txt"],
495+ ];
496+ assert_eq!(paths, expected);
497+
498+ let out_dir = tmp.path().join("out");
499+ let out = jammer(&[
500+ "unjam",
501+ file.to_str().unwrap(),
502+ "-o",
503+ out_dir.to_str().unwrap(),
504+ ]);
505+ assert!(out.status.success(), "{}", stderr(&out));
506+ let files = tree(&out_dir);
507+ assert_eq!(
508+ files.keys().collect::<Vec<_>>(),
509+ ["!../y.txt", "!.txt", "bang.txt", "sub/!/q.txt"]
510+ );
511+ assert_eq!(files["bang.txt"], b"mount spelling");
512+}
513+
514+#[test]
515+fn jam_notes_what_a_new_desk_would_lack() {
516+ let tmp = tempfile::tempdir().unwrap();
517+ let dir = tmp.path().join("bare");
518+ fs::create_dir_all(dir.join("mar/foo")).unwrap();
519+ fs::write(dir.join("a.txt"), "a").unwrap();
520+ fs::write(dir.join("b.foo-bar"), "b").unwrap();
521+ fs::write(dir.join("c.hoon"), "~").unwrap();
522+ // %foo-bar is defined at mar/foo/bar.hoon
523+ fs::write(dir.join("mar/foo/bar.hoon"), "|_ a=@ --").unwrap();
524+ let file = tmp.path().join("bare.deskjam");
525+ let out = jammer(&["jam", dir.to_str().unwrap(), "-o", file.to_str().unwrap()]);
526+ assert!(out.status.success(), "{}", stderr(&out));
527+ let err = stderr(&out);
528+ assert!(
529+ err.contains("no mark definition under mar/ for %txt;"),
530+ "{err}"
531+ );
532+ assert!(err.contains("no sys.kelvin"), "{err}");
533+
534+ fs::write(dir.join("mar/txt.hoon"), "|_ a=wain --").unwrap();
535+ fs::create_dir(dir.join("sys")).unwrap();
536+ fs::write(dir.join("sys/kelvin"), "unused, no extension").unwrap();
537+ fs::write(dir.join("sys.kelvin"), "[%zuse 408]\n").unwrap();
538+ fs::write(dir.join("mar/kelvin.hoon"), "|_ a=* --").unwrap();
539+ let out = jammer(&["jam", dir.to_str().unwrap(), "-o", file.to_str().unwrap()]);
540+ assert!(out.status.success(), "{}", stderr(&out));
541+ let err = stderr(&out);
542+ assert!(!err.contains("note:"), "{err}");
543+}
544+
545+#[test]
546+fn jam_warns_about_crlf_in_hoon_parsed_files() {
547+ let tmp = tempfile::tempdir().unwrap();
548+ let dir = tmp.path().join("crlf");
549+ fs::create_dir_all(dir.join("mar")).unwrap();
550+ fs::write(dir.join("sys.kelvin"), "[%zuse 408]\r\n").unwrap();
551+ fs::write(dir.join("mar/kelvin.hoon"), "~").unwrap();
552+ // built-in conversions need no mark file; %jam is done by mar/mime.hoon
553+ fs::write(dir.join("a.noun"), "x").unwrap();
554+ fs::write(dir.join("b.mime"), "x").unwrap();
555+ fs::write(dir.join("c.jam"), "x").unwrap();
556+ fs::write(dir.join("mar/mime.hoon"), "~").unwrap();
557+ let file = tmp.path().join("crlf.deskjam");
558+ let out = jammer(&["jam", dir.to_str().unwrap(), "-o", file.to_str().unwrap()]);
559+ assert!(out.status.success(), "{}", stderr(&out));
560+ let err = stderr(&out);
561+ assert!(
562+ err.contains("/sys/kelvin contains carriage returns"),
563+ "{err}"
564+ );
565+ assert!(!err.contains("no mark definition"), "{err}");
566+}
567+
568+#[cfg(unix)]
569+#[test]
570+fn unjam_follows_a_symlinked_output_root() {
571+ let tmp = tempfile::tempdir().unwrap();
572+ fs::create_dir(tmp.path().join("real")).unwrap();
573+ std::os::unix::fs::symlink("real", tmp.path().join("link")).unwrap();
574+ let out = jammer(&[
575+ "unjam",
576+ fixture("jfix.deskjam").to_str().unwrap(),
577+ "-o",
578+ tmp.path().join("link").to_str().unwrap(),
579+ ]);
580+ assert!(out.status.success(), "{}", stderr(&out));
581+ assert_eq!(tree(&tmp.path().join("real")), tree(&fixture("jfix")));
582+}
583+
584+#[test]
585+fn unjam_skips_clashes_and_keeps_going() {
586+ let tmp = tempfile::tempdir().unwrap();
587+ let path = |segments: &[&str]| segments.iter().map(|s| s.to_string()).collect();
588+ // `/a/txt` is the file a.txt; `/a.txt/b/c` needs a directory a.txt.
589+ let clash = encode(
590+ "clash",
591+ vec![
592+ SourceFile {
593+ path: path(&["a", "txt"]),
594+ contents: b"a".to_vec(),
595+ },
596+ SourceFile {
597+ path: path(&["a.txt", "b", "c"]),
598+ contents: b"b".to_vec(),
599+ },
600+ SourceFile {
601+ path: path(&["z", "txt"]),
602+ contents: b"z".to_vec(),
603+ },
604+ ],
605+ );
606+ let file = tmp.path().join("clash.deskjam");
607+ fs::write(&file, clash).unwrap();
608+ let out_dir = tmp.path().join("out");
609+ let out = jammer(&[
610+ "unjam",
611+ file.to_str().unwrap(),
612+ "-o",
613+ out_dir.to_str().unwrap(),
614+ ]);
615+ assert_eq!(out.status.code(), Some(3), "{}", stderr(&out));
616+ assert!(
617+ stderr(&out).contains("is not a directory"),
618+ "{}",
619+ stderr(&out)
620+ );
621+ assert_eq!(
622+ tree(&out_dir).keys().collect::<Vec<_>>(),
623+ ["a.txt", "z.txt"]
624+ );
625+}
626+
627+#[test]
628+fn jam_will_not_clobber_other_files() {
629+ let tmp = tempfile::tempdir().unwrap();
630+ let precious = tmp.path().join("notes.txt");
631+ fs::write(&precious, "precious").unwrap();
632+ let out = jammer(&[
633+ "jam",
634+ fixture("jfix").to_str().unwrap(),
635+ "-o",
636+ precious.to_str().unwrap(),
637+ ]);
638+ assert!(!out.status.success());
639+ assert!(stderr(&out).contains("--force"), "{}", stderr(&out));
640+ assert_eq!(fs::read(&precious).unwrap(), b"precious");
641+ let out = jammer(&[
642+ "jam",
643+ fixture("jfix").to_str().unwrap(),
644+ "-o",
645+ precious.to_str().unwrap(),
646+ "--force",
647+ ]);
648+ assert!(out.status.success(), "{}", stderr(&out));
649+ assert!(decode(&fs::read(&precious).unwrap()).is_ok());
650+}
651+
652+#[cfg(unix)]
653+#[test]
654+fn jam_reports_unreadable_files() {
655+ use std::os::unix::fs::PermissionsExt;
656+ let tmp = tempfile::tempdir().unwrap();
657+ let dir = tmp.path().join("unread");
658+ fs::create_dir(&dir).unwrap();
659+ fs::write(dir.join("ok.txt"), "ok").unwrap();
660+ let secret = dir.join("secret.txt");
661+ fs::write(&secret, "secret").unwrap();
662+ fs::set_permissions(&secret, fs::Permissions::from_mode(0o000)).unwrap();
663+ if fs::read(&secret).is_ok() {
664+ return; // running as root
665+ }
666+ let file = tmp.path().join("u.deskjam");
667+ let out = jammer(&["jam", dir.to_str().unwrap(), "-o", file.to_str().unwrap()]);
668+ assert_eq!(out.status.code(), Some(3), "{}", stderr(&out));
669+ assert!(stderr(&out).contains("cannot read it"), "{}", stderr(&out));
670+ assert_eq!(decode(&fs::read(&file).unwrap()).unwrap().pages.len(), 1);
671+}
672+
673+/// Names from deskjams and from the filesystem must not reach the terminal
674+/// raw: escape sequences could hide or forge messages.
675+#[test]
676+fn hostile_names_are_escaped() {
677+ use jammer::jam::jam;
678+ use jammer::noun::Arena;
679+ use jammer::treap::map_from_entries;
680+
681+ let tmp = tempfile::tempdir().unwrap();
682+ // A page whose mark is "x<ESC>[31m" and whose noun is a cell.
683+ let mut a = Arena::new();
684+ let mark = a.atom(b"x\x1b[31m");
685+ let one = a.atom_u64(1);
686+ let noun = a.cell(one, one);
687+ let page = a.cell(mark, noun);
688+ let path = a.path(&["ok", "txt"]);
689+ let map = map_from_entries(&mut a, &[(path, page)]);
690+ let desk = a.cord("esc");
691+ let tag = a.cord("deskpack-0");
692+ let body = a.cell(desk, map);
693+ let root = a.cell(tag, body);
694+ let file = tmp.path().join("esc.deskjam");
695+ fs::write(&file, jam(&a, root)).unwrap();
696+ let out = jammer(&[
697+ "unjam",
698+ file.to_str().unwrap(),
699+ "-o",
700+ tmp.path().join("o").to_str().unwrap(),
701+ ]);
702+ assert!(!out.stderr.contains(&0x1b), "{}", stderr(&out));
703+ assert!(stderr(&out).contains("\\u{1b}"), "{}", stderr(&out));
704+
705+ #[cfg(unix)]
706+ {
707+ let dir = tmp.path().join("evil");
708+ fs::create_dir(&dir).unwrap();
709+ fs::write(dir.join("ok.txt"), "ok").unwrap();
710+ fs::write(dir.join("bad\x1b[2Jname.txt"), "x").unwrap();
711+ let out = jammer(&[
712+ "jam",
713+ dir.to_str().unwrap(),
714+ "-o",
715+ tmp.path().join("e.deskjam").to_str().unwrap(),
716+ ]);
717+ assert!(out.status.success(), "{}", stderr(&out));
718+ assert!(!out.stderr.contains(&0x1b), "{}", stderr(&out));
719+ }
720+}
721+
722+#[cfg(unix)]
723+#[test]
724+fn desk_name_comes_from_the_path_as_given() {
725+ let tmp = tempfile::tempdir().unwrap();
726+ fs::create_dir_all(tmp.path().join("repo/desk")).unwrap();
727+ fs::write(tmp.path().join("repo/desk/a.txt"), "a").unwrap();
728+ std::os::unix::fs::symlink("repo/desk", tmp.path().join("my-app")).unwrap();
729+ let out = Command::new(env!("CARGO_BIN_EXE_jammer"))
730+ .args(["jam", "my-app"])
731+ .current_dir(tmp.path())
732+ .output()
733+ .unwrap();
734+ assert!(out.status.success(), "{}", stderr(&out));
735+ let pack = decode(&fs::read(tmp.path().join("my-app.deskjam")).unwrap()).unwrap();
736+ assert_eq!(pack.desk, b"my-app");
737+}
738+
739+#[test]
740+fn many_hyphenated_extensions_are_fast() {
741+ let tmp = tempfile::tempdir().unwrap();
742+ let dir = tmp.path().join("hy");
743+ fs::create_dir(&dir).unwrap();
744+ let ext = vec!["a"; 40].join("-");
745+ for i in 0..300 {
746+ fs::write(dir.join(format!("f{i}.{ext}")), "x").unwrap();
747+ }
748+ let start = std::time::Instant::now();
749+ let out = jammer(&[
750+ "jam",
751+ dir.to_str().unwrap(),
752+ "-o",
753+ tmp.path().join("hy.deskjam").to_str().unwrap(),
754+ ]);
755+ assert!(out.status.success(), "{}", stderr(&out));
756+ assert!(
757+ stderr(&out).contains("no mark definition"),
758+ "{}",
759+ stderr(&out)
760+ );
761+ assert!(start.elapsed().as_secs() < 10);
762+}
763+
764+#[test]
765+fn unjam_notes_marks_it_guessed() {
766+ use jammer::jam::jam;
767+ use jammer::noun::Arena;
768+ use jammer::treap::map_from_entries;
769+
770+ let mut a = Arena::new();
771+ let mark = a.cord("webmanifest");
772+ let noun = a.cord("{}");
773+ let page = a.cell(mark, noun);
774+ let path = a.path(&["site", "webmanifest"]);
775+ let map = map_from_entries(&mut a, &[(path, page)]);
776+ let desk = a.cord("guess");
777+ let tag = a.cord("deskpack-0");
778+ let body = a.cell(desk, map);
779+ let root = a.cell(tag, body);
780+ let tmp = tempfile::tempdir().unwrap();
781+ let file = tmp.path().join("guess.deskjam");
782+ fs::write(&file, jam(&a, root)).unwrap();
783+ let out = jammer(&[
784+ "unjam",
785+ file.to_str().unwrap(),
786+ "-o",
787+ tmp.path().join("o").to_str().unwrap(),
788+ ]);
789+ assert!(out.status.success(), "{}", stderr(&out));
790+ assert!(
791+ stderr(&out).contains("does not know the marks %webmanifest"),
792+ "{}",
793+ stderr(&out)
794+ );
795+ // and nothing is said for the fixture, whose marks are all known
796+ let out = jammer(&[
797+ "unjam",
798+ fixture("jfix.deskjam").to_str().unwrap(),
799+ "-o",
800+ tmp.path().join("f").to_str().unwrap(),
801+ ]);
802+ assert!(!stderr(&out).contains("note:"), "{}", stderr(&out));
803+}
804+
805+/// Pages that make jammer walk a lot of nouns without producing output
806+/// count against the budget too, so this ~1 MB file is refused quickly.
807+#[test]
808+fn unjam_bounds_the_work_of_rendering() {
809+ use jammer::jam::jam;
810+ use jammer::noun::{Arena, Noun};
811+ use jammer::treap::map_from_entries;
812+
813+ let mut a = Arena::new();
814+ // One list of 100,000 NULs; each page is a different suffix of it.
815+ let zero = a.atom(&[]);
816+ let mut suffixes = vec![zero];
817+ for _ in 0..100_000 {
818+ let next = a.cell(zero, *suffixes.last().unwrap());
819+ suffixes.push(next);
820+ }
821+ let tape = a.cord("tape");
822+ let entries: Vec<(Noun, Noun)> = (0..800)
823+ .map(|i| {
824+ let page = a.cell(tape, suffixes[100_000 - i]);
825+ (a.path(&[format!("f{i}"), "tape".to_string()]), page)
826+ })
827+ .collect();
828+ let map = map_from_entries(&mut a, &entries);
829+ let desk = a.cord("work");
830+ let tag = a.cord("deskpack-0");
831+ let body = a.cell(desk, map);
832+ let root = a.cell(tag, body);
833+ let tmp = tempfile::tempdir().unwrap();
834+ let file = tmp.path().join("work.deskjam");
835+ fs::write(&file, jam(&a, root)).unwrap();
836+
837+ let start = std::time::Instant::now();
838+ let out = jammer(&[
839+ "unjam",
840+ file.to_str().unwrap(),
841+ "-o",
842+ tmp.path().join("o").to_str().unwrap(),
843+ ]);
844+ assert!(!out.status.success());
845+ assert!(
846+ stderr(&out).contains("refusing to unpack"),
847+ "{}",
848+ stderr(&out)
849+ );
850+ let out = jammer(&["list", file.to_str().unwrap()]);
851+ assert!(out.status.success());
852+ assert!(
853+ String::from_utf8(out.stdout)
854+ .unwrap()
855+ .contains(" ? %tape")
856+ );
857+ assert!(start.elapsed().as_secs() < 60);
858+}
859+
860+/// A few small malformed pages are skipped one by one; the rest unpacks.
861+#[test]
862+fn unjam_skips_malformed_pages() {
863+ use jammer::jam::jam;
864+ use jammer::noun::{Arena, Noun};
865+ use jammer::treap::map_from_entries;
866+
867+ let mut a = Arena::new();
868+ let txt = a.cord("txt");
869+ let bad = a.atom_u64(5); // a %txt page must be a list
870+ let line = a.cord("hello");
871+ let good = a.list(&[line]);
872+ let mut entries: Vec<(Noun, Noun)> = (0..8)
873+ .map(|i| {
874+ let page = a.cell(txt, bad);
875+ (a.path(&[format!("bad{i}"), "txt".to_string()]), page)
876+ })
877+ .collect();
878+ let page = a.cell(txt, good);
879+ entries.push((a.path(&["good", "txt"]), page));
880+ let map = map_from_entries(&mut a, &entries);
881+ let desk = a.cord("mixed");
882+ let tag = a.cord("deskpack-0");
883+ let body = a.cell(desk, map);
884+ let root = a.cell(tag, body);
885+ let tmp = tempfile::tempdir().unwrap();
886+ let file = tmp.path().join("mixed.deskjam");
887+ fs::write(&file, jam(&a, root)).unwrap();
888+ let out_dir = tmp.path().join("o");
889+ let out = jammer(&[
890+ "unjam",
891+ file.to_str().unwrap(),
892+ "-o",
893+ out_dir.to_str().unwrap(),
894+ ]);
895+ assert_eq!(out.status.code(), Some(3), "{}", stderr(&out));
896+ assert_eq!(
897+ stderr(&out).matches("not a valid %txt noun").count(),
898+ 8,
899+ "{}",
900+ stderr(&out)
901+ );
902+ assert_eq!(
903+ tree(&out_dir).get("good.txt").map(Vec::as_slice),
904+ Some(&b"hello"[..])
905+ );
906+}
jammer/tests/core_vectors.rsnew file+286
@@ -0,0 +1,286 @@
1+//! Vectors produced by a real ship (`urbit eval`, vere 4.6, zuse 408).
2+
3+use jammer::jam::{cue, jam};
4+use jammer::noun::{Arena, Noun};
5+use jammer::treap::{is_valid_treap, map_from_entries, set_from_items, tree_items};
6+
7+/// Parse Hoon `@ux` syntax (`0x2.4ee1`) into little-endian bytes.
8+fn ux(text: &str) -> Vec<u8> {
9+ let hex: String = text
10+ .trim_start_matches("0x")
11+ .chars()
12+ .filter(|&c| c != '.')
13+ .collect();
14+ let hex = if hex.len() % 2 == 1 {
15+ format!("0{hex}")
16+ } else {
17+ hex
18+ };
19+ let mut bytes: Vec<u8> = (0..hex.len())
20+ .step_by(2)
21+ .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
22+ .collect();
23+ bytes.reverse();
24+ while bytes.last() == Some(&0) {
25+ bytes.pop();
26+ }
27+ bytes
28+}
29+
30+fn num(a: &mut Arena, v: u64) -> Noun {
31+ a.atom_u64(v)
32+}
33+
34+fn check_jam(a: &mut Arena, noun: Noun, expected: &str) {
35+ let bytes = jam(a, noun);
36+ assert_eq!(bytes, ux(expected), "jam mismatch for expected {expected}");
37+ let cued = cue(a, &bytes).unwrap();
38+ assert_eq!(cued.noun, noun, "cue did not round-trip for {expected}");
39+ assert!(!cued.trailing_garbage);
40+}
41+
42+#[test]
43+fn jam_matches_ship() {
44+ let mut a = Arena::new();
45+ let n = num(&mut a, 0);
46+ check_jam(&mut a, n, "0x2");
47+ let n = num(&mut a, 1);
48+ check_jam(&mut a, n, "0xc");
49+ let one = num(&mut a, 1);
50+ let n = a.cell(one, one);
51+ check_jam(&mut a, n, "0x331");
52+ let (z, nineteen) = (num(&mut a, 0), num(&mut a, 19));
53+ let n = a.cell(z, nineteen);
54+ check_jam(&mut a, n, "0x9b09");
55+ let five = num(&mut a, 5);
56+ let n = a.cell(five, five);
57+ check_jam(&mut a, n, "0x2.4ee1");
58+ let mut big = vec![0u8; 8];
59+ big.push(1);
60+ let n = a.atom(&big);
61+ check_jam(&mut a, n, "0x8000.0000.0000.0000.0300");
62+ let n = a.cord("Hello, world!");
63+ check_jam(&mut a, n, "0x10.b236.3937.bb90.1637.b636.32a4.4d00");
64+ let two = num(&mut a, 2);
65+ let p = a.cell(one, two);
66+ let n = a.cell(p, p);
67+ check_jam(&mut a, n, "0x49.c8c5");
68+ let n = a.path(&["app", "foo", "hoon"]);
69+ check_jam(&mut a, n, "0x2d.cded.ed1f.01de.decc.f01e.0e0c.2f01");
70+ let mut bex1000 = vec![0u8; 125];
71+ bex1000.push(1);
72+ let n = a.atom(&bex1000);
73+ check_jam(
74+ &mut a,
75+ n,
76+ "0x2000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.001e.9800",
77+ );
78+ let large = num(&mut a, 12_345_678_901_234_567_890);
79+ let n = a.cell(large, large);
80+ check_jam(&mut a, n, "0x127.56a9.5319.d63e.15a4.0401");
81+ let b = num(&mut a, 256);
82+ let bb = a.cell(b, b);
83+ let n = a.cell(b, bb);
84+ check_jam(&mut a, n, "0x24.e4d8.0181");
85+ let mime = a.cord("mime");
86+ let mite = a.path(&["text", "plain"]);
87+ let (three, abc) = (num(&mut a, 3), a.cord("abc"));
88+ let octs = a.cell(three, abc);
89+ let m = a.cell(mite, octs);
90+ let n = a.cell(mime, m);
91+ check_jam(
92+ &mut a,
93+ n,
94+ "0x18d8.985e.0686.dcd2.c2d8.e078.07a3.c32b.a7c0.5cad.ad2d.bf01",
95+ );
96+}
97+
98+#[test]
99+fn mug_matches_ship() {
100+ let mut a = Arena::new();
101+ let zero = num(&mut a, 0);
102+ let one = num(&mut a, 1);
103+ let two = num(&mut a, 2);
104+ let letter = a.cord("a");
105+ let zz = a.cell(zero, zero);
106+ let one_two = a.cell(one, two);
107+ let path = a.path(&["app", "foo", "hoon"]);
108+ let hello = a.cord("Hello, world!");
109+ for (noun, expected) in [
110+ (zero, 0x79ff_04e8),
111+ (one, 0x715c_2a60),
112+ (letter, 0x36b6_f52a),
113+ (zz, 0x192f_5588),
114+ (one_two, 0x6a36_c1d9),
115+ (path, 0x5747_c2a6),
116+ (hello, 0x4d44_1035),
117+ ] {
118+ assert_eq!(a.mug(noun), expected);
119+ }
120+}
121+
122+#[test]
123+fn maps_and_sets_match_ship() {
124+ let mut a = Arena::new();
125+ let entries: Vec<(Noun, Noun)> = [
126+ (&["a", "hoon"][..], 1),
127+ (&["b", "txt"][..], 2),
128+ (&["sys", "kelvin"][..], 3),
129+ ]
130+ .iter()
131+ .map(|(p, v)| (a.path(p), a.atom_u64(*v)))
132+ .collect();
133+ let map = map_from_entries(&mut a, &entries);
134+ assert!(is_valid_treap(&a, map, |item| a
135+ .as_cell(item)
136+ .map(|(k, _)| k)));
137+ assert_eq!(
138+ jam(&a, map),
139+ ux(
140+ "0xb210.f2dc.dede.d1f0.1c3c.15a6.45d1.e1d1.e038.b82a.e8b7.34bb.3632.b5be.01e6.f2e6.f015"
141+ )
142+ );
143+
144+ let zuse = a.cord("zuse");
145+ let wefts: Vec<Noun> = [409, 408, 410]
146+ .iter()
147+ .map(|&v| {
148+ let n = a.atom_u64(v);
149+ a.cell(zuse, n)
150+ })
151+ .collect();
152+ let set = set_from_items(&mut a, &wefts);
153+ assert_eq!(
154+ jam(&a, set),
155+ ux("0xa9c.d182.66ae.64c1.3359.cc18.32b9.babd.7c05")
156+ );
157+
158+ let entries: Vec<(Noun, Noun)> = (1..=40u64)
159+ .map(|i| (a.path(&["lib", &i.to_string(), "hoon"]), a.atom_u64(i)))
160+ .collect();
161+ let map = map_from_entries(&mut a, &entries);
162+ assert!(is_valid_treap(&a, map, |item| a
163+ .as_cell(item)
164+ .map(|(k, _)| k)));
165+ assert_eq!(tree_items(&a, map).unwrap().len(), 40);
166+ assert_eq!(
167+ jam(&a, map),
168+ ux(
169+ "0x1410.c3f3.083c.7819.e81d.9ab3.e420.f1ea.63a0.766a.c10c.3fb0.83c7.899e.81d9.ab33.5083.c7c1.9e81.d9aa.f108.3c7a.18e8.1d9a.b043.0e44.1e3c.a83b.3566.6c20.f1f2.63a0.766a.c10c.3830.83c7.b18e.81d9.aac1.083c.7c50.766a.b242.0f1f.341d.9aac.b420.f1ee.67a0.766a.c10c.3d21.078f.331d.03b3.56a0.861e.883c.79d0.766a.b050.83c7.919e.81d9.ab04.30e2.a107.8f43.3d03.b355.f883.c7bd.0766.ab3d.083c.7c99.e81d.9ab4.10c3.c308.3c7a.1968.1d9a.acc4.1e3d.283b.3560.861c.7420.f1ea.67a0.766a.bd61.078f.932d.03b3.558b.083c.7b98.e81d.9ab3.6420.f1e2.63a0.766a.b842.0f1e.463a.0766.ab56.1078.f132.d03b.3560.861e.c41e.3da8.3b35.5b30.83c7.9196.81d9.ab41.0c3c.b083.c7a9.9681.d9aa.e984.1e3d.8cb4.0ecd.5821.8776.1078.f332.d03b.3560.861c.9841.e3e0.c740.ecd5.9c83.c78d.0766.ac10.c3a2.841e.3c0d.340e.cd56.4a10.78f6.33d0.3b35.5e30.83c7.c196.81d9.ab30.d083.c799.9e81.d9aa.dc41.e3d6.83b3.569a.2107.8f03.1d03.b355.db08.3c7b.9968.1d9a.ad18.5b9b.dbda.3e03.8196.81c4.d2d8.f015"
170+ )
171+ );
172+}
173+
174+#[test]
175+fn cue_is_as_lenient_as_hoon() {
176+ let mut a = Arena::new();
177+ // (cue 0x2d0) => 5: oversized length field
178+ let five = a.atom_u64(5);
179+ assert_eq!(cue(&mut a, &ux("0x2d0")).unwrap().noun, five);
180+ // (cue 0xc23b.86e1) => [5 5 5]: backref to a second occurrence
181+ let tail = a.cell(five, five);
182+ let triple = a.cell(five, tail);
183+ assert_eq!(cue(&mut a, &ux("0xc23b.86e1")).unwrap().noun, triple);
184+ // (cue 0x17b8) => 5: trailing bits are ignored
185+ let cued = cue(&mut a, &ux("0x17b8")).unwrap();
186+ assert_eq!(cued.noun, five);
187+ assert!(cued.trailing_garbage);
188+ // (cue 0x7.f800.0040.0000) => 255: payload bits past the end read as 0
189+ let n255 = a.atom_u64(255);
190+ assert_eq!(cue(&mut a, &ux("0x7.f800.0040.0000")).unwrap().noun, n255);
191+}
192+
193+#[test]
194+fn cue_rejects_garbage() {
195+ let mut a = Arena::new();
196+ assert!(cue(&mut a, &[]).is_err());
197+ // a lone cell tag
198+ assert!(cue(&mut a, &[0b01]).is_err());
199+ // [0 <backref to bit 1>]: cell `1 0`, atom 0 `0 1`, backref `1 1` + mat(1)
200+ // `0 1 1`. No noun starts at bit 1.
201+ assert!(matches!(
202+ cue(&mut a, &[0xb9, 0x01]),
203+ Err(jammer::jam::CueError::BadBackref { target: 1, .. })
204+ ));
205+ // the same stream pointing at bit 2, where the atom 0 starts, is fine:
206+ // backref `1 1` + mat(2) `0 0 1 0 0 1`... built with jam itself instead
207+ let zero = num(&mut a, 0);
208+ let pair = a.cell(zero, zero);
209+ let bytes = jam(&a, pair);
210+ assert_eq!(cue(&mut a, &bytes).unwrap().noun, pair);
211+ // a 65-zero length prefix
212+ let mut bytes = vec![0u8; 9];
213+ bytes.push(0x80);
214+ assert!(cue(&mut a, &bytes).is_err());
215+ // a huge declared atom is not allocated
216+ let mut huge = vec![0u8; 7];
217+ huge.push(0x80); // 63 zeros then a 1 at bit 63: width >= 2^62
218+ huge.extend_from_slice(&[0xff; 8]);
219+ let _ = cue(&mut a, &huge);
220+}
221+
222+/// Nouns whose first murmur3 hash folds to zero, so `mug` retries with the
223+/// next seed (found by inverting murmur3; values from a ship).
224+#[test]
225+fn mug_retries_with_next_seed() {
226+ let mut a = Arena::new();
227+ let one = num(&mut a, 1);
228+ let x = num(&mut a, 0x6ddb_5cd6);
229+ let y = num(&mut a, 0xb33b_0b40);
230+ let p = num(&mut a, 0xb91a_4685);
231+ let p = a.cell(one, p);
232+ let q = num(&mut a, 0x709a_4eed);
233+ let q = a.cell(one, q);
234+ assert_eq!(a.mug(x), 0x0429_ceea);
235+ assert_eq!(a.mug(y), 0x5cbf_3de5);
236+ assert_eq!(a.mug(p), 0x5381_734e);
237+ assert_eq!(a.mug(q), 0x7bd5_70ca);
238+ for (noun, double) in [(x, 0x7c30_923f), (y, 0x56af_cb80), (p, 0x5540_af45)] {
239+ let mug = u64::from(a.mug(noun));
240+ let mug = num(&mut a, mug);
241+ assert_eq!(a.mug(mug), double);
242+ }
243+}
244+
245+#[test]
246+fn cue_bounds_the_number_of_nouns() {
247+ // A list of n nulls is ~4 bits per element and decodes to 2n nouns.
248+ let mut a = Arena::new();
249+ let zero = num(&mut a, 0);
250+ let mut list = zero;
251+ for _ in 0..1000 {
252+ list = a.cell(zero, list);
253+ }
254+ let bytes = jam(&a, list);
255+ let mut b = Arena::new();
256+ assert!(matches!(
257+ jammer::jam::cue_with_limit(&mut b, &bytes, 2000),
258+ Err(jammer::jam::CueError::TooLarge { .. })
259+ ));
260+ assert_eq!(
261+ jammer::jam::cue_with_limit(&mut b, &bytes, 2001)
262+ .unwrap()
263+ .noun,
264+ {
265+ let mut c = Arena::new();
266+ let n = cue(&mut c, &bytes).unwrap().noun;
267+ // same arena handles differ; compare by re-jamming
268+ assert_eq!(jam(&c, n), bytes);
269+ cue(&mut b, &bytes).unwrap().noun
270+ }
271+ );
272+}
273+
274+/// vere's cue gives up on a length prefix of 64 zero bits (%meme), so a
275+/// deskjam containing one could never be imported; don't accept it either.
276+#[test]
277+fn cue_rejects_64_bit_length_prefixes() {
278+ let mut bytes = vec![0u8; 8];
279+ bytes.push(0x02); // bit 64 = 0, bit 65 = 1: tag 0, then 64 zeros, then 1
280+ bytes.extend([0u8; 8]);
281+ let mut a = Arena::new();
282+ assert!(matches!(
283+ cue(&mut a, &bytes),
284+ Err(jammer::jam::CueError::TooLarge { .. })
285+ ));
286+}
jammer/tests/deskpack.rsnew file+72
@@ -0,0 +1,72 @@
1+use jammer::deskpack::{SourceFile, decode, encode};
2+
3+fn file(path: &[&str], contents: &[u8]) -> SourceFile {
4+ SourceFile {
5+ path: path.iter().map(|s| s.to_string()).collect(),
6+ contents: contents.to_vec(),
7+ }
8+}
9+
10+/// `(jam [%deskpack-0 %foo (~(gas by *(map path page:clay)) files)])` on a ship.
11+const SHIP_JAM: &str = "a802e1d31930ded10db63634b17c075b9b2b27c05580004a05c31930ded10db3b7383c07734b13c07134b63c055c6c4c2f03431930ded10db737b7b47c070f1bc1c185e02b34ba70606840cae6eaf44ab6e4030836e69616c703c03d1e195d3e02e56d696df80db9a5d9b195adf00f37973780aef6f66780e05ad6c6c2e0d6e6cac87401";
12+
13+fn ship_bytes() -> Vec<u8> {
14+ let mut bytes: Vec<u8> = (0..SHIP_JAM.len())
15+ .step_by(2)
16+ .map(|i| u8::from_str_radix(&SHIP_JAM[i..i + 2], 16).unwrap())
17+ .collect();
18+ bytes.reverse();
19+ bytes
20+}
21+
22+fn sample() -> Vec<SourceFile> {
23+ vec![
24+ file(&["app", "foo", "hoon"], b"abc"),
25+ file(&["sys", "kelvin"], b"[%zuse 408]\n"),
26+ file(&["desk", "bill"], b""),
27+ file(&["lib", "bin", "png"], &[1, 0, 2, 0, 0]),
28+ ]
29+}
30+
31+#[test]
32+fn encode_matches_ship() {
33+ assert_eq!(encode("foo", sample()), ship_bytes());
34+}
35+
36+#[test]
37+fn encode_ignores_file_order() {
38+ let mut files = sample();
39+ files.reverse();
40+ assert_eq!(encode("foo", files), ship_bytes());
41+}
42+
43+#[test]
44+fn decode_round_trip() {
45+ let pack = decode(&ship_bytes()).unwrap();
46+ assert_eq!(pack.desk, b"foo");
47+ assert!(pack.canonical_map);
48+ assert!(!pack.trailing_garbage);
49+ let paths: Vec<String> = pack
50+ .pages
51+ .iter()
52+ .map(|p| {
53+ p.path
54+ .iter()
55+ .map(|s| String::from_utf8(s.clone()).unwrap())
56+ .collect::<Vec<_>>()
57+ .join("/")
58+ })
59+ .collect();
60+ assert_eq!(
61+ paths,
62+ ["app/foo/hoon", "desk/bill", "lib/bin/png", "sys/kelvin"]
63+ );
64+ assert!(pack.pages.iter().all(|p| p.mark == b"mime"));
65+}
66+
67+#[test]
68+fn decode_rejects_other_jams() {
69+ // (jam [%foo ~])
70+ assert!(decode(&[0x02]).is_err());
71+ assert!(decode(&[]).is_err());
72+}
jammer/tests/fixtures/jfix.deskjamnew file

Binary file not shown.

jammer/tests/fixtures/jfix/app/hello.hoonnew file+2
@@ -0,0 +1,2 @@
1+|= a=@
2+(add a 1)
jammer/tests/fixtures/jfix/data.jsonnew file+1
@@ -0,0 +1 @@
1+{"c":true,"a":"é\"\n\u0009","d":false,"b":[1,2.50,{"x":null}],"e":{}}
jammer/tests/fixtures/jfix/desk.billnew file+3
@@ -0,0 +1,3 @@
1+:~ %hello
2+ %second-agent
3+==
jammer/tests/fixtures/jfix/desk.docket-0new file+11
@@ -0,0 +1,11 @@
1+:~
2+ title+'Fixture "quoted" ✓'
3+ info+'A test desk for jammer.'
4+ color+0xab.cdef
5+ version+[1 2 1.000]
6+ website+'https://example.com'
7+ license+'MIT'
8+ image+'https://example.com/icon.svg'
9+ base+'fixture'
10+ glob-ames+[~sampel-palnet 0v1l.lh2ff.co1oc.s4c5k.mlbtm.fubak.2ksqe.ol277.kaura.pk0nh.lp4rv]
11+==
jammer/tests/fixtures/jfix/desk.shipnew file+1
@@ -0,0 +1 @@
1+~sampel-palnet
jammer/tests/fixtures/jfix/lib/docket.hoonnew file+223
@@ -0,0 +1,223 @@
1+/- *docket
2+|%
3+::
4+++ mime
5+ |%
6+ +$ draft
7+ $: title=(unit @t)
8+ info=(unit @t)
9+ color=(unit @ux)
10+ glob-http=(unit [=url hash=@uvH])
11+ glob-ames=(unit [=ship hash=@uvH])
12+ base=(unit term)
13+ site=(unit path)
14+ image=(unit url)
15+ version=(unit version)
16+ website=(unit url)
17+ license=(unit cord)
18+ ==
19+ ::
20+ ++ finalize
21+ |= =draft
22+ ^- (unit docket)
23+ ?~ title.draft ~
24+ ?~ info.draft ~
25+ ?~ color.draft ~
26+ ?~ version.draft ~
27+ ?~ website.draft ~
28+ ?~ license.draft ~
29+ =/ href=(unit href)
30+ ?^ site.draft `[%site u.site.draft]
31+ ?~ base.draft ~
32+ ?^ glob-http.draft
33+ `[%glob u.base hash.u.glob-http %http url.u.glob-http]:draft
34+ ?~ glob-ames.draft
35+ ~
36+ `[%glob u.base hash.u.glob-ames %ames ship.u.glob-ames]:draft
37+ ?~ href ~
38+ =, draft
39+ :- ~
40+ :* %1
41+ u.title
42+ u.info
43+ u.color
44+ u.href
45+ image
46+ u.version
47+ u.website
48+ u.license
49+ ==
50+ ::
51+ ++ from-clauses
52+ =| =draft
53+ |= cls=(list clause)
54+ ^- (unit docket)
55+ =* loop $
56+ ?~ cls (finalize draft)
57+ =* clause i.cls
58+ =. draft
59+ ?- -.clause
60+ %title draft(title `title.clause)
61+ %info draft(info `info.clause)
62+ %color draft(color `color.clause)
63+ %glob-http draft(glob-http `[url hash]:clause)
64+ %glob-ames draft(glob-ames `[ship hash]:clause)
65+ %base draft(base `base.clause)
66+ %site draft(site `path.clause)
67+ %image draft(image `url.clause)
68+ %version draft(version `version.clause)
69+ %website draft(website `website.clause)
70+ %license draft(license `license.clause)
71+ ==
72+ loop(cls t.cls)
73+ ::
74+ ++ to-clauses
75+ |= d=docket
76+ ^- (list clause)
77+ %- zing
78+ :~ :~ title+title.d
79+ info+info.d
80+ color+color.d
81+ version+version.d
82+ website+website.d
83+ license+license.d
84+ ==
85+ ?~ image.d ~ ~[image+u.image.d]
86+ ?: ?=(%site -.href.d) ~[site+path.href.d]
87+ =/ ref=glob-reference glob-reference.href.d
88+ :~ base+base.href.d
89+ ?- -.location.ref
90+ %http [%glob-http url.location.ref hash.ref]
91+ %ames [%glob-ames ship.location.ref hash.ref]
92+ == == ==
93+ ::
94+ ++ spit-clause
95+ |= =clause
96+ ^- tape
97+ %+ weld " {(trip -.clause)}+"
98+ ?+ -.clause "'{(trip +.clause)}'"
99+ %color (scow %ux color.clause)
100+ %site (spud path.clause)
101+ ::
102+ %glob-http
103+ "['{(trip url.clause)}' {(scow %uv hash.clause)}]"
104+ ::
105+ %glob-ames
106+ "[{(scow %p ship.clause)} {(scow %uv hash.clause)}]"
107+ ::
108+ %version
109+ =, version.clause
110+ "[{(scow %ud major)} {(scow %ud minor)} {(scow %ud patch)}]"
111+ ==
112+ ::
113+ ++ spit-docket
114+ |= dock=docket
115+ ^- tape
116+ ;: welp
117+ ":~\0a"
118+ `tape`(zing (join "\0a" (turn (to-clauses dock) spit-clause)))
119+ "\0a=="
120+ ==
121+ --
122+::
123+++ enjs
124+ =, enjs:format
125+ |%
126+ ::
127+ ++ charge-update
128+ |= u=^charge-update
129+ ^- json
130+ %+ frond -.u
131+ ^- json
132+ ?- -.u
133+ %del-charge s+desk.u
134+ ::
135+ %initial
136+ %- pairs
137+ %+ turn ~(tap by initial.u)
138+ |=([=desk c=^charge] [desk (charge c)])
139+ ::
140+ %add-charge
141+ %- pairs
142+ :~ desk+s+desk.u
143+ charge+(charge charge.u)
144+ ==
145+ ==
146+ ::
147+ ++ num
148+ |= a=@u
149+ ^- ^tape
150+ =/ p=json (numb a)
151+ ?> ?=(%n -.p)
152+ (trip p.p)
153+ ::
154+ ++ version
155+ |= v=^version
156+ ^- json
157+ :- %s
158+ %- crip
159+ "{(num major.v)}.{(num minor.v)}.{(num patch.v)}"
160+ ::
161+ ++ merge
162+ |= [a=json b=json]
163+ ^- json
164+ ?> &(?=(%o -.a) ?=(%o -.b))
165+ [%o (~(uni by p.a) p.b)]
166+ ::
167+ ++ href
168+ |= h=^href
169+ %+ frond -.h
170+ ?- -.h
171+ %site s+(spat path.h)
172+ %glob
173+ %- pairs
174+ :~ base+s+base.h
175+ glob-reference+(glob-reference glob-reference.h)
176+ ==
177+ ==
178+ ::
179+ ++ glob-reference
180+ |= ref=^glob-reference
181+ %- pairs
182+ :~ hash+s+(scot %uv hash.ref)
183+ location+(glob-location location.ref)
184+ ==
185+ ::
186+ ++ glob-location
187+ |= loc=^glob-location
188+ ^- json
189+ %+ frond -.loc
190+ ?- -.loc
191+ %http s+url.loc
192+ %ames s+(scot %p ship.loc)
193+ ==
194+ ::
195+ ++ charge
196+ |= c=^charge
197+ %+ merge (docket docket.c)
198+ %- pairs
199+ :~ chad+(chad chad.c)
200+ ==
201+ ::
202+ ++ docket
203+ |= d=^docket
204+ ^- json
205+ %- pairs
206+ :~ title+s+title.d
207+ info+s+info.d
208+ color+s+(scot %ux color.d)
209+ href+(href href.d)
210+ image+?~(image.d ~ s+u.image.d)
211+ version+(version version.d)
212+ license+s+license.d
213+ website+s+website.d
214+ ==
215+ ::
216+ ++ chad
217+ |= c=^chad
218+ %+ frond -.c
219+ ?+ -.c ~
220+ %hung s+err.c
221+ ==
222+ --
223+--
jammer/tests/fixtures/jfix/mar/bill.hoonnew file+34
@@ -0,0 +1,34 @@
1+|_ bil=(list dude:gall)
2+++ grow
3+ |%
4+ ++ mime `^mime`[/text/x-bill (as-octs:mimes:html hoon)]
5+ ++ noun bil
6+ ++ hoon
7+ ^- @t
8+ |^ (crip (of-wall:format (wrap-lines (spit-duz bil))))
9+ ::
10+ ++ wrap-lines
11+ |= taz=wall
12+ ^- wall
13+ ?~ taz ["~"]~
14+ :- (weld ":~ " i.taz)
15+ %- snoc :_ "=="
16+ (turn t.taz |=(t=tape (weld " " t)))
17+ ::
18+ ++ spit-duz
19+ |= duz=(list dude:gall)
20+ ^- wall
21+ (turn duz |=(=dude:gall ['%' (trip dude)]))
22+ --
23+ ++ txt (to-wain:format hoon)
24+ --
25+++ grab
26+ |%
27+ ++ noun (list dude:gall)
28+ ++ mime
29+ |= [=mite len=@ud tex=@]
30+ ~_ tex
31+ !<((list dude:gall) (slap !>(~) (ream tex)))
32+ --
33+++ grad %noun
34+--
jammer/tests/fixtures/jfix/mar/css.hoonnew file+25
@@ -0,0 +1,25 @@
1+::
2+:::: /hoon/css/mar
3+ ::
4+/? 310
5+=, eyre
6+=, mimes:html
7+|_ mud=@t
8+++ grow :: convert to
9+ |%
10+ ++ mime [/text/css (as-octs mud)] :: convert to %mime
11+ ++ hymn :: convert to %hymn
12+ |^ html
13+ ++ style ;style
14+ ;- (trip mud)
15+ ==
16+ ++ html ;html:(head:"{style}" body)
17+ --
18+ --
19+++ grab
20+ |% :: convert from
21+ ++ mime |=([p=mite q=octs] (@t q.q))
22+ ++ noun @t :: clam from %noun
23+ --
24+++ grad %mime
25+--
jammer/tests/fixtures/jfix/mar/docket-0.hoonnew file+25
@@ -0,0 +1,25 @@
1+/+ dock=docket
2+|_ =docket:dock
3+++ grow
4+ |%
5+ ++ mime
6+ ^- ^mime
7+ [/text/x-docket (as-octt:mimes:html (spit-docket:mime:dock docket))]
8+ ++ noun docket
9+ ++ json (docket:enjs:dock docket)
10+ --
11+++ grab
12+ |%
13+ ::
14+ ++ mime
15+ |= [=mite len=@ud tex=@]
16+ ^- docket:dock
17+ %- need
18+ %- from-clauses:mime:dock
19+ !<((list clause:dock) (slap !>(~) (ream tex)))
20+
21+ ::
22+ ++ noun docket:dock
23+ --
24+++ grad %noun
25+--
jammer/tests/fixtures/jfix/mar/hoon.hoonnew file+303
@@ -0,0 +1,303 @@
1+|_ own=@t
2+::
3+++ grow :: convert to
4+ |%
5+ ++ mime `^mime`[/text/x-hoon (as-octs:mimes:html own)] :: convert to %mime
6+ ++ hymn
7+ ^- manx
8+ =/ to-wall
9+ |= =tape
10+ ^- wall
11+ %+ roll (flop tape)
12+ |= [char=@tD =wall]
13+ ?~ wall
14+ [[char ~] ~]
15+ ?: =('\0a' char)
16+ [~ wall]
17+ [[char i.wall] t.wall]
18+ ::
19+ =/ src=tape (trip own)
20+ =/ count=@ud (lent (to-wall src))
21+ =; [style=tape script=tape]
22+ ;html
23+ ;head
24+ ;title: source
25+ ;meta(charset "utf-8");
26+ ;meta(name "viewport", content "width=device-width, initial-scale=1");
27+ ;style: {style}
28+ ==
29+ ;body
30+ ;div.viewer
31+ :: ;div.viewer-bar
32+ :: ;span.viewer-name: {(scow %ud count)} lines
33+ :: ;button.copy-link(id "copy-link", hidden ""): copy link
34+ :: ==
35+ ;div.code
36+ ;pre.gutter(id "gutter");
37+ ;div.code-body
38+ ;div(id "hl");
39+ ;pre.code-pre(id "code"): {src}
40+ ==
41+ ==
42+ ==
43+ ;script: {script}
44+ ==
45+ ==
46+ ::
47+ :-
48+ ^~ ^= style
49+ ^- tape
50+ %- trip
51+ '''
52+ :root {
53+ --bg: #fbfbf9;
54+ --panel: #ffffff;
55+ --gutter-bg: #f3f3ef;
56+ --line: #e4e4dd;
57+ --fg: #2b2b2b;
58+ --fg-dim: #9a9a90;
59+ --sel: #fff7d6;
60+ --sel-edge: #f1e4a8;
61+ --accent: #4a6da7;
62+ --lh: 20px;
63+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
64+ "Liberation Mono", "Courier New", monospace;
65+ }
66+ * { box-sizing: border-box; }
67+ html, body {
68+ margin: 0;
69+ height: 100%;
70+ background: var(--bg);
71+ color: var(--fg);
72+ font-family: var(--mono);
73+ -webkit-font-smoothing: antialiased;
74+ }
75+ .viewer {
76+ height: 100vh;
77+ display: flex;
78+ flex-direction: column;
79+ padding: 16px;
80+ gap: 12px;
81+ }
82+ .viewer-bar {
83+ flex: 0 0 auto;
84+ display: flex;
85+ align-items: center;
86+ gap: 12px;
87+ font-size: 13px;
88+ color: var(--fg-dim);
89+ }
90+ .viewer-name {
91+ flex: 1 1 auto;
92+ min-width: 0;
93+ overflow: hidden;
94+ text-overflow: ellipsis;
95+ white-space: nowrap;
96+ color: var(--fg);
97+ }
98+ .copy-link {
99+ flex: 0 0 auto;
100+ padding: 4px 12px;
101+ border: 1px solid var(--line);
102+ border-radius: 6px;
103+ background: var(--panel);
104+ color: var(--fg-dim);
105+ font: inherit;
106+ font-size: 12px;
107+ cursor: pointer;
108+ transition: color .12s, border-color .12s;
109+ }
110+ .copy-link:hover { color: var(--accent); border-color: var(--accent); }
111+ .copy-link[hidden] { display: none; }
112+ .code {
113+ flex: 1 1 auto;
114+ min-height: 0;
115+ display: flex;
116+ align-items: flex-start;
117+ overflow: auto;
118+ background: var(--panel);
119+ border: 1px solid var(--line);
120+ border-radius: 8px;
121+ }
122+ .code-body {
123+ position: relative;
124+ flex: 1 1 auto;
125+ min-width: 0;
126+ width: max-content;
127+ }
128+ #gutter, #code {
129+ margin: 0;
130+ padding: 14px 0;
131+ font-family: var(--mono);
132+ font-size: 13px;
133+ line-height: var(--lh);
134+ white-space: pre;
135+ tab-size: 2;
136+ }
137+ #gutter {
138+ position: sticky;
139+ left: 0;
140+ z-index: 2;
141+ flex: 0 0 auto;
142+ padding-left: 16px;
143+ padding-right: 12px;
144+ text-align: right;
145+ color: var(--fg-dim);
146+ background: var(--gutter-bg);
147+ border-right: 1px solid var(--line);
148+ user-select: none;
149+ -webkit-user-select: none;
150+ cursor: pointer;
151+ }
152+ #code {
153+ display: block;
154+ padding-left: 14px;
155+ padding-right: 18px;
156+ color: var(--fg);
157+ }
158+ #hl {
159+ position: absolute;
160+ left: 0;
161+ right: 0;
162+ z-index: 0; /* under the text */
163+ display: none;
164+ background: var(--sel);
165+ box-shadow: inset 2px 0 0 var(--sel-edge);
166+ pointer-events: none;
167+ }
168+ #code { position: relative; z-index: 1; } /* text above the bar */
169+ :focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
170+ '''
171+ ^~ ^= code
172+ ^- tape
173+ %- trip
174+ '''
175+ (function () {
176+ "use strict";
177+ const panel = document.querySelector(".code"); // the scroll container
178+ const code = document.getElementById("code");
179+ const gutter = document.getElementById("gutter");
180+ const hl = document.getElementById("hl");
181+ if (!panel || !code || !gutter || !hl) return;
182+ const text = code.textContent;
183+ let lineCount = text.length ? text.split("\n").length : 1;
184+ if (text.endsWith("\n")) lineCount -= 1;
185+ if (lineCount < 1) lineCount = 1;
186+ (function fillGutter() {
187+ let s = "";
188+ for (let i = 1; i <= lineCount; i++) s += i + "\n";
189+ gutter.textContent = s;
190+ })();
191+ let lineH = 0;
192+ let codeTop = 0; // code's content-box top padding, the y-origin of line 1
193+ function measure() {
194+ const cs = getComputedStyle(code);
195+ const lh = parseFloat(cs.lineHeight);
196+ lineH = isNaN(lh) ? parseFloat(cs.fontSize) * 1.55 : lh;
197+ codeTop = parseFloat(cs.paddingTop) || 0;
198+ positionHighlight(); // keep the bar correct after a re-measure
199+ }
200+ let anchor = null;
201+ let selLo = 0;
202+ let selHi = 0;
203+ function positionHighlight() {
204+ if (!selLo) { hl.style.display = "none"; return; }
205+ hl.style.display = "block";
206+ hl.style.top = (codeTop + (selLo - 1) * lineH) + "px";
207+ hl.style.height = ((selHi - selLo + 1) * lineH) + "px";
208+ }
209+ function applySel(lo, hi) {
210+ selLo = lo;
211+ selHi = hi;
212+ positionHighlight();
213+ }
214+ function deselect() {
215+ selLo = selHi = 0;
216+ anchor = null;
217+ positionHighlight();
218+ history.replaceState(null, "", location.pathname + location.search);
219+ }
220+ function parseHash(hash) {
221+ const m = /^#L(\d+)(?:-L(\d+))?$/.exec(hash || "");
222+ if (!m) return null;
223+ let lo = parseInt(m[1], 10);
224+ let hi = m[2] ? parseInt(m[2], 10) : lo;
225+ if (hi < lo) { const t = lo; lo = hi; hi = t; }
226+ lo = Math.max(1, Math.min(lo, lineCount));
227+ hi = Math.max(1, Math.min(hi, lineCount));
228+ return [lo, hi];
229+ }
230+ function setHash(lo, hi) {
231+ const frag = lo === hi ? "L" + lo : "L" + lo + "-L" + hi;
232+ history.replaceState(null, "", "#" + frag);
233+ }
234+ function scrollToLine(n) {
235+ const y = codeTop + (n - 1) * lineH;
236+ if (y < panel.scrollTop || y > panel.scrollTop + panel.clientHeight) {
237+ panel.scrollTop = Math.max(0, y - panel.clientHeight / 2);
238+ }
239+ }
240+ function selectFromHash(scroll) {
241+ const r = parseHash(location.hash);
242+ if (!r) return false;
243+ anchor = r[0];
244+ applySel(r[0], r[1]);
245+ if (scroll) scrollToLine(r[0]);
246+ return true;
247+ }
248+ function lineFromEvent(e) {
249+ // y within the code content: pointer relative to the code pre's rendered
250+ // top, which already accounts for scroll since getBoundingClientRect is
251+ // viewport-relative and the pre moves as the panel scrolls.
252+ const rect = code.getBoundingClientRect();
253+ const y = e.clientY - rect.top - codeTop;
254+ let n = Math.floor(y / lineH) + 1;
255+ if (n < 1) n = 1;
256+ if (n > lineCount) n = lineCount;
257+ return n;
258+ }
259+ function onClick(e) {
260+ const inGutter = e.target === gutter;
261+ if (!inGutter && !e.shiftKey) return; // normal text interaction in code
262+ const n = lineFromEvent(e);
263+ if (!e.shiftKey && selLo === n && selHi === n) {
264+ deselect();
265+ return;
266+ }
267+ if (e.shiftKey && anchor !== null) {
268+ applySel(Math.min(anchor, n), Math.max(anchor, n));
269+ setHash(selLo, selHi);
270+ } else {
271+ anchor = n;
272+ applySel(n, n);
273+ setHash(n, n);
274+ }
275+ }
276+ gutter.addEventListener("click", onClick);
277+ code.addEventListener("click", onClick);
278+ document.addEventListener("keydown", function (e) {
279+ if (e.key === "Escape") deselect();
280+ });
281+ window.addEventListener("hashchange", function () {
282+ if (!selectFromHash(true)) deselect();
283+ });
284+ let rz = null;
285+ window.addEventListener("resize", function () {
286+ clearTimeout(rz);
287+ rz = setTimeout(measure, 100);
288+ });
289+ measure();
290+ selectFromHash(true);
291+ })();
292+ '''
293+ ++ txt
294+ (to-wain:format own)
295+ --
296+++ grab
297+ |% :: convert from
298+ ++ mime |=([p=mite q=octs] q.q)
299+ ++ noun @t :: clam from %noun
300+ ++ txt of-wain:format
301+ --
302+++ grad %txt
303+--
jammer/tests/fixtures/jfix/mar/html.hoonnew file+22
@@ -0,0 +1,22 @@
1+::
2+:::: /hoon/html/mar
3+ ::
4+/? 310
5+ ::
6+:::: compute
7+ ::
8+=, html
9+|_ htm=@t
10+++ grow :: convert to
11+ ^?
12+ |% ::
13+ ++ mime [/text/html (met 3 htm) htm] :: to %mime
14+ ++ hymn (need (de-xml htm)) :: to %hymn
15+ -- ::
16+++ grab ^?
17+ |% :: convert from
18+ ++ noun @t :: clam from %noun
19+ ++ mime |=([p=mite q=octs] q.q) :: retrieve form %mime
20+ --
21+++ grad %mime
22+--
jammer/tests/fixtures/jfix/mar/js.hoonnew file+25
@@ -0,0 +1,25 @@
1+::
2+:::: /hoon/js/mar
3+ ::
4+/? 310
5+::
6+=, eyre
7+|_ mud=@
8+++ grow
9+ |%
10+ ++ mime [/application/javascript (as-octs:mimes:html (@t mud))]
11+ ++ hymn :: convert to %hymn
12+ |^ html
13+ ++ script ;script
14+ ;- (trip (@t mud))
15+ ==
16+ ++ html ;html:(head:"{script}" body)
17+ --
18+ --
19+++ grab
20+ |% :: convert from
21+ ++ mime |=([p=mite q=octs] (@t q.q))
22+ ++ noun cord :: clam from %noun
23+ --
24+++ grad %mime
25+--
jammer/tests/fixtures/jfix/mar/json.hoonnew file+26
@@ -0,0 +1,26 @@
1+::
2+:::: /hoon/json/mar
3+ ::
4+/? 310
5+ ::
6+:::: compute
7+ ::
8+=, eyre
9+=, format
10+=, html
11+|_ jon=^json
12+::
13+++ grow :: convert to
14+ |%
15+ ++ mime [/application/json (as-octs:mimes -:txt)] :: convert to %mime
16+ ++ txt [(en:json jon)]~
17+ --
18+++ grab
19+ |% :: convert from
20+ ++ mime |=([p=mite q=octs] (fall (de:json (@t q.q)) *^json))
21+ ++ noun ^json :: clam from %noun
22+ ++ numb numb:enjs
23+ ++ time time:enjs
24+ --
25+++ grad %mime
26+--
jammer/tests/fixtures/jfix/mar/kelvin.hoonnew file+28
@@ -0,0 +1,28 @@
1+|_ kal=waft:clay
2+++ grow
3+ |%
4+ ++ mime `^mime`[/text/x-kelvin (as-octs:mimes:html hoon)]
5+ ++ noun kal
6+ ++ hoon
7+ %+ rap 3
8+ %+ turn
9+ %+ sort
10+ ~(tap in (waft-to-wefts:clay kal))
11+ |= [a=weft b=weft]
12+ ?: =(lal.a lal.b)
13+ (gte num.a num.b)
14+ (gte lal.a lal.b)
15+ |= =weft
16+ (rap 3 '[%' (scot %tas lal.weft) ' ' (scot %ud num.weft) ']\0a' ~)
17+ ::
18+ ++ txt (to-wain:format hoon)
19+ --
20+++ grab
21+ |%
22+ ++ noun waft:clay
23+ ++ mime
24+ |= [=mite len=@ud tex=@]
25+ (cord-to-waft:clay tex)
26+ --
27+++ grad %noun
28+--
jammer/tests/fixtures/jfix/mar/mime.hoonnew file+32
@@ -0,0 +1,32 @@
1+::
2+:::: /hoon/mime/mar
3+ ::
4+/? 310
5+::
6+|_ own=mime
7+++ grow
8+ ^?
9+ |%
10+ ++ jam `@`q.q.own
11+ --
12+::
13+++ grab :: convert from
14+ ^?
15+ |%
16+ ++ noun mime :: clam from %noun
17+ ++ tape
18+ |=(a=_"" [/application/x-urb-unknown (as-octt:mimes:html a)])
19+ --
20+++ grad
21+ ^?
22+ |%
23+ ++ form %mime
24+ ++ diff |=(mime +<)
25+ ++ pact |=(mime +<)
26+ ++ join |=([mime mime] `(unit mime)`~)
27+ ++ mash
28+ |= [[ship desk mime] [ship desk mime]]
29+ ^- mime
30+ ~|(%mime-mash !!)
31+ --
32+--
jammer/tests/fixtures/jfix/mar/noun.hoonnew file+22
@@ -0,0 +1,22 @@
1+::
2+:::: /hoon/noun/mar
3+ ::
4+/? 310
5+!:
6+:::: A minimal noun mark
7+|_ non=*
8+++ grab |%
9+ ++ noun *
10+ --
11+++ grow |%
12+ ++ mime [/application/x-urb-jam (as-octs:mimes:html (jam non))]
13+ --
14+++ grad
15+ |%
16+ ++ form %noun
17+ ++ diff |=(* +<)
18+ ++ pact |=(* +<)
19+ ++ join |=([* *] *(unit *))
20+ ++ mash |=([[ship desk *] [ship desk *]] `*`~|(%noun-mash !!))
21+ --
22+--
jammer/tests/fixtures/jfix/mar/ship.hoonnew file+20
@@ -0,0 +1,20 @@
1+|_ s=ship
2+++ grad %noun
3+++ grow
4+ |%
5+ ++ noun s
6+ ++ json s+(scot %p s)
7+ ++ mime
8+ ^- ^mime
9+ [/text/x-ship (as-octt:mimes:html (scow %p s))]
10+
11+ --
12+++ grab
13+ |%
14+ ++ noun ship
15+ ++ json (su:dejs:format ;~(pfix sig fed:ag))
16+ ++ mime
17+ |= [=mite len=@ tex=@]
18+ (slav %p (snag 0 (to-wain:format tex)))
19+ --
20+--
jammer/tests/fixtures/jfix/mar/txt-diff.hoonnew file+16
@@ -0,0 +1,16 @@
1+::
2+:::: /hoon/txt-diff/mar
3+ ::
4+/? 310
5+|_ txt-diff=(urge:clay cord)
6+::
7+++ grad %noun
8+++ grow
9+ |%
10+ ++ noun txt-diff
11+ --
12+++ grab :: convert from
13+ |%
14+ ++ noun (urge:clay cord) :: make from %noun
15+ --
16+--
jammer/tests/fixtures/jfix/mar/txt.hoonnew file+274
@@ -0,0 +1,274 @@
1+::
2+:::: /hoon/txt/mar
3+ ::
4+/? 310
5+::
6+=, clay
7+=, differ
8+=, format
9+=, mimes:html
10+|_ txt=wain
11+::
12+++ grab :: convert from
13+ |%
14+ ++ mime |=((pair mite octs) (to-wain q.q))
15+ ++ noun wain :: clam from %noun
16+ --
17+++ grow
18+ => v=.
19+ |%
20+ ++ mime => v [/text/plain (as-octs (of-wain txt))]
21+ --
22+++ grad
23+ |%
24+ ++ form %txt-diff
25+ ++ diff
26+ |= tyt=wain
27+ ^- (urge cord)
28+ (lusk txt tyt (loss txt tyt))
29+ ::
30+ ++ pact
31+ |= dif=(urge cord)
32+ ~| [%pacting dif]
33+ ^- wain
34+ (lurk txt dif)
35+ ::
36+ ++ join
37+ |= [ali=(urge cord) bob=(urge cord)]
38+ ^- (unit (urge cord))
39+ |^
40+ =. ali (clean ali)
41+ =. bob (clean bob)
42+ |- ^- (unit (urge cord))
43+ ?~ ali `bob
44+ ?~ bob `ali
45+ ?- -.i.ali
46+ %&
47+ ?- -.i.bob
48+ %&
49+ ?: =(p.i.ali p.i.bob)
50+ %+ bind $(ali t.ali, bob t.bob)
51+ |=(cud=(urge cord) [i.ali cud])
52+ ?: (gth p.i.ali p.i.bob)
53+ %+ bind $(p.i.ali (sub p.i.ali p.i.bob), bob t.bob)
54+ |=(cud=(urge cord) [i.bob cud])
55+ %+ bind $(ali t.ali, p.i.bob (sub p.i.bob p.i.ali))
56+ |=(cud=(urge cord) [i.ali cud])
57+ ::
58+ %|
59+ ?: =(p.i.ali (lent p.i.bob))
60+ %+ bind $(ali t.ali, bob t.bob)
61+ |=(cud=(urge cord) [i.bob cud])
62+ ?: (gth p.i.ali (lent p.i.bob))
63+ %+ bind $(p.i.ali (sub p.i.ali (lent p.i.bob)), bob t.bob)
64+ |=(cud=(urge cord) [i.bob cud])
65+ ~
66+ ==
67+ ::
68+ %|
69+ ?- -.i.bob
70+ %|
71+ ?. =(i.ali i.bob)
72+ ~
73+ %+ bind $(ali t.ali, bob t.bob)
74+ |=(cud=(urge cord) [i.ali cud])
75+ ::
76+ %&
77+ ?: =(p.i.bob (lent p.i.ali))
78+ %+ bind $(ali t.ali, bob t.bob)
79+ |=(cud=(urge cord) [i.ali cud])
80+ ?: (gth p.i.bob (lent p.i.ali))
81+ %+ bind $(ali t.ali, p.i.bob (sub p.i.bob (lent p.i.ali)))
82+ |=(cud=(urge cord) [i.ali cud])
83+ ~
84+ ==
85+ ==
86+ ++ clean :: clean
87+ |= wig=(urge cord)
88+ ^- (urge cord)
89+ ?~ wig ~
90+ ?~ t.wig wig
91+ ?: ?=(%& -.i.wig)
92+ ?: ?=(%& -.i.t.wig)
93+ $(wig [[%& (add p.i.wig p.i.t.wig)] t.t.wig])
94+ [i.wig $(wig t.wig)]
95+ ?: ?=(%| -.i.t.wig)
96+ $(wig [[%| (welp p.i.wig p.i.t.wig) (welp q.i.wig q.i.t.wig)] t.t.wig])
97+ [i.wig $(wig t.wig)]
98+ --
99+ ::
100+ ++ mash
101+ |= $: [als=ship ald=desk ali=(urge cord)]
102+ [bos=ship bod=desk bob=(urge cord)]
103+ ==
104+ ^- (urge cord)
105+ |^
106+ =. ali (clean ali)
107+ =. bob (clean bob)
108+ |- ^- (urge cord)
109+ ?~ ali bob
110+ ?~ bob ali
111+ ?- -.i.ali
112+ %&
113+ ?- -.i.bob
114+ %&
115+ ?: =(p.i.ali p.i.bob)
116+ [i.ali $(ali t.ali, bob t.bob)]
117+ ?: (gth p.i.ali p.i.bob)
118+ [i.bob $(p.i.ali (sub p.i.ali p.i.bob), bob t.bob)]
119+ [i.ali $(ali t.ali, p.i.bob (sub p.i.bob p.i.ali))]
120+ ::
121+ %|
122+ ?: =(p.i.ali (lent p.i.bob))
123+ [i.bob $(ali t.ali, bob t.bob)]
124+ ?: (gth p.i.ali (lent p.i.bob))
125+ [i.bob $(p.i.ali (sub p.i.ali (lent p.i.bob)), bob t.bob)]
126+ =/ [fic=(unce cord) ali=(urge cord) bob=(urge cord)]
127+ (resolve ali bob)
128+ [fic $(ali ali, bob bob)]
129+ :: ~ :: here, alice is good for a while, but not for the whole
130+ == :: length of bob's changes
131+ ::
132+ %|
133+ ?- -.i.bob
134+ %|
135+ =/ [fic=(unce cord) ali=(urge cord) bob=(urge cord)]
136+ (resolve ali bob)
137+ [fic $(ali ali, bob bob)]
138+ ::
139+ %&
140+ ?: =(p.i.bob (lent p.i.ali))
141+ [i.ali $(ali t.ali, bob t.bob)]
142+ ?: (gth p.i.bob (lent p.i.ali))
143+ [i.ali $(ali t.ali, p.i.bob (sub p.i.bob (lent p.i.ali)))]
144+ =/ [fic=(unce cord) ali=(urge cord) bob=(urge cord)]
145+ (resolve ali bob)
146+ [fic $(ali ali, bob bob)]
147+ ==
148+ ==
149+ ::
150+ ++ annotate :: annotate conflict
151+ |= $: ali=(list @t)
152+ bob=(list @t)
153+ bas=(list @t)
154+ ==
155+ ^- (list @t)
156+ %- zing
157+ ^- (list (list @t))
158+ %- flop
159+ ^- (list (list @t))
160+ :- :_ ~
161+ %^ cat 3 '<<<<<<<<<<<<'
162+ %^ cat 3 ' '
163+ %^ cat 3 `@t`(scot %p bos)
164+ %^ cat 3 '/'
165+ bod
166+
167+ :- bob
168+ :- ~['------------']
169+ :- bas
170+ :- ~['++++++++++++']
171+ :- ali
172+ :- :_ ~
173+ %^ cat 3 '>>>>>>>>>>>>'
174+ %^ cat 3 ' '
175+ %^ cat 3 `@t`(scot %p als)
176+ %^ cat 3 '/'
177+ ald
178+ ~
179+ ::
180+ ++ clean :: clean
181+ |= wig=(urge cord)
182+ ^- (urge cord)
183+ ?~ wig ~
184+ ?~ t.wig wig
185+ ?: ?=(%& -.i.wig)
186+ ?: ?=(%& -.i.t.wig)
187+ $(wig [[%& (add p.i.wig p.i.t.wig)] t.t.wig])
188+ [i.wig $(wig t.wig)]
189+ ?: ?=(%| -.i.t.wig)
190+ $(wig [[%| (welp p.i.wig p.i.t.wig) (welp q.i.wig q.i.t.wig)] t.t.wig])
191+ [i.wig $(wig t.wig)]
192+ ::
193+ ++ resolve
194+ |= [ali=(urge cord) bob=(urge cord)]
195+ ^- [fic=[%| p=(list cord) q=(list cord)] ali=(urge cord) bob=(urge cord)]
196+ =- [[%| bac (annotate alc boc bac)] ali bob]
197+ |- ^- $: $: bac=(list cord)
198+ alc=(list cord)
199+ boc=(list cord)
200+ ==
201+ ali=(urge cord)
202+ bob=(urge cord)
203+ ==
204+ ?~ ali [[~ ~ ~] ali bob]
205+ ?~ bob [[~ ~ ~] ali bob]
206+ ?- -.i.ali
207+ %&
208+ ?- -.i.bob
209+ %& [[~ ~ ~] ali bob] :: no conflict
210+ %|
211+ =+ lob=(lent p.i.bob)
212+ ?: =(lob p.i.ali)
213+ [[p.i.bob p.i.bob q.i.bob] t.ali t.bob]
214+ ?: (lth lob p.i.ali)
215+ [[p.i.bob p.i.bob q.i.bob] [[%& (sub p.i.ali lob)] t.ali] t.bob]
216+ =+ wat=(scag (sub lob p.i.ali) p.i.bob)
217+ =+ ^= res
218+ %= $
219+ ali t.ali
220+ bob [[%| (scag (sub lob p.i.ali) p.i.bob) ~] t.bob]
221+ ==
222+ :* :* (welp bac.res wat)
223+ (welp alc.res wat)
224+ (welp boc.res q.i.bob)
225+ ==
226+ ali.res
227+ bob.res
228+ ==
229+ ==
230+ ::
231+ %|
232+ ?- -.i.bob
233+ %&
234+ =+ loa=(lent p.i.ali)
235+ ?: =(loa p.i.bob)
236+ [[p.i.ali q.i.ali p.i.ali] t.ali t.bob]
237+ ?: (lth loa p.i.bob)
238+ [[p.i.ali q.i.ali p.i.ali] t.ali [[%& (sub p.i.bob loa)] t.bob]]
239+ =+ wat=(slag (sub loa p.i.bob) p.i.ali)
240+ =+ ^= res
241+ %= $
242+ ali [[%| (scag (sub loa p.i.bob) p.i.ali) ~] t.ali]
243+ bob t.bob
244+ ==
245+ :* :* (welp bac.res wat)
246+ (welp alc.res q.i.ali)
247+ (welp boc.res wat)
248+ ==
249+ ali.res
250+ bob.res
251+ ==
252+ ::
253+ %|
254+ =+ loa=(lent p.i.ali)
255+ =+ lob=(lent p.i.bob)
256+ ?: =(loa lob)
257+ [[p.i.ali q.i.ali q.i.bob] t.ali t.bob]
258+ =+ ^= res
259+ ?: (gth loa lob)
260+ $(ali [[%| (scag (sub loa lob) p.i.ali) ~] t.ali], bob t.bob)
261+ ~& [%scagging loa=loa pibob=p.i.bob slag=(scag loa p.i.bob)]
262+ $(ali t.ali, bob [[%| (scag (sub lob loa) p.i.bob) ~] t.bob])
263+ :* :* (welp bac.res ?:((gth loa lob) p.i.bob p.i.ali))
264+ (welp alc.res q.i.ali)
265+ (welp boc.res q.i.bob)
266+ ==
267+ ali.res
268+ bob.res
269+ ==
270+ ==
271+ ==
272+ --
273+ --
274+--
jammer/tests/fixtures/jfix/notes.txtnew file+4
@@ -0,0 +1,4 @@
1+line 1
2+line 2
3+
4+line 4
jammer/tests/fixtures/jfix/sur/docket.hoonnew file+82
@@ -0,0 +1,82 @@
1+|%
2+::
3++$ version
4+ [major=@ud minor=@ud patch=@ud]
5+::
6++$ glob (map path mime)
7+::
8++$ url cord
9+:: $glob-location: How to retrieve a glob
10+::
11++$ glob-reference
12+ [hash=@uvH location=glob-location]
13+::
14++$ glob-location
15+ $% [%http =url]
16+ [%ames =ship]
17+ ==
18+:: $href: Where a tile links to
19+::
20++$ href
21+ $% [%glob base=term =glob-reference]
22+ [%site =path]
23+ ==
24+:: $chad: State of a docket
25+::
26++$ chad
27+ $~ [%install ~]
28+ $% :: Done
29+ [%glob =glob]
30+ [%site ~]
31+ :: Waiting
32+ [%install ~]
33+ [%suspend glob=(unit glob)]
34+ :: Error
35+ [%hung err=cord]
36+ ==
37+::
38+:: $charge: A realized $docket
39+::
40++$ charge
41+ $: =docket
42+ =chad
43+ ==
44+::
45+:: $clause: A key and value, as part of a docket
46+::
47+:: Only used to parse $docket
48+::
49++$ clause
50+ $% [%title title=@t]
51+ [%info info=@t]
52+ [%color color=@ux]
53+ [%glob-http url=cord hash=@uvH]
54+ [%glob-ames =ship hash=@uvH]
55+ [%image =url]
56+ [%site =path]
57+ [%base base=term]
58+ [%version =version]
59+ [%website website=url]
60+ [%license license=cord]
61+ ==
62+::
63+:: $docket: A description of JS bundles for a desk
64+::
65++$ docket
66+ $: %1
67+ title=@t
68+ info=@t
69+ color=@ux
70+ =href
71+ image=(unit url)
72+ =version
73+ website=url
74+ license=cord
75+ ==
76+::
77++$ charge-update
78+ $% [%initial initial=(map desk charge)]
79+ [%add-charge =desk =charge]
80+ [%del-charge =desk]
81+ ==
82+--
jammer/tests/fixtures/jfix/sys.kelvinnew file+2
@@ -0,0 +1,2 @@
1+[%zuse 409]
2+[%zuse 408]
jammer/tests/fixtures/jfix/web/app.jsnew file+1
@@ -0,0 +1 @@
1+console.log("hi")
jammer/tests/fixtures/jfix/web/index.htmlnew file+2
@@ -0,0 +1,2 @@
1+<!doctype html>
2+<p>hi</p>
jammer/tests/fixtures/jfix/web/style.cssnew file+1
@@ -0,0 +1 @@
1+p { color: red }
jammer/tests/marks.rsnew file+148
@@ -0,0 +1,148 @@
1+//! Rendering native pages, checked against output from a ship.
2+
3+use jammer::jam::cue;
4+use jammer::marks::to_bytes;
5+use jammer::noun::Arena;
6+
7+fn ux(text: &str) -> Vec<u8> {
8+ let hex: String = text
9+ .trim_start_matches("0x")
10+ .chars()
11+ .filter(|&c| c != '.')
12+ .collect();
13+ let hex = if hex.len() % 2 == 1 {
14+ format!("0{hex}")
15+ } else {
16+ hex
17+ };
18+ let mut bytes: Vec<u8> = (0..hex.len())
19+ .step_by(2)
20+ .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
21+ .collect();
22+ bytes.reverse();
23+ bytes
24+}
25+
26+/// Render the noun `(cue jam)` with `mark`.
27+fn render(mark: &str, jam: &str) -> String {
28+ let mut arena = Arena::new();
29+ let noun = cue(&mut arena, &ux(jam)).unwrap().noun;
30+ String::from_utf8(to_bytes(&arena, mark.as_bytes(), noun).unwrap()).unwrap()
31+}
32+
33+#[test]
34+fn json_matches_en_json() {
35+ // `(jam j)` and `(en:json:html j)` on a ship.
36+ let cases = [
37+ (
38+ "0x55a.21f8.e1cf.8387.82b6.946e.3e0b.5a21.d878.39b8.2cd9.382d.39a5.1b97.82b9.b29a.9718.9a03.7707.1705.6937.3f05.dfc1",
39+ r#"{"c":true,"a":"x","g":{},"f":[],"d":null,"b":1.5e3,"e":false}"#,
40+ ),
41+ (
42+ "0x7c.5e78.4127.39c4.4153.8640.fed8.cac8.40dc.401a.e412.e840.14e6.b8c4.44e3.9a00.e7c1",
43+ r#""q\"b\\s\n t\u0009r\u000d n del\u007f é ✓ </>""#,
44+ ),
45+ (
46+ "0xa9.b9f0.659c.4b74.1863.6265.746f.7571.2220.6874.6977.2079.656b.fa00.bbf8.2d49.af06.5656.4f81.cf82.c9ae.1437.7059.c3c1",
47+ r#"[null,0,["deep",[]],{"":"","key with \"quote\"":-1}]"#,
48+ ),
49+ (
50+ "0x6.e087.4328.71d1.b898.e817.7043.ca84.746e.5415.e684.746f.3417.7043.e318.71d1.b918.e817.7043.e284.746f.1415.de84.746e.f417.7043.c684.746e.3415.da84.746e.d416.9d68.4746.eb41.5edc.71d1.b818.e816.73a1.1d1b.9d05.68c7.bb83.a505.dfc1",
51+ r#"{"11":11,"9":9,"2":2,"4":4,"12":12,"7":7,"8":8,"6":6,"1":1,"10":10,"5":5,"3":3}"#,
52+ ),
53+ ];
54+ for (jam, expected) in cases {
55+ assert_eq!(render("json", jam), expected);
56+ }
57+}
58+
59+#[test]
60+fn octs_marks_keep_trailing_zeros() {
61+ let mut arena = Arena::new();
62+ let size = arena.atom_u64(5);
63+ let data = arena.atom(&[1, 0, 2]);
64+ let octs = arena.cell(size, data);
65+ for mark in ["woff2", "wasm"] {
66+ assert_eq!(
67+ to_bytes(&arena, mark.as_bytes(), octs).unwrap(),
68+ [1, 0, 2, 0, 0]
69+ );
70+ }
71+ // the same shape wrapped in a mime
72+ let mite = arena.path(&["font", "woff2"]);
73+ let mime = arena.cell(mite, octs);
74+ assert_eq!(to_bytes(&arena, b"mime", mime).unwrap(), [1, 0, 2, 0, 0]);
75+}
76+
77+#[test]
78+fn noun_marks_write_the_jam() {
79+ // `(jam [1 2 3])` on a ship, as the `%noun` mark's grow:mime writes it
80+ let mut arena = Arena::new();
81+ let noun = cue(&mut arena, &ux("0x3.4487.1dce.aded.df01"))
82+ .unwrap()
83+ .noun;
84+ let (_, value) = arena.as_cell(noun).unwrap();
85+ let bytes = to_bytes(&arena, b"noun", value).unwrap();
86+ let mut other = Arena::new();
87+ let back = cue(&mut other, &bytes).unwrap().noun;
88+ assert_eq!(jammer::jam::jam(&other, back), bytes);
89+ assert_eq!(bytes, ux("0x34.4871"));
90+}
91+
92+#[test]
93+fn unknown_marks() {
94+ let mut arena = Arena::new();
95+ let text = arena.cord("hello");
96+ assert_eq!(to_bytes(&arena, b"custom", text).unwrap(), b"hello");
97+ let cell = arena.cell(text, text);
98+ assert!(to_bytes(&arena, b"custom", cell).is_err());
99+}
100+
101+#[test]
102+fn bad_nouns_are_errors_not_panics() {
103+ let mut arena = Arena::new();
104+ let atom = arena.cord("x");
105+ let cell = arena.cell(atom, atom);
106+ for mark in [
107+ "mime", "txt", "kelvin", "bill", "docket-0", "json", "ship", "woff2",
108+ ] {
109+ // whichever of these shapes is wrong for the mark must be rejected cleanly
110+ let _ = to_bytes(&arena, mark.as_bytes(), atom);
111+ let _ = to_bytes(&arena, mark.as_bytes(), cell);
112+ }
113+ // a mime claiming a petabyte is refused, not allocated
114+ let huge = arena.atom_u64(1 << 50);
115+ let octs = arena.cell(huge, atom);
116+ let mite = arena.path(&["text", "plain"]);
117+ let mime = arena.cell(mite, octs);
118+ assert!(to_bytes(&arena, b"mime", mime).is_err());
119+}
120+
121+/// `scot %tas` keeps NUL bytes inside kelvin labels (checked on a ship by
122+/// mounting `[[%1 ~] (sy ~[['Z s' 1] [0x62.0061 2] [0xff 3] [0x6100 4]])]`).
123+#[test]
124+fn kelvin_labels_keep_nul_bytes() {
125+ let mut arena = Arena::new();
126+ let wefts: Vec<_> = [
127+ (&b"Z s"[..], 1u64),
128+ (&[0x61, 0x00, 0x62][..], 2),
129+ (&[0xff][..], 3),
130+ (&[0x00, 0x61][..], 4),
131+ ]
132+ .iter()
133+ .map(|&(lal, num)| {
134+ let lal = arena.atom(lal);
135+ let num = arena.atom_u64(num);
136+ arena.cell(lal, num)
137+ })
138+ .collect();
139+ let set = jammer::treap::set_from_items(&mut arena, &wefts);
140+ let one = arena.atom_u64(1);
141+ let null = arena.atom(&[]);
142+ let tag = arena.cell(one, null);
143+ let waft = arena.cell(tag, set);
144+ assert_eq!(
145+ to_bytes(&arena, b"kelvin", waft).unwrap(),
146+ b"[%Z s 1]\n[%a\0b 2]\n[%\0a 4]\n[%\xff 3]\n"
147+ );
148+}
jammer/tests/vectors.rsnew file+115
@@ -0,0 +1,115 @@
1+//! Table-driven checks against output from a ship (`tests/vectors/`).
2+
3+use std::fs;
4+use std::path::Path;
5+
6+use jammer::aura;
7+use jammer::jam::cue;
8+use jammer::marks::{MarkError, to_bytes};
9+use jammer::noun::Arena;
10+
11+fn table(name: &str) -> String {
12+ fs::read_to_string(
13+ Path::new(env!("CARGO_MANIFEST_DIR"))
14+ .join("tests/vectors")
15+ .join(name),
16+ )
17+ .unwrap()
18+}
19+
20+/// Hex digits in file order to bytes.
21+fn hex(text: &str) -> Vec<u8> {
22+ (0..text.len())
23+ .step_by(2)
24+ .map(|i| u8::from_str_radix(&text[i..i + 2], 16).unwrap())
25+ .collect()
26+}
27+
28+/// A Hoon atom literal (`0x1.abcd` or `123.456`) as little-endian bytes.
29+fn literal(text: &str) -> Vec<u8> {
30+ if let Some(digits) = text.strip_prefix("0x") {
31+ let digits: String = digits.chars().filter(|&c| c != '.').collect();
32+ let digits = if digits.len() % 2 == 1 {
33+ format!("0{digits}")
34+ } else {
35+ digits
36+ };
37+ let mut bytes = hex(&digits);
38+ bytes.reverse();
39+ bytes
40+ } else {
41+ let digits: String = text.chars().filter(|&c| c != '.').collect();
42+ digits.parse::<u128>().unwrap().to_le_bytes().to_vec()
43+ }
44+}
45+
46+#[test]
47+fn marks_render_like_grow_mime() {
48+ let mut checked = 0;
49+ for line in table("marks.tsv").lines().filter(|l| !l.starts_with('#')) {
50+ let fields: Vec<&str> = line.split('\t').collect();
51+ let (mark, sample, jam, file) = (fields[0], fields[1], fields[2], fields[3]);
52+ let mut arena = Arena::new();
53+ let noun = cue(&mut arena, &hex(jam)).unwrap().noun;
54+ let rendered = to_bytes(&arena, mark.as_bytes(), noun);
55+ match mark {
56+ // printers jammer does not implement must say so, not guess
57+ "hymn" | "urb" | "snip" | "eth-txs" => {
58+ assert!(
59+ matches!(rendered, Err(MarkError::Unsupported(_))),
60+ "%{mark} {sample}"
61+ )
62+ }
63+ _ => {
64+ assert_eq!(rendered.unwrap(), hex(file), "%{mark} {sample}");
65+ checked += 1;
66+ }
67+ }
68+ }
69+ assert!(checked > 200);
70+}
71+
72+#[test]
73+fn dockets_render_like_spit_docket() {
74+ let mut checked = 0;
75+ for line in table("dockets.txt").lines().filter(|l| !l.starts_with('#')) {
76+ let fields: Vec<&str> = line.split('|').map(str::trim).collect();
77+ let (name, jam, file) = (fields[0], fields[2], fields[3]);
78+ let mut arena = Arena::new();
79+ let noun = cue(&mut arena, &hex(jam)).unwrap().noun;
80+ assert_eq!(
81+ to_bytes(&arena, b"docket-0", noun).unwrap(),
82+ hex(file),
83+ "{name}"
84+ );
85+ checked += 1;
86+ }
87+ assert!(checked > 100);
88+}
89+
90+#[test]
91+fn auras_print_like_scow() {
92+ let mut checked = 0;
93+ for line in table("auras.txt").lines().filter(|l| !l.starts_with('#')) {
94+ let Some((expr, expected)) = line.split_once(" => ") else {
95+ continue;
96+ };
97+ let expected = expected.trim().trim_matches('"');
98+ let inner = expr
99+ .trim()
100+ .trim_start_matches("(scow %")
101+ .trim_end_matches(')');
102+ let (which, arg) = inner.split_once(' ').unwrap();
103+ let atom = literal(arg);
104+ let printed = match which {
105+ "p" => aura::p(&atom),
106+ "ud" => aura::ud(&atom),
107+ "ux" => aura::ux(&atom),
108+ "uv" => aura::uv(&atom),
109+ other => panic!("unknown aura {other}"),
110+ };
111+ assert_eq!(printed, expected, "{expr}");
112+ checked += 1;
113+ }
114+ assert!(checked > 1000);
115+}
jammer/tests/vectors/auras.txtnew file+1305
@@ -0,0 +1,1305 @@
1+# (scow ...) from `urbit eval` (vere 4.6)
2+(scow %p 0) => "~zod"
3+(scow %p 0x1) => "~nec"
4+(scow %p 0x2) => "~bud"
5+(scow %p 0x3) => "~wes"
6+(scow %p 0x4) => "~sev"
7+(scow %p 0x5) => "~per"
8+(scow %p 0x6) => "~sut"
9+(scow %p 0x7) => "~let"
10+(scow %p 0x8) => "~ful"
11+(scow %p 0x9) => "~pen"
12+(scow %p 0xa) => "~syt"
13+(scow %p 0xb) => "~dur"
14+(scow %p 0xc) => "~wep"
15+(scow %p 0xd) => "~ser"
16+(scow %p 0xe) => "~wyl"
17+(scow %p 0xf) => "~sun"
18+(scow %p 0x10) => "~ryp"
19+(scow %p 0x11) => "~syx"
20+(scow %p 0x12) => "~dyr"
21+(scow %p 0x13) => "~nup"
22+(scow %p 0x14) => "~heb"
23+(scow %p 0x15) => "~peg"
24+(scow %p 0x16) => "~lup"
25+(scow %p 0x17) => "~dep"
26+(scow %p 0x18) => "~dys"
27+(scow %p 0x19) => "~put"
28+(scow %p 0x1a) => "~lug"
29+(scow %p 0x1b) => "~hec"
30+(scow %p 0x1c) => "~ryt"
31+(scow %p 0x1d) => "~tyv"
32+(scow %p 0x1e) => "~syd"
33+(scow %p 0x1f) => "~nex"
34+(scow %p 0x20) => "~lun"
35+(scow %p 0x21) => "~mep"
36+(scow %p 0x22) => "~lut"
37+(scow %p 0x23) => "~sep"
38+(scow %p 0x24) => "~pes"
39+(scow %p 0x25) => "~del"
40+(scow %p 0x26) => "~sul"
41+(scow %p 0x27) => "~ped"
42+(scow %p 0x28) => "~tem"
43+(scow %p 0x29) => "~led"
44+(scow %p 0x2a) => "~tul"
45+(scow %p 0x2b) => "~met"
46+(scow %p 0x2c) => "~wen"
47+(scow %p 0x2d) => "~byn"
48+(scow %p 0x2e) => "~hex"
49+(scow %p 0x2f) => "~feb"
50+(scow %p 0x30) => "~pyl"
51+(scow %p 0x31) => "~dul"
52+(scow %p 0x32) => "~het"
53+(scow %p 0x33) => "~mev"
54+(scow %p 0x34) => "~rut"
55+(scow %p 0x35) => "~tyl"
56+(scow %p 0x36) => "~wyd"
57+(scow %p 0x37) => "~tep"
58+(scow %p 0x38) => "~bes"
59+(scow %p 0x39) => "~dex"
60+(scow %p 0x3a) => "~sef"
61+(scow %p 0x3b) => "~wyc"
62+(scow %p 0x3c) => "~bur"
63+(scow %p 0x3d) => "~der"
64+(scow %p 0x3e) => "~nep"
65+(scow %p 0x3f) => "~pur"
66+(scow %p 0x40) => "~rys"
67+(scow %p 0x41) => "~reb"
68+(scow %p 0x42) => "~den"
69+(scow %p 0x43) => "~nut"
70+(scow %p 0x44) => "~sub"
71+(scow %p 0x45) => "~pet"
72+(scow %p 0x46) => "~rul"
73+(scow %p 0x47) => "~syn"
74+(scow %p 0x48) => "~reg"
75+(scow %p 0x49) => "~tyd"
76+(scow %p 0x4a) => "~sup"
77+(scow %p 0x4b) => "~sem"
78+(scow %p 0x4c) => "~wyn"
79+(scow %p 0x4d) => "~rec"
80+(scow %p 0x4e) => "~meg"
81+(scow %p 0x4f) => "~net"
82+(scow %p 0x50) => "~sec"
83+(scow %p 0x51) => "~mul"
84+(scow %p 0x52) => "~nym"
85+(scow %p 0x53) => "~tev"
86+(scow %p 0x54) => "~web"
87+(scow %p 0x55) => "~sum"
88+(scow %p 0x56) => "~mut"
89+(scow %p 0x57) => "~nyx"
90+(scow %p 0x58) => "~rex"
91+(scow %p 0x59) => "~teb"
92+(scow %p 0x5a) => "~fus"
93+(scow %p 0x5b) => "~hep"
94+(scow %p 0x5c) => "~ben"
95+(scow %p 0x5d) => "~mus"
96+(scow %p 0x5e) => "~wyx"
97+(scow %p 0x5f) => "~sym"
98+(scow %p 0x60) => "~sel"
99+(scow %p 0x61) => "~ruc"
100+(scow %p 0x62) => "~dec"
101+(scow %p 0x63) => "~wex"
102+(scow %p 0x64) => "~syr"
103+(scow %p 0x65) => "~wet"
104+(scow %p 0x66) => "~dyl"
105+(scow %p 0x67) => "~myn"
106+(scow %p 0x68) => "~mes"
107+(scow %p 0x69) => "~det"
108+(scow %p 0x6a) => "~bet"
109+(scow %p 0x6b) => "~bel"
110+(scow %p 0x6c) => "~tux"
111+(scow %p 0x6d) => "~tug"
112+(scow %p 0x6e) => "~myr"
113+(scow %p 0x6f) => "~pel"
114+(scow %p 0x70) => "~syp"
115+(scow %p 0x71) => "~ter"
116+(scow %p 0x72) => "~meb"
117+(scow %p 0x73) => "~set"
118+(scow %p 0x74) => "~dut"
119+(scow %p 0x75) => "~deg"
120+(scow %p 0x76) => "~tex"
121+(scow %p 0x77) => "~sur"
122+(scow %p 0x78) => "~fel"
123+(scow %p 0x79) => "~tud"
124+(scow %p 0x7a) => "~nux"
125+(scow %p 0x7b) => "~rux"
126+(scow %p 0x7c) => "~ren"
127+(scow %p 0x7d) => "~wyt"
128+(scow %p 0x7e) => "~nub"
129+(scow %p 0x7f) => "~med"
130+(scow %p 0x80) => "~lyt"
131+(scow %p 0x81) => "~dus"
132+(scow %p 0x82) => "~neb"
133+(scow %p 0x83) => "~rum"
134+(scow %p 0x84) => "~tyn"
135+(scow %p 0x85) => "~seg"
136+(scow %p 0x86) => "~lyx"
137+(scow %p 0x87) => "~pun"
138+(scow %p 0x88) => "~res"
139+(scow %p 0x89) => "~red"
140+(scow %p 0x8a) => "~fun"
141+(scow %p 0x8b) => "~rev"
142+(scow %p 0x8c) => "~ref"
143+(scow %p 0x8d) => "~mec"
144+(scow %p 0x8e) => "~ted"
145+(scow %p 0x8f) => "~rus"
146+(scow %p 0x90) => "~bex"
147+(scow %p 0x91) => "~leb"
148+(scow %p 0x92) => "~dux"
149+(scow %p 0x93) => "~ryn"
150+(scow %p 0x94) => "~num"
151+(scow %p 0x95) => "~pyx"
152+(scow %p 0x96) => "~ryg"
153+(scow %p 0x97) => "~ryx"
154+(scow %p 0x98) => "~fep"
155+(scow %p 0x99) => "~tyr"
156+(scow %p 0x9a) => "~tus"
157+(scow %p 0x9b) => "~tyc"
158+(scow %p 0x9c) => "~leg"
159+(scow %p 0x9d) => "~nem"
160+(scow %p 0x9e) => "~fer"
161+(scow %p 0x9f) => "~mer"
162+(scow %p 0xa0) => "~ten"
163+(scow %p 0xa1) => "~lus"
164+(scow %p 0xa2) => "~nus"
165+(scow %p 0xa3) => "~syl"
166+(scow %p 0xa4) => "~tec"
167+(scow %p 0xa5) => "~mex"
168+(scow %p 0xa6) => "~pub"
169+(scow %p 0xa7) => "~rym"
170+(scow %p 0xa8) => "~tuc"
171+(scow %p 0xa9) => "~fyl"
172+(scow %p 0xaa) => "~lep"
173+(scow %p 0xab) => "~deb"
174+(scow %p 0xac) => "~ber"
175+(scow %p 0xad) => "~mug"
176+(scow %p 0xae) => "~hut"
177+(scow %p 0xaf) => "~tun"
178+(scow %p 0xb0) => "~byl"
179+(scow %p 0xb1) => "~sud"
180+(scow %p 0xb2) => "~pem"
181+(scow %p 0xb3) => "~dev"
182+(scow %p 0xb4) => "~lur"
183+(scow %p 0xb5) => "~def"
184+(scow %p 0xb6) => "~bus"
185+(scow %p 0xb7) => "~bep"
186+(scow %p 0xb8) => "~run"
187+(scow %p 0xb9) => "~mel"
188+(scow %p 0xba) => "~pex"
189+(scow %p 0xbb) => "~dyt"
190+(scow %p 0xbc) => "~byt"
191+(scow %p 0xbd) => "~typ"
192+(scow %p 0xbe) => "~lev"
193+(scow %p 0xbf) => "~myl"
194+(scow %p 0xc0) => "~wed"
195+(scow %p 0xc1) => "~duc"
196+(scow %p 0xc2) => "~fur"
197+(scow %p 0xc3) => "~fex"
198+(scow %p 0xc4) => "~nul"
199+(scow %p 0xc5) => "~luc"
200+(scow %p 0xc6) => "~len"
201+(scow %p 0xc7) => "~ner"
202+(scow %p 0xc8) => "~lex"
203+(scow %p 0xc9) => "~rup"
204+(scow %p 0xca) => "~ned"
205+(scow %p 0xcb) => "~lec"
206+(scow %p 0xcc) => "~ryd"
207+(scow %p 0xcd) => "~lyd"
208+(scow %p 0xce) => "~fen"
209+(scow %p 0xcf) => "~wel"
210+(scow %p 0xd0) => "~nyd"
211+(scow %p 0xd1) => "~hus"
212+(scow %p 0xd2) => "~rel"
213+(scow %p 0xd3) => "~rud"
214+(scow %p 0xd4) => "~nes"
215+(scow %p 0xd5) => "~hes"
216+(scow %p 0xd6) => "~fet"
217+(scow %p 0xd7) => "~des"
218+(scow %p 0xd8) => "~ret"
219+(scow %p 0xd9) => "~dun"
220+(scow %p 0xda) => "~ler"
221+(scow %p 0xdb) => "~nyr"
222+(scow %p 0xdc) => "~seb"
223+(scow %p 0xdd) => "~hul"
224+(scow %p 0xde) => "~ryl"
225+(scow %p 0xdf) => "~lud"
226+(scow %p 0xe0) => "~rem"
227+(scow %p 0xe1) => "~lys"
228+(scow %p 0xe2) => "~fyn"
229+(scow %p 0xe3) => "~wer"
230+(scow %p 0xe4) => "~ryc"
231+(scow %p 0xe5) => "~sug"
232+(scow %p 0xe6) => "~nys"
233+(scow %p 0xe7) => "~nyl"
234+(scow %p 0xe8) => "~lyn"
235+(scow %p 0xe9) => "~dyn"
236+(scow %p 0xea) => "~dem"
237+(scow %p 0xeb) => "~lux"
238+(scow %p 0xec) => "~fed"
239+(scow %p 0xed) => "~sed"
240+(scow %p 0xee) => "~bec"
241+(scow %p 0xef) => "~mun"
242+(scow %p 0xf0) => "~lyr"
243+(scow %p 0xf1) => "~tes"
244+(scow %p 0xf2) => "~mud"
245+(scow %p 0xf3) => "~nyt"
246+(scow %p 0xf4) => "~byr"
247+(scow %p 0xf5) => "~sen"
248+(scow %p 0xf6) => "~weg"
249+(scow %p 0xf7) => "~fyr"
250+(scow %p 0xf8) => "~mur"
251+(scow %p 0xf9) => "~tel"
252+(scow %p 0xfa) => "~rep"
253+(scow %p 0xfb) => "~teg"
254+(scow %p 0xfc) => "~pec"
255+(scow %p 0xfd) => "~nel"
256+(scow %p 0xfe) => "~nev"
257+(scow %p 0xff) => "~fes"
258+(scow %p 0x100) => "~marzod"
259+(scow %p 0x101) => "~marnec"
260+(scow %p 0x1234) => "~doprut"
261+(scow %p 0xfffe) => "~fipnev"
262+(scow %p 0xffff) => "~fipfes"
263+(scow %p 0xf869) => "~pondet"
264+(scow %p 0xc835) => "~hintyl"
265+(scow %p 0x71d3) => "~tagrud"
266+(scow %p 0x1ee9) => "~sildyn"
267+(scow %p 0x2ea) => "~bindem"
268+(scow %p 0x1834) => "~dorrut"
269+(scow %p 0xea35) => "~dantyl"
270+(scow %p 0xf249) => "~pittyd"
271+(scow %p 0xf173) => "~talset"
272+(scow %p 0xcf97) => "~topryx"
273+(scow %p 0x960c) => "~ravwep"
274+(scow %p 0x9f0) => "~lislyr"
275+(scow %p 0xaceb) => "~batlux"
276+(scow %p 0xb26e) => "~linmyr"
277+(scow %p 0x167c) => "~hopren"
278+(scow %p 0x1a32) => "~hodhet"
279+(scow %p 0xc538) => "~hobbes"
280+(scow %p 0x5bc9) => "~fonrup"
281+(scow %p 0x3d9f) => "~dasmer"
282+(scow %p 0x576) => "~littex"
283+(scow %p 0x8e3) => "~fidwer"
284+(scow %p 0xfd97) => "~matryx"
285+(scow %p 0xcaed) => "~massed"
286+(scow %p 0x50e) => "~litwyl"
287+(scow %p 0x5999) => "~somtyr"
288+(scow %p 0xa6f8) => "~lommur"
289+(scow %p 0xa08f) => "~tacrus"
290+(scow %p 0x7cf7) => "~nisfyr"
291+(scow %p 0x9ea6) => "~fanpub"
292+(scow %p 0x77ab) => "~siddeb"
293+(scow %p 0x2707) => "~liblet"
294+(scow %p 0x185e) => "~dorwyx"
295+(scow %p 0x2fdf) => "~dotlud"
296+(scow %p 0xb7a2) => "~barnus"
297+(scow %p 0x1e4a) => "~silsup"
298+(scow %p 0xd968) => "~dinmes"
299+(scow %p 0x4be) => "~samlev"
300+(scow %p 0x81ca) => "~disned"
301+(scow %p 0x7d9b) => "~woltyc"
302+(scow %p 0xe266) => "~fitdyl"
303+(scow %p 0x1.0000) => "~dapnep-ronmyl"
304+(scow %p 0x1.0001) => "~milrys-soglec"
305+(scow %p 0xffff.ffff) => "~dostec-risfen"
306+(scow %p 0xffff.fffe) => "~fastec-danwyc"
307+(scow %p 0x1.21d2) => "~fipfes-loppeg"
308+(scow %p 0x1.62d9) => "~fipfes-hollyx"
309+(scow %p 0x1.b8a7) => "~fipfes-rapryd"
310+(scow %p 0x3.2e4b) => "~fipfes-tirtes"
311+(scow %p 0xf704.c9ff) => "~hasheb-difryc"
312+(scow %p 0x3ff5.bb8c) => "~micryn-davsev"
313+(scow %p 0x1088.2756) => "~wolbur-rosmel"
314+(scow %p 0xaad2.4268) => "~lomryc-midsel"
315+(scow %p 0x8a85.c15c) => "~rigrus-sitmul"
316+(scow %p 0xe98e.7c35) => "~monfen-lasnev"
317+(scow %p 0x7746.20d7) => "~fidwep-tobnum"
318+(scow %p 0x118e.c10e) => "~matmeg-borzod"
319+(scow %p 0x99fa.823c) => "~dospel-tapled"
320+(scow %p 0xacd6.1e66) => "~siprut-nolrux"
321+(scow %p 0x16c5.0a33) => "~sornys-pilseb"
322+(scow %p 0x83da.fca7) => "~doctes-ricdux"
323+(scow %p 0x943a.8520) => "~samwen-hocneb"
324+(scow %p 0xbc5.621b) => "~ritdep-fosdep"
325+(scow %p 0x4504.8e22) => "~wicrun-ripteb"
326+(scow %p 0xd30b.ad4b) => "~fogpes-noptux"
327+(scow %p 0x8e73.5d57) => "~riltes-walfyl"
328+(scow %p 0x1026.dbe5) => "~havset-fitfet"
329+(scow %p 0xa507.bfc5) => "~dannux-nibweb"
330+(scow %p 0x4d6c.fd8f) => "~halted-donpyl"
331+(scow %p 0x7b26.331f) => "~nidbyt-rabhut"
332+(scow %p 0xd9a6.4a0d) => "~sidnub-rolful"
333+(scow %p 0x5af1.f3e0) => "~pagdus-digdep"
334+(scow %p 0x4437.5862) => "~natnyr-mogbyn"
335+(scow %p 0x3fc9.6ee6) => "~ricsyl-nopweb"
336+(scow %p 0xa7ce.a6c2) => "~dinsen-borsup"
337+(scow %p 0xeefb.df23) => "~patlec-nimdux"
338+(scow %p 0x3336.4fc7) => "~sattev-narbyn"
339+(scow %p 0x7412.3cb0) => "~fogden-hidreg"
340+(scow %p 0xe066.e3eb) => "~torpun-sarmul"
341+(scow %p 0x87b9.a518) => "~tadlyx-fothus"
342+(scow %p 0xaa59.6951) => "~pocbex-minsut"
343+(scow %p 0x4f1.d0f2) => "~ligmer-mapdyr"
344+(scow %p 0xf105.8aa6) => "~pictep-nisdul"
345+(scow %p 0x8213.f7c6) => "~tarnub-wolfus"
346+(scow %p 0x1213.13ca) => "~hidped-rinmul"
347+(scow %p 0x17b.d051) => "~siclut-laspem"
348+(scow %p 0x5561.eaba) => "~davpun-patdyt"
349+(scow %p 0x24cf.01e1) => "~figsyr-torteb"
350+(scow %p 0xb5c4.6ec1) => "~tagzod-nibsyn"
351+(scow %p 0x790c.2f7c) => "~tipmet-mitpun"
352+(scow %p 0xa849.6261) => "~ripper-lagwyn"
353+(scow %p 0x609a.d795) => "~monwyl-malseg"
354+(scow %p 0x2ac8.e1da) => "~tindem-hilsul"
355+(scow %p 0xddf.6d08) => "~novsub-bannum"
356+(scow %p 0x1385.11eb) => "~hadpur-silrut"
357+(scow %p 0x9669.44aa) => "~lasnyd-padlet"
358+(scow %p 0x7f20.f9fe) => "~ritdus-boltep"
359+(scow %p 0x29fd.1d9d) => "~firrep-dovlev"
360+(scow %p 0x1349.4861) => "~doppel-pocmut"
361+(scow %p 0x8eae.31c8) => "~sallyr-rovped"
362+(scow %p 0x3711.6d20) => "~rapder-rocreg"
363+(scow %p 0x24ee.49b1) => "~socdus-molrex"
364+(scow %p 0x6f2.e771) => "~formep-hoptep"
365+(scow %p 0xeff4.5b61) => "~taldyr-montev"
366+(scow %p 0x58a3.95d4) => "~samren-tirpes"
367+(scow %p 0xc55f.f186) => "~mocteg-migrun"
368+(scow %p 0x1c64.373a) => "~mogfel-witfex"
369+(scow %p 0xf4fa.91a8) => "~havsub-hasheb"
370+(scow %p 0x879a.51ff) => "~modmel-mocbyr"
371+(scow %p 0x2428.80aa) => "~mirlug-botbyt"
372+(scow %p 0xaba7.01ca) => "~nibhet-sorfyn"
373+(scow %p 0x4e2f.3761) => "~fabret-mitsud"
374+(scow %p 0x7443.a124) => "~bitlun-wintug"
375+(scow %p 0xa23.8ba8) => "~dovtug-tocnec"
376+(scow %p 0x6a90.6926) => "~patpun-tocsyx"
377+(scow %p 0x46dc.4bc4) => "~tarnum-fitref"
378+(scow %p 0xa993.26f4) => "~sivsen-togtug"
379+(scow %p 0xce78.9a93) => "~picmep-torsug"
380+(scow %p 0xa4b.db41) => "~lonnet-nimnux"
381+(scow %p 0x2d67.5a7b) => "~ronhus-rilpun"
382+(scow %p 0x458d.3cdb) => "~dovsyt-tambex"
383+(scow %p 0x2792.eef8) => "~norbel-tampyx"
384+(scow %p 0x4cdf.ad2e) => "~tinlup-dibdyn"
385+(scow %p 0xf469.7101) => "~rintyv-sanben"
386+(scow %p 0xd8fc.a355) => "~sonwer-pidsyr"
387+(scow %p 0x2791.ae2c) => "~sonwes-dacdul"
388+(scow %p 0xc778.b54b) => "~rosrep-forten"
389+(scow %p 0xd57a.c4c9) => "~namlet-tamnyl"
390+(scow %p 0x76ea.9afd) => "~boltyv-hocryt"
391+(scow %p 0xa84.1788) => "~bidden-dacwyx"
392+(scow %p 0x9028.b9ad) => "~difnub-ritdyt"
393+(scow %p 0x62dd.ec66) => "~torfun-poldef"
394+(scow %p 0xc3a7.71c4) => "~nosdun-labmev"
395+(scow %p 0x597f.e18b) => "~magseg-hastux"
396+(scow %p 0x7ef1.77e0) => "~sipmyn-savmut"
397+(scow %p 0x577b.296e) => "~lanbus-widsul"
398+(scow %p 0x9537.5d68) => "~tichex-lopneb"
399+(scow %p 0xd104.b91f) => "~rovnub-radmed"
400+(scow %p 0x77d7.a830) => "~tadmun-nimmul"
401+(scow %p 0x79d7.dedb) => "~divtex-ronzod"
402+(scow %p 0xb9b.b366) => "~ritren-dirpec"
403+(scow %p 0xc86c.7f7f) => "~dozput-tamteb"
404+(scow %p 0xeed7.6a5a) => "~pidtyd-socsul"
405+(scow %p 0x7889.fa7a) => "~sonben-fasnyr"
406+(scow %p 0xdec5.ac46) => "~molsug-ladryp"
407+(scow %p 0xd13e.74f3) => "~ripreg-sidryx"
408+(scow %p 0x984d.6df8) => "~dildyn-tiphes"
409+(scow %p 0xe1af.6a00) => "~nibpyx-tarben"
410+(scow %p 0x2b84.3e28) => "~hosdeb-witder"
411+(scow %p 0x708a.63bb) => "~tinhec-findus"
412+(scow %p 0xbbca.a6e0) => "~modsyn-lossud"
413+(scow %p 0xc7f4.c8a0) => "~wanted-pidpen"
414+(scow %p 0x84ec.1e73) => "~dispyl-namlun"
415+(scow %p 0xd093.e07f) => "~dilrys-mosrum"
416+(scow %p 0x7019.61c0) => "~fanpun-rildem"
417+(scow %p 0x4bb2.9d02) => "~motmer-migdex"
418+(scow %p 0x466a.165f) => "~micsec-matmun"
419+(scow %p 0x1fb7.61b4) => "~tolsun-sovhut"
420+(scow %p 0xc716.4f27) => "~rinmed-dotrus"
421+(scow %p 0x2f85.ea0f) => "~marfex-rinsum"
422+(scow %p 0x6a95.427d) => "~wismug-lislen"
423+(scow %p 0x6fa2.9d46) => "~wattel-bicryt"
424+(scow %p 0xc4e2.4589) => "~sopsur-sibrut"
425+(scow %p 0x1e96.6a7d) => "~hantem-tacped"
426+(scow %p 0xf069.9ae1) => "~holluc-nolryg"
427+(scow %p 0x87a1.eabf) => "~ladteb-mictyc"
428+(scow %p 0x1a65.4d92) => "~samsym-ramrex"
429+(scow %p 0xd455.f1e8) => "~filmed-walnyd"
430+(scow %p 0xd730.9ecd) => "~tobrus-tindec"
431+(scow %p 0x7c37.2491) => "~mardet-widnyx"
432+(scow %p 0x6078.4186) => "~labpem-noctun"
433+(scow %p 0xd158.03e0) => "~fotreb-lisrep"
434+(scow %p 0xd118.76a4) => "~filryd-nimtyn"
435+(scow %p 0xa80a.4805) => "~nosder-waltuc"
436+(scow %p 0x4167.c3fb) => "~todsut-ravrev"
437+(scow %p 0xa41c.657a) => "~libmug-toldec"
438+(scow %p 0x3101.7342) => "~holsec-wiswel"
439+(scow %p 0x5869.c99b) => "~dorbyl-tobnev"
440+(scow %p 0x1bb1.39fa) => "~monnus-pilfep"
441+(scow %p 0xb3e2.74eb) => "~hanpur-dabmet"
442+(scow %p 0xb33c.fa31) => "~hobheb-hadtex"
443+(scow %p 0xcf59.440c) => "~barsym-bantex"
444+(scow %p 0xf6a2.1b92) => "~lonsyr-hannep"
445+(scow %p 0x283d.4412) => "~firmex-dirsyt"
446+(scow %p 0x9497.06b3) => "~pacden-tamnym"
447+(scow %p 0xe873.12b0) => "~norneb-tilhus"
448+(scow %p 0xf169.9e5f) => "~sipwyd-lignev"
449+(scow %p 0x44f3.d5a4) => "~tidbur-nibrum"
450+(scow %p 0xf8d1.7ea0) => "~mogtem-datheb"
451+(scow %p 0x27a5.0502) => "~rostyn-nilmus"
452+(scow %p 0xd912.b0dc) => "~banmeb-bolwer"
453+(scow %p 0xd72a.a460) => "~borsem-tonnym"
454+(scow %p 0xfbd7.7b31) => "~havdes-navwyc"
455+(scow %p 0xb85b.4eab) => "~togtyd-figlyt"
456+(scow %p 0x52d1.37e4) => "~lacmut-borwed"
457+(scow %p 0xcb27.e7a5) => "~divpem-todled"
458+(scow %p 0xc979.2a58) => "~fonter-sornyd"
459+(scow %p 0x725a.023b) => "~tarder-davnec"
460+(scow %p 0xb87f.c0d1) => "~famdus-ralbyn"
461+(scow %p 0x121f.3cc4) => "~lanwer-liswes"
462+(scow %p 0x1419.0ee8) => "~tablec-dirful"
463+(scow %p 0x652f.a613) => "~novnes-nortev"
464+(scow %p 0x36d3.88d4) => "~lasreg-noshep"
465+(scow %p 0xc39.6144) => "~ragnum-bidsur"
466+(scow %p 0xe501.08d8) => "~podfes-nomsen"
467+(scow %p 0x4941.0b81) => "~mitdev-sorser"
468+(scow %p 0xcdac.c3ea) => "~hindes-firnem"
469+(scow %p 0x9bb.039a) => "~halhep-lacleg"
470+(scow %p 0x382b.0c2d) => "~maller-hoswyc"
471+(scow %p 0x81b9.1040) => "~pidweb-famtun"
472+(scow %p 0xd463.1dc4) => "~lasryn-navsyn"
473+(scow %p 0x1672.6583) => "~lomweb-harmer"
474+(scow %p 0xcaf5.a2a1) => "~fotlun-fipdet"
475+(scow %p 0xeece.a947) => "~midtes-fanwyd"
476+(scow %p 0xf061.4517) => "~labmeb-fasler"
477+(scow %p 0xb30c.0b98) => "~fipsyt-tinsyd"
478+(scow %p 0xff98.7125) => "~linfen-rildun"
479+(scow %p 0x5126.ff0f) => "~fosdet-sithul"
480+(scow %p 0x4e3e.6967) => "~fadtes-fopped"
481+(scow %p 0xf1ef.ddee) => "~toclec-wordex"
482+(scow %p 0x75fa.3a5f) => "~samden-walmer"
483+(scow %p 0x10f5.4ac0) => "~halpex-pitmex"
484+(scow %p 0x95c2.3a79) => "~tocreb-ribrum"
485+(scow %p 0x41e7.dc09) => "~larped-hidwyt"
486+(scow %p 0xdb45.062d) => "~nocbyt-raltyv"
487+(scow %p 0x12b1.1ad0) => "~monbud-habtud"
488+(scow %p 0xb058.5eb7) => "~mortec-pagsen"
489+(scow %p 0x6d23.469d) => "~roslut-samned"
490+(scow %p 0xf313.c934) => "~baldul-socfyr"
491+(scow %p 0x33b0.81d9) => "~labbyl-hasfun"
492+(scow %p 0x19ef.50c4) => "~silmyn-borpem"
493+(scow %p 0x658b.a25d) => "~hatdev-hosren"
494+(scow %p 0x31d2.7176) => "~figrym-ramtyc"
495+(scow %p 0xb472.b2e6) => "~pacmeb-palbes"
496+(scow %p 0xe2ef.229a) => "~fopnep-rabnel"
497+(scow %p 0xe32a.96b4) => "~lonhes-tomdun"
498+(scow %p 0x75a7.6c1a) => "~dalfeb-hacnes"
499+(scow %p 0x8022.ef32) => "~rilbes-firlyt"
500+(scow %p 0x3c80.f71f) => "~bilfus-nildev"
501+(scow %p 0xd18c.4894) => "~bildyn-ligreb"
502+(scow %p 0xcc2d.6076) => "~ridmel-missyd"
503+(scow %p 0x5c7c.2cec) => "~dalhes-dovwet"
504+(scow %p 0xee58.a986) => "~parnec-sibseg"
505+(scow %p 0xdbd3.db62) => "~sovwyt-fanhec"
506+(scow %p 0x3481.251a) => "~botfed-radled"
507+(scow %p 0x83da.479c) => "~ragryn-dovrys"
508+(scow %p 0xe3c2.bbce) => "~marpeg-dozned"
509+(scow %p 0xb3d2.8022) => "~micbel-laptyd"
510+(scow %p 0x6bd3.0911) => "~balpun-binnep"
511+(scow %p 0x2cb9.7a7a) => "~todryx-lopteg"
512+(scow %p 0xf3da.ff9f) => "~timtuc-bicreg"
513+(scow %p 0x95a3.90e3) => "~fitnes-nibrus"
514+(scow %p 0x3409.d67f) => "~rapsyn-danlux"
515+(scow %p 0x4d42.42f8) => "~tilwel-worsun"
516+(scow %p 0xf71.8757) => "~ticbes-toltex"
517+(scow %p 0xd018.0778) => "~pidbyr-rocleg"
518+(scow %p 0x5a3d.8204) => "~hacmul-fadtyc"
519+(scow %p 0x88cb.8e3c) => "~wanwyc-fasdev"
520+(scow %p 0x272f.f8a5) => "~taslet-tortes"
521+(scow %p 0x7fe5.f416) => "~sitnyl-masmes"
522+(scow %p 0xe80e.cc19) => "~mottyv-poclev"
523+(scow %p 0xc5f9.a3f2) => "~dolset-pondem"
524+(scow %p 0xf36e.31e8) => "~polten-ticluc"
525+(scow %p 0x8c8e.4630) => "~wannyx-motned"
526+(scow %p 0x78f2.90db) => "~bonsut-hatfyl"
527+(scow %p 0x5572.2a55) => "~hobfed-righec"
528+(scow %p 0x2384.bc66) => "~lavsed-ticler"
529+(scow %p 0x2185.d0ea) => "~midryg-borlyr"
530+(scow %p 0xc4f.7af8) => "~salled-norbes"
531+(scow %p 0x9fe0.9fe2) => "~datdeb-palled"
532+(scow %p 0x2a43.ab56) => "~dotryp-dopryd"
533+(scow %p 0xece4.7d77) => "~picdyl-simlus"
534+(scow %p 0x251b.0926) => "~simtyc-togtud"
535+(scow %p 0xd7a4.84ea) => "~ponrep-fadfel"
536+(scow %p 0x8bcb.e492) => "~lavsul-mislyd"
537+(scow %p 0x11dc.1f30) => "~tonleg-signex"
538+(scow %p 0xb6bc.83ff) => "~dotlut-paslur"
539+(scow %p 0x5346.e427) => "~socbyn-fopten"
540+(scow %p 0x2bc9.35ab) => "~lonbyt-racnym"
541+(scow %p 0x70.d424) => "~pidrex-rachep"
542+(scow %p 0xab12.c60e) => "~ridlex-moddes"
543+(scow %p 0xacb9.f4af) => "~ripmul-nalfel"
544+(scow %p 0xf93.3425) => "~nomrup-ravler"
545+(scow %p 0xa367.5363) => "~tamret-lomhex"
546+(scow %p 0x166f.4be7) => "~lopseg-hatwel"
547+(scow %p 0xc207.4f3e) => "~navnel-tirnep"
548+(scow %p 0xcac8.4fe0) => "~lisnyl-socnex"
549+(scow %p 0x25f6.47a0) => "~lonsyt-walsec"
550+(scow %p 0xea61.312b) => "~larsev-halled"
551+(scow %p 0x3790.8529) => "~bolhes-magsyd"
552+(scow %p 0xa38.a0db) => "~minryl-rivryc"
553+(scow %p 0x6ecb.2ba0) => "~dozder-hodnum"
554+(scow %p 0x6436.7c97) => "~siphex-datped"
555+(scow %p 0xbc9b.0005) => "~rippyx-lacdus"
556+(scow %p 0x530d.999e) => "~wacryt-lavmet"
557+(scow %p 0x3506.c031) => "~hidsut-figrel"
558+(scow %p 0xf8c2.c3a5) => "~tagpyl-lisrym"
559+(scow %p 0xd2a6.70de) => "~sigmet-sibmun"
560+(scow %p 0xacba.cf9f) => "~locwyx-latryd"
561+(scow %p 0x6197.64f3) => "~ridlyr-rabrem"
562+(scow %p 0xefb8.25cd) => "~sonpub-bidheb"
563+(scow %p 0xdfd2.3150) => "~litdys-modnub"
564+(scow %p 0xea4c.f216) => "~nalsel-pilwed"
565+(scow %p 0xf5c5.2d3e) => "~pilrun-dabmyl"
566+(scow %p 0x47b2.33ee) => "~nolmel-rinnep"
567+(scow %p 0x8999.cb80) => "~polbyr-simluc"
568+(scow %p 0xa1c1.9da3) => "~palmun-pittuc"
569+(scow %p 0xebcc.1200) => "~misnul-molwyc"
570+(scow %p 0x4df6.f4a8) => "~fossed-podful"
571+(scow %p 0x6c0b.9bba) => "~norbud-mondyn"
572+(scow %p 0x51a7.efbb) => "~hidnyx-hadwyt"
573+(scow %p 0xaf6e.dc59) => "~dalben-ritdyt"
574+(scow %p 0x93e8.605d) => "~bondux-witdyl"
575+(scow %p 0xd8d0.04ae) => "~sabpel-racrud"
576+(scow %p 0x26c4.40cd) => "~habbyr-tadwes"
577+(scow %p 0xd6ba.7c44) => "~falsem-solsup"
578+(scow %p 0x885c.13d2) => "~nimwyt-fopnex"
579+(scow %p 0x17c7.405e) => "~binmet-sivsym"
580+(scow %p 0xc3ad.088d) => "~mindes-finwyt"
581+(scow %p 0x42a5.0dc8) => "~solmer-rigweb"
582+(scow %p 0x472d.5770) => "~bidlux-migryt"
583+(scow %p 0x6907.ab68) => "~latnym-waclyd"
584+(scow %p 0x666b.c88a) => "~wisdun-davtyp"
585+(scow %p 0xdb9b.6092) => "~micdel-tirler"
586+(scow %p 0x90a1.1965) => "~dosryd-novwyt"
587+(scow %p 0x46d9.992e) => "~lonsud-batbel"
588+(scow %p 0x869b.dcef) => "~digwyn-firteb"
589+(scow %p 0x1d1e.fe49) => "~tacdun-ribdyn"
590+(scow %p 0xb3a0.e58d) => "~mipbex-dozwyx"
591+(scow %p 0x6017.d60e) => "~tonrud-dondyt"
592+(scow %p 0x4634.acc9) => "~modlus-matsyx"
593+(scow %p 0x36c2.38a4) => "~bonmyn-polred"
594+(scow %p 0x4c26.6abe) => "~dildex-napdux"
595+(scow %p 0xb75c.48a1) => "~pilfyl-livnyx"
596+(scow %p 0xd64f.5604) => "~mossyr-torned"
597+(scow %p 0x9668.060c) => "~todlun-rindyr"
598+(scow %p 0x65fa.8235) => "~mactux-folned"
599+(scow %p 0xd71.9248) => "~ligpes-salwyt"
600+(scow %p 0x597b.8664) => "~fiprul-dibryt"
601+(scow %p 0xe0a0.46eb) => "~sopber-namsup"
602+(scow %p 0xd5a3.72e5) => "~napseg-lisduc"
603+(scow %p 0x70c9.37ea) => "~fipsun-socrus"
604+(scow %p 0x3b85.edf7) => "~nattun-modnex"
605+(scow %p 0xc6e3.11a0) => "~dalmes-ridsyt"
606+(scow %p 0x7d92.7f7b) => "~tomfur-tollux"
607+(scow %p 0x2a53.7521) => "~bacdef-lostux"
608+(scow %p 0x616c.2db6) => "~hactyn-walfel"
609+(scow %p 0x4ed4.a8d1) => "~locnev-witpes"
610+(scow %p 0xa1.74ef) => "~racreb-dartun"
611+(scow %p 0x12c6.8309) => "~ligmet-padwyd"
612+(scow %p 0x7aef.d6af) => "~losnyl-daplus"
613+(scow %p 0x651f.1ca5) => "~fanlyt-parlus"
614+(scow %p 0x4c66.52be) => "~simfel-misdys"
615+(scow %p 0x6dce.11cc) => "~ponner-bottun"
616+(scow %p 0xd785.6e53) => "~hopmyr-ranten"
617+(scow %p 0xaecc.bccf) => "~folnys-ticfeb"
618+(scow %p 0xd3e1.ed84) => "~bisbep-barten"
619+(scow %p 0x7ac7.450d) => "~nibbec-tognyr"
620+(scow %p 0x2f83.1d3c) => "~piclup-fadden"
621+(scow %p 0x6688.f211) => "~torruc-solsum"
622+(scow %p 0xa0e0.733e) => "~hadmud-ricsub"
623+(scow %p 0x7a5c.b776) => "~ligtyl-tanbes"
624+(scow %p 0x283e.a2c3) => "~dozbel-matrud"
625+(scow %p 0x4801.ae9e) => "~bonseg-tacdex"
626+(scow %p 0x976d.b84e) => "~famlet-padmev"
627+(scow %p 0x5e9a.3811) => "~bacnyr-hanrut"
628+(scow %p 0xa7bd.ee9f) => "~dolsud-mispet"
629+(scow %p 0x99ba.f557) => "~panhus-divsyx"
630+(scow %p 0x58b4.098c) => "~dotmer-widfur"
631+(scow %p 0x34c1.df18) => "~sarrum-dannyt"
632+(scow %p 0x2b43.611a) => "~raghes-rittes"
633+(scow %p 0x84d5.4ac4) => "~molbel-rovsef"
634+(scow %p 0xff08.4b25) => "~sopnus-tidrun"
635+(scow %p 0xee00.0776) => "~fipnut-locsem"
636+(scow %p 0x916b.0b9a) => "~topfer-hatnub"
637+(scow %p 0x751a.4f64) => "~noplus-savsur"
638+(scow %p 0xcdc5.a666) => "~bisfyn-banfus"
639+(scow %p 0xab17.65d7) => "~livwyx-hinmyn"
640+(scow %p 0x7975.0d27) => "~todnut-naptul"
641+(scow %p 0xa8ec.7bd6) => "~famren-bandyn"
642+(scow %p 0x73f.4d07) => "~hatzod-rosdef"
643+(scow %p 0xcd25.f4ec) => "~pasryl-ricpyx"
644+(scow %p 0xcbae.a6a0) => "~ronrup-lidfes"
645+(scow %p 0xb9be.ab68) => "~hoszod-sovlyr"
646+(scow %p 0x77c8.c9e9) => "~witpun-dappur"
647+(scow %p 0xa925.1885) => "~havtun-rocput"
648+(scow %p 0xcca3.da50) => "~hadmyr-tonsem"
649+(scow %p 0x1a8d.caf8) => "~lopmex-rabfel"
650+(scow %p 0x6e4.acb3) => "~fallen-laczod"
651+(scow %p 0x5ddc.b589) => "~worpeg-tasryd"
652+(scow %p 0xf812.84f9) => "~hossun-podlyn"
653+(scow %p 0xcb48.8aac) => "~sogdeg-dopnes"
654+(scow %p 0x79d0.0607) => "~batrud-lonrun"
655+(scow %p 0x10b1.7891) => "~sonbud-pidrus"
656+(scow %p 0xea85.727e) => "~fadsun-walryc"
657+(scow %p 0x23d9.3263) => "~fordes-sitnet"
658+(scow %p 0xe364.bad9) => "~simdur-ropdyr"
659+(scow %p 0xe7c6.af18) => "~lignyd-rigdex"
660+(scow %p 0xe536.7be9) => "~falsun-modben"
661+(scow %p 0x9689.5018) => "~toplyx-panmug"
662+(scow %p 0x54f0.7f95) => "~mollyt-racsyr"
663+(scow %p 0xfaed.571d) => "~divbel-livsul"
664+(scow %p 0x5951.a263) => "~natrun-saldux"
665+(scow %p 0xc0eb.a2d1) => "~borlud-wantud"
666+(scow %p 0xc0c2.6b36) => "~milput-tassyd"
667+(scow %p 0xfee5.c01f) => "~hapler-randes"
668+(scow %p 0x4a27.acd1) => "~lommec-sornep"
669+(scow %p 0x4c73.53d9) => "~taprym-rapdur"
670+(scow %p 0xae79.c1bd) => "~sathec-tarryg"
671+(scow %p 0xafa2.6656) => "~hapleg-fogpec"
672+(scow %p 0x9544.f3e3) => "~rilsyt-sabdyr"
673+(scow %p 0x54a2.552c) => "~bilmes-dovhep"
674+(scow %p 0x9018.15a3) => "~mortyc-bitdep"
675+(scow %p 0xf22c.18b0) => "~filsum-mignul"
676+(scow %p 0xf39a.f3dd) => "~silpur-namrel"
677+(scow %p 0x4b36.40c8) => "~dilrex-pacnex"
678+(scow %p 0x9002.f909) => "~wiclyt-tadpex"
679+(scow %p 0x31d0.194d) => "~racdyt-latrud"
680+(scow %p 0x8259.67c7) => "~barrec-ribfur"
681+(scow %p 0xd075.f080) => "~silrud-polmut"
682+(scow %p 0xbfe4.89a3) => "~batfun-fasper"
683+(scow %p 0x476f.d0d1) => "~miglyd-tobpes"
684+(scow %p 0x128f.58f7) => "~somdus-rapnes"
685+(scow %p 0x88b7.6e13) => "~sabfyn-mirwep"
686+(scow %p 0xb18f.d7e4) => "~dalrux-watdeb"
687+(scow %p 0x9a2b.05dc) => "~tomper-bispun"
688+(scow %p 0x343a.6994) => "~norwes-ripsef"
689+(scow %p 0x56f3.4510) => "~parlev-sabwex"
690+(scow %p 0x82a2.282e) => "~labdes-dophul"
691+(scow %p 0x5591.6a7f) => "~sigmut-pilfex"
692+(scow %p 0xe9fa.8fdc) => "~havbus-dolbur"
693+(scow %p 0x5232.10c4) => "~nammes-lomrux"
694+(scow %p 0x7542.7bc5) => "~litnyx-sorrel"
695+(scow %p 0x447c.2100) => "~sivpel-sopdul"
696+(scow %p 0x15c0.b225) => "~figdus-tabdev"
697+(scow %p 0xc27a.e300) => "~binpes-lomsut"
698+(scow %p 0x86fc.3b8d) => "~rabfer-halnut"
699+(scow %p 0x96d8.c29a) => "~ramtyn-nordys"
700+(scow %p 0x9521.e2cb) => "~walrys-dabweb"
701+(scow %p 0x8c5a.c9d7) => "~dozten-wactes"
702+(scow %p 0x1ea0.30c5) => "~bidsub-topdet"
703+(scow %p 0xd2fb.bde3) => "~tactyn-tindyl"
704+(scow %p 0x3ec9.e662) => "~somsyx-rosfed"
705+(scow %p 0x41d5.9f57) => "~ricnem-racluc"
706+(scow %p 0x5f34.1ded) => "~sapsel-tanmut"
707+(scow %p 0x2318.e433) => "~hacryl-ragsyt"
708+(scow %p 0xdede.e4e2) => "~tacsep-fintyl"
709+(scow %p 0x744.d9d6) => "~fogsel-fighep"
710+(scow %p 0x6501.aee5) => "~livfed-simpyl"
711+(scow %p 0x1.0000.0000) => "~doznec-dozzod-dozzod"
712+(scow %p 0x1.0000.0100) => "~doznec-dozzod-marzod"
713+(scow %p 0xffff.ffff.ffff.ffff) => "~fipfes-fipfes-dostec-risfen"
714+(scow %p 0x1.1234.abcd) => "~doznec-picpel-timdel"
715+(scow %p 0xaa25.bd00.0990.0747) => "~hasdel-toczod-bishet-libmus"
716+(scow %p 0xb48.7f7d.fd9c.f50b) => "~dirreg-palwyt-ladnys-rovduc"
717+(scow %p 0x356d.fe5e.f94b.eb7a) => "~tirtug-mipwyx-soppeg-picler"
718+(scow %p 0x27a1.93fd.aaa6.4825) => "~liblus-ridnel-risdur-wicmev"
719+(scow %p 0x5021.5538.fd9b.ebf1) => "~ritmep-savbes-lisfyl-tomhus"
720+(scow %p 0x5afd.5180.8401.cd77) => "~finnel-podlyt-signed-bitrum"
721+(scow %p 0xe8a3.0ecb.6f3f.05be) => "~lansyl-wislec-hadpes-hosmed"
722+(scow %p 0xe854.12d5.83e9.aad5) => "~lanweb-dophes-dolref-nalnut"
723+(scow %p 0xd814.ac10.11a9.c6f8) => "~nolheb-batryp-middet-dirmul"
724+(scow %p 0xf51c.bd6e.c273.e2f2) => "~rosryt-tocmyr-fipsyt-rovneb"
725+(scow %p 0x42ec.bec1.4e1f.10c5) => "~dapfed-hilduc-ribdef-dopmyr"
726+(scow %p 0x7c4e.48f7.b2b5.6670) => "~nismeg-nidfyr-sibnub-pilryn"
727+(scow %p 0x946e.b519.e8d5.2b3f) => "~mocmyr-dabput-nolset-famnel"
728+(scow %p 0x5905.f56b.3837.a566) => "~somper-rosbel-fillep-sornub"
729+(scow %p 0x1537.2197.811e.cdd7) => "~lidtep-pasryx-nisrut-moclep"
730+(scow %p 0xe915.af2a.a734.4d09) => "~donpeg-tidtul-dalpur-ribteg"
731+(scow %p 0x4a5.f6a1.cf9a.f702) => "~sammex-tonlus-litdex-dilfun"
732+(scow %p 0x95c5.cd91.aec8.cc04) => "~pacluc-lagleb-daphut-divdut"
733+(scow %p 0xdcd0.a524.54c2.676f) => "~risnyd-pinpes-sollep-dapdyr"
734+(scow %p 0x3ac7.7738.906e.16f2) => "~rocner-sidbes-motnex-lasseb"
735+(scow %p 0x7a5c.fc73.299b.d8fd) => "~monben-lavset-tanhex-doslun"
736+(scow %p 0xaf58.9886.7fd9.ba92) => "~tidrex-fallyx-fotsyt-lavned"
737+(scow %p 0xacff.289f.f0a6.fd90) => "~batfes-tabmer-tolryp-niblyt"
738+(scow %p 0xfde1.32aa.f33f.2c41) => "~matlys-forlep-mitteb-satsul"
739+(scow %p 0xe0ba.c507.23d3.057c) => "~nimpex-hoblet-rinsev-laprep"
740+(scow %p 0x9f64.bf34.8845.b755) => "~patsyr-macrut-mirdus-ponwel"
741+(scow %p 0x1177.cc2a.9eaf.4389) => "~solsur-ragtul-tasred-balryt"
742+(scow %p 0x8809.9c39.2541.942f) => "~nodpen-hapdex-donsym-tobsec"
743+(scow %p 0xd6dd.280e.f2f1.125a) => "~famhul-tabwyl-morrys-tamnup"
744+(scow %p 0xb04b.f9c5.3e47.f944) => "~havsem-sovluc-losdet-wicfus"
745+(scow %p 0xe9fd.ac73.2a0c.4b7e) => "~donnel-batset-pitreg-molryc"
746+(scow %p 0xc122.da23.81a5.80c8) => "~diglut-hatsep-wantyc-pidsyd"
747+(scow %p 0xb0fc.3278.8147.ea99) => "~havpec-forfel-nomnut-ropfun"
748+(scow %p 0x64a5.bbcb.c600.b67c) => "~socmex-doslec-tonnub-hoplyn"
749+(scow %p 0xd6bd.24cd.da69.d41c) => "~famtyp-livlyd-hadlys-filtus"
750+(scow %p 0x2101.0858.5395.b630) => "~pasnec-fidrex-fidbyt-panlet"
751+(scow %p 0xfff4.781f.b54e.6265) => "~fipbyr-fabnex-borhes-modmun"
752+(scow %p 0x90f0.9199.8fcd.0a49) => "~billyr-hadtyr-lodtul-loslyx"
753+(scow %p 0x9c34.0bb0.88fb.1554) => "~haprut-dirbyl-narlut-pitmus"
754+(scow %p 0xd820.5501.9965.1db1) => "~nollun-savnec-fopnus-ravdys"
755+(scow %p 0xa77.88c3.7243.3100) => "~sogsur-nodfex-lorrun-nibmed"
756+(scow %p 0x1195.83de.5f27.f9cf) => "~solpyx-rabryl-bonnev-mospem"
757+(scow %p 0x5e7d.ad1d.ee02.56b8) => "~worwyt-poctyv-tantyl-soppur"
758+(scow %p 0xd72d.0453.c34d.5a29) => "~datbyn-samtev-racwyc-novseb"
759+(scow %p 0x688c.85ae.ad05.c8eb) => "~picref-rolhut-sabhes-pilrux"
760+(scow %p 0xb6c.a0a8.e2e5.48dc) => "~dirtux-tactuc-tanwet-rinzod"
761+(scow %p 0x9ca9.3544.9aa2.835f) => "~hapfyl-tirsub-wintus-nisred"
762+(scow %p 0x506c.a760.1226.a338) => "~rittux-ricsel-sitpem-rossug"
763+(scow %p 0x9f8d.a461.3a59.2982) => "~patmec-sonruc-balrex-hidbet"
764+(scow %p 0x32bc.9ac5.cd92.bccd) => "~forbyt-tilluc-sivdyl-novzod"
765+(scow %p 0xf095.4d9b.589f.aaa2) => "~lappyx-nattyc-lapten-nodrum"
766+(scow %p 0x8571.4ac3.f08a.ed09) => "~rolter-sicfex-digsub-hiddes"
767+(scow %p 0xc4e4.feca.b065.bb30) => "~mitryc-mipned-lasrep-mogdyn"
768+(scow %p 0x96fc.096c.6139.b443) => "~ravpec-listux-parnes-rovwet"
769+(scow %p 0xfa2a.7556.7e27.37c8) => "~noctul-dacmut-harbud-lasmet"
770+(scow %p 0x7a3.87bf.0d6d.342a) => "~hidsyl-lonmyl-sanpen-wactes"
771+(scow %p 0xce55.4597.72b5.15ca) => "~fadsum-locryx-picmus-ridtyc"
772+(scow %p 0xd40e.8a12.1486.8a28) => "~milwyl-figdyr-ricnes-dibryl"
773+(scow %p 0x6d28.59ff.4b0c.cc9e) => "~tastem-somfes-timlep-toglex"
774+(scow %p 0x571e.381f.ede6.0c68) => "~napsyd-bicnex-nidtes-polped"
775+(scow %p 0x7c74.7613.25dc.6184) => "~nisdut-tannup-ravfus-tarlen"
776+(scow %p 0xc14d.ef11.ad4a.7495) => "~digrec-polsyx-habbex-ralter"
777+(scow %p 0x3df5.9655.a69a.1a3a) => "~dassen-ravsum-tidrys-pardut"
778+(scow %p 0xfbf7.e067.b16c.e2ef) => "~sorfyr-nimmyn-misnus-natler"
779+(scow %p 0x7a1.1011.cb62.6433) => "~hidlus-rigsyx-somnem-folbex"
780+(scow %p 0xbcd5.f9f1.dd3f.e3e7) => "~borhes-sovtes-micfun-hassed"
781+(scow %p 0x9768.3f69.5b4a.b518) => "~ripmes-lopdet-sibsem-dotfex"
782+(scow %p 0x6a3d.81d2.5f14.d3fb) => "~bidder-disrel-ticned-barsyl"
783+(scow %p 0x131d.3432.2c8e.80fd) => "~modtyv-ramhet-parmyn-hoctev"
784+(scow %p 0x48f.1b2d.a3b1.52d3) => "~samrus-folbyn-silnul-hindyn"
785+(scow %p 0xc67c.2e9b.3657.5fb3) => "~harren-fostyc-novdev-ligsyn"
786+(scow %p 0xd5bc.a4c7.324a.83e1) => "~fopbyt-sonner-banler-parlup"
787+(scow %p 0x9aeb.5dd9.fd76.b098) => "~tillux-mordun-nimsup-hidsem"
788+(scow %p 0x8c2e.01c8.9ed3.f472) => "~nibhex-marlex-namtux-palrym"
789+(scow %p 0x2436.6748.bb0b.af4e) => "~livwyd-magreg-signev-sibsyr"
790+(scow %p 0xf867.198e.8079.9b64) => "~ponmyn-lorted-fantyv-dorfun"
791+(scow %p 0xac9c.a068.21eb.8c3d) => "~batleg-tacmes-posnem-panlup"
792+(scow %p 0x4135.e5c1.e901.b1b0) => "~nartyl-sarduc-haldev-bosfyr"
793+(scow %p 0xbe43.bbda.a6b8.1586) => "~hilnut-dosler-fabheb-fidren"
794+(scow %p 0xf040.41b3.0cac.a470) => "~laprys-nardev-bilnem-doclec"
795+(scow %p 0x9991.72a9.e9fb.20f5) => "~todleb-padfyl-mocpes-nomrup"
796+(scow %p 0x5c8e.8dbf.77d1.f9da) => "~banted-pagmyl-dorlyx-tomnux"
797+(scow %p 0xbaa1.7f98.9906.c830) => "~lodlus-palfep-tagfen-samrux"
798+(scow %p 0x49d5.0a0c.5f2d.c7fc) => "~tiphes-sogwep-rivtyl-bondun"
799+(scow %p 0x687f.db25.658b.2183) => "~picmed-nacdel-fogwer-worwer"
800+(scow %p 0x5e2c.b05e.c745.b6e3) => "~worwen-havwyx-ridrut-racfes"
801+(scow %p 0x4907.786e.3bf2.92bf) => "~tiplet-fabmyr-fitbyt-ridber"
802+(scow %p 0xe635.0093.023d.1e89) => "~naltyl-dozryn-nompur-palsut"
803+(scow %p 0x9541.e6b9.5010.579b) => "~pacreb-nalmel-namder-dibweb"
804+(scow %p 0x4418.823f.fc41.60f2) => "~sandys-mappur-ladsyd-tocpec"
805+(scow %p 0x8b52.3b24.1ed7.5a64) => "~nomnym-widpes-mottud-bisbyt"
806+(scow %p 0x910.6414.9993.6988) => "~lisryp-socheb-tarped-hocdet"
807+(scow %p 0x37f3.86ed.dd75.afc5) => "~tadnyt-latsed-hocmyl-nilnex"
808+(scow %p 0x9232.decd.88b3.7b81) => "~dochet-riblyd-fotrex-rondun"
809+(scow %p 0x5e91.6eb8.97f3.e40b) => "~worleb-malrun-filnum-folmur"
810+(scow %p 0x2f14.0291.8985.b34f) => "~dotheb-binleb-macdul-minseb"
811+(scow %p 0x4afd.1878.527e.1ca8) => "~sicnel-dorfel-panpel-hatfed"
812+(scow %p 0xd698.a72d.5ce4.138d) => "~famfep-ricbyn-lapnyr-hinryx"
813+(scow %p 0x45dc.afe7.ed36.1d5f) => "~locseb-tidnyl-wantyp-nopnep"
814+(scow %p 0xb1e3.60a8.b64f.0211) => "~sapwer-rontuc-holrun-hintem"
815+(scow %p 0x2d22.f102.a486.e9b8) => "~bollut-talbud-bolrup-pagdys"
816+(scow %p 0xfde2.5b2c.74f5.4bac) => "~matfyn-fonwen-fabwep-matsul"
817+(scow %p 0x2b50.eea2.e935.785f) => "~pidsec-bacnus-sapfer-bossug"
818+(scow %p 0xd3.add6.e078.53c1) => "~dozrud-pocfet-fodnev-lapsem"
819+(scow %p 0xf8ff.5733.f797.11b4) => "~ponfes-napmev-nidreg-tasben"
820+(scow %p 0xb217.6652.8b26.a204) => "~lindep-dolnym-fitpem-modsed"
821+(scow %p 0x3d30.ee75.689a.a90a) => "~daspyl-bacdeg-migrus-tonryp"
822+(scow %p 0x7198.957b.3114.a8f4) => "~tagfep-pacrux-fonnul-soclud"
823+(scow %p 0xd57e.d9bf.45fd.c1f0) => "~fopnub-dinmyl-dothec-lomten"
824+(scow %p 0xba18.7597.de21.ba89) => "~loddys-dacryx-nocpes-pitbur"
825+(scow %p 0x5a96.bc8b.2c9b.6d3d) => "~finryg-borrev-pilset-panrec"
826+(scow %p 0xbde4.c11a.f22a.63bf) => "~tocryc-diglug-dapret-ragnyr"
827+(scow %p 0xd042.d525.961b.90ab) => "~mopden-fopdel-ponter-nimpec"
828+(scow %p 0x68cf.33af.d8d8.d5b4) => "~picwel-piltun-matwep-nilfyn"
829+(scow %p 0x7c75.0a73.87b6.0130) => "~nisdeg-sogset-hasmus-dolrev"
830+(scow %p 0x9948.e493.c138.035f) => "~todreg-rapryn-tonlys-togpec"
831+(scow %p 0x7b29.ce4f.9653.38fb) => "~ranled-fadnet-maslet-figmyn"
832+(scow %p 0x7aee.13bc.d3fc.a1ca) => "~monbec-modbyt-lodden-lisnet"
833+(scow %p 0x71fd.b271.83ab.6920) => "~tagnel-linter-malnel-wachet"
834+(scow %p 0xbafc.1f6c.b965.b054) => "~lodpec-mirtux-nidweg-tadtud"
835+(scow %p 0xbc6c.1a40.cdb2.9178) => "~bortux-hodrys-wichet-sicdel"
836+(scow %p 0x9e44.0669.5f7e.dd34) => "~fansub-sigdet-fopbes-signet"
837+(scow %p 0xb6a9.0d24.1a42.eee1) => "~bitfyl-sabpes-tipryp-hassyr"
838+(scow %p 0x8771.825e.accc.f66e) => "~lonter-mapwyx-hocfyn-botwer"
839+(scow %p 0x66ca.f129.d9ab.e680) => "~dolned-talled-lomnus-doplut"
840+(scow %p 0x947f.1c87.d584.8381) => "~mocmed-rinpun-sicbel-palnev"
841+(scow %p 0x1b29.0714.2085.dc12) => "~folled-hidheb-hoplen-lomlun"
842+(scow %p 0x7827.eaff.c8cf.7472) => "~fabped-danfes-sictyn-talbec"
843+(scow %p 0x18c5.59d5.7cf4.7955) => "~dorluc-somhes-witfyn-mogmud"
844+(scow %p 0x8ec6.ae93.8bdd.edbc) => "~soplen-hacryn-ticfed-nilrux"
845+(scow %p 0xcec6.31a8.21c0.2dc1) => "~fadlen-diltuc-lomsec-nilrud"
846+(scow %p 0x4182.b721.c507.e171) => "~narneb-barmep-lagsyd-bosmel"
847+(scow %p 0xfdeb.73c4.3d2d.50ec) => "~matlux-salnul-pactyc-sovmus"
848+(scow %p 0x1224.09a5.b2d1.0d0e) => "~doppes-lismex-saplug-satryp"
849+(scow %p 0x6466.15ad.55d6.bd95) => "~socdyl-lidmug-sipsun-pagwex"
850+(scow %p 0x4b28.a003.f4e4.847f) => "~roptem-tacwes-tanreg-sabrym"
851+(scow %p 0x85ae.555e.b625.04b1) => "~rolhut-savwyx-tirsub-widtev"
852+(scow %p 0x67cd.94bf.5adf.c75d) => "~maglyd-mocmyl-rosfex-hobrup"
853+(scow %p 0xda96.c3a7.8c4c.e954) => "~hatryg-fasrym-dozbud-tanneb"
854+(scow %p 0x9b03.e384.3037.7d50) => "~tinwes-waltyn-timpeg-tocber"
855+(scow %p 0xacfd.90c3.7911.93e8) => "~batnel-bilfex-barref-nosrun"
856+(scow %p 0xacbf.5c92.ee5e.cca8) => "~batmyl-bandux-nisrys-sidtyp"
857+(scow %p 0x7737.14a5.edd4.5b9e) => "~sidtep-fogmex-ribwer-hapheb"
858+(scow %p 0xf220.a4ad.51cd.92bf) => "~pitlun-sonmug-banpub-bostel"
859+(scow %p 0xeb16.2bde.710b.72cd) => "~ladlup-pidryl-lonpur-tornyl"
860+(scow %p 0x1b9f.5929.865d.e9b5) => "~folmer-somled-havnub-lopsyn"
861+(scow %p 0xcffc.015c.8a07.04e0) => "~toppec-marben-sidtyr-fabtex"
862+(scow %p 0x7694.49ae.fd55.e76a) => "~tannum-tiphut-wolfex-nopnyr"
863+(scow %p 0x1378.7190.15fb.ba18) => "~modfel-tagbex-pitmut-wisbec"
864+(scow %p 0x2d01.b2dc.328c.bec1) => "~bolnec-linseb-bidfer-fanber"
865+(scow %p 0x1.0000.0000.0000.0000) => "~doznec--dozzod-dozzod-dozzod-dozzod"
866+(scow %p 0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff) => "~fipfes-fipfes-fipfes-fipfes--fipfes-fipfes-fipfes-fipfes"
867+(scow %p 0x8000.0000.0000.0000.0000.0000.0000.0000) => "~laszod-dozzod-dozzod-dozzod--dozzod-dozzod-dozzod-dozzod"
868+(scow %p 0x2c32.fef2.55fd.12b9.e5b3.7894.9b92.86cc) => "~torhet-mipmud-savnel-dopmel--sardev-fabnum-tindux-latryd"
869+(scow %p 0xed71.1826.b1e5.d3ef.4580.91e9.60b4.935d) => "~rivter-dorsul-sapsug-nosmun--loclyt-haddyn-ronlur-ridmus"
870+(scow %p 0x287c.d448.0785.930c.43bb.19f9.3318.a7b7) => "~tabren-milreg-hidseg-ridwep--moldyt-lortel-pildys-ricbep"
871+(scow %p 0x9a24.cf98.50e7.c82a.a967.0974.f888.5a4a) => "~tilpes-topfep-ritnyl-hintul--firmyn-lisdut-ponres-finsup"
872+(scow %p 0xce86.ce41.0e41.b1d8.0b95.14ac.1989.3d92) => "~fadlyx-fadreb-wisreb-sapret--dirpyx-fogber-lorred-dasdux"
873+(scow %p 0x52bc.1d7e.6c8a.d74c.42a6.73a4.adfd.4423) => "~motbyt-tognub-timfun-datwyn--dappub-saltec-pocnel-sansep"
874+(scow %p 0x97a5.12c8.facc.9276.4ed4.35b2.5707.56cb) => "~ripmex-doplex-nocryd-doctex--pannes-tirpem-naplet-poslec"
875+(scow %p 0x93a5.61e5.b6fb.41cc.6f09.749e.edfa.05a2) => "~ridmex-norsug-bitteg-narryd--ligpen-divfer-rivrep-litnus"
876+(scow %p 0x6031.0fd3.2a90.3e1d.ae25.5bba.e70c.71c2) => "~rondul-sibrud-ticbex-midtyv--hacdel-fonpex-moswep-tagfur"
877+(scow %p 0x4a02.0b8c.0244.1c34.6501.ecdd.f8f1.16b2) => "~sicbud-dirref-binsub-rinrut--watnec-dovhul-pontes-hoppem"
878+(scow %p 0x48a0.b4bb.ebe5.c4a2.9826.0377.98ba.c021) => "~nidten-hosdyt-ladsug-mitnus--falsul-wansur-falpex-tommep"
879+(scow %p 0x510e.393a.b42d.b9e3.d477.01df.e8ed.9082) => "~podwyl-difsef-hosbyn-parwer--milsur-marlud-lansed-bilneb"
880+(scow %p 0x3fb4.13f1.9c00.520c.db4f.0763.ed0f.531d) => "~loplur-modtes-hapzod-motwep--nacnet-hidwex-rivsun-tamtyv"
881+(scow %p 0xdbaa.2f6c.ebb7.12ca.d74d.a6e7.27f5.1f96) => "~naclep-dottux-ladbep-dopned--datrec-lomnyl-libsen-mirryg"
882+(scow %p 0x9b57.6d00.73f4.6b3b.9c2e.f1ed.31bd.820a) => "~tinnyx-taszod-salbyr-balwyc--haphex-talsed-diltyp-mapsyt"
883+(scow %p 0xdaa1.768a.8078.be0b.6ae1.98ca.71ed.768b) => "~hatlus-tanfun-lasfel-hildur--bidlys-falned-tagsed-tanrev"
884+(scow %p 0xcf58.89a7.44cd.58df.85ea.2e59.561e.1774) => "~toprex-navrym-sanlyd-noplud--roldem-fosteb-possyd-dardut"
885+(scow %p 0x4279.194d.644b.8539.6f60.a71d.e38f.b707) => "~daptud-lorrec-socsem-roldex--ligsel-rictyv-walrus-barlet"
886+(scow %p 0x50ad.4b42.7e59.412e.930e.dbe4.af80.c32c) => "~ritmug-ropden-misteb-narhex--ridwyl-nacryc-tidlyt-faswen"
887+(scow %p 0x2865.d6f6.4944.9c41.d53d.af90.3f68.ae69) => "~tabwet-famweg-tipsub-hapreb--fopder-tidbex-lopmes-hacdet"
888+(scow %p 0x692d.37f4.e766.31cf.29f6.293e.f85e.b85e) => "~davbyn-tadbyr-mosdyl-dilwel--hanweg-hannep-ponwyx-racwyx"
889+(scow %p 0x9388.ca13.3b88.eb08.08d1.30a0.285a.6a78) => "~ridres-masnup-widres-ladful--fidhus-losten-tabfus-bidfel"
890+(scow %p 0xfb70.a1c7.ebe7.3705.8b90.01e7.f57d.e19d) => "~sorsyp-labner-ladnyl-tadper--nombex-marnyl-roswyt-larnem"
891+(scow %p 0x34ce.0fec.8b39.c161.a0d0.18c0.5a95.c2ee) => "~ramfen-sibfed-nomdex-digruc--tacnyd-dorwed-finpyx-filbec"
892+(scow %p 0x632.5ab8.71bb.9ec7.eb00.c7ca.6e1a.e030) => "~sighet-finrun-tagdyt-fanner--ladzod-migned-mallug-nimpyl"
893+(scow %p 0x11b6.18a2.c7a7.db48.6784.a904.8cc4.d8cc) => "~solbus-dornus-migrym-nacreg--magtyn-firsev-nibnul-nolryd"
894+(scow %p 0x1026.3fec.b7aa.40d4.e27b.5723.b7dc.1dc9) => "~rigsul-lopfed-barlep-rilnes--fitrux-napsep-barseb-togrup"
895+(scow %p 0xd38c.d22f.3e90.f8c4.d0ff.6425.1220.ed66) => "~nosref-nilfeb-midbex-ponnul--mopfes-socdel-doplun-rivdyl"
896+(scow %p 0xe2a7.cd6e.b6b6.565b.6191.c232.a57e.5e4f) => "~fitrym-lagmyr-bitbus-poshep--norleb-filhet-pinnub-wornet"
897+(scow %p 0xc85d.045c.c78d.f563.6134.4f61.7c46.8bcb) => "~hinmus-samben-migmec-roswex--norrut-minruc-nisrul-nomlec"
898+(scow %p 0x4d7f.58e7.b147.3b21.3ccc.3d25.f4e7.6ea7) => "~natmed-nopnyl-sapsyn-widmep--bisryd-dasdel-bonnyl-malrym"
899+(scow %p 0xf706.3d8c.53c8.48d4.835e.1550.d518.1223) => "~fodsut-dasref-tamlex-nidnes--rabwyx-lidsec-fopdys-dopsep"
900+(scow %p 0x73b3.0979.84b4.0ffc.84f4.461b.d145.59d4) => "~saldev-listud-toblur-sibpec--tobbyr-novhec-habpet-somnes"
901+(scow %p 0x1072.400b.aea9.79c7.a8d1.6ddc.ce3e.f6cd) => "~rigmeb-rildur-hacfyl-tarner--taphus-tasseb-fadnep-tonlyd"
902+(scow %p 0x6af.980a.c524.a52f.ceb3.b061.4d80.457d) => "~sigtun-falsyt-hobpes-pinfeb--faddev-havruc-natlyt-locwyt"
903+(scow %p 0x2e3a.f702.8045.cd39.d81b.2b7b.ccb7.fc9f) => "~fossef-fodbud-laspet-lagdex--nolhec-pidrux-ragbep-lavmer"
904+(scow %p 0x6da2.e572.601d.5b28.2599.1680.248e.c4a1) => "~tasnus-sarmeb-rontyv-fontem--daltyr-hoplyt-livted-mitlus"
905+(scow %p 0xecfc.4be9.230f.05cc.870c.a2d5.2d54.0f03) => "~dovpec-ropdyn-rovsun-litryd--lonwep-moghes-bolweb-sibwes"
906+(scow %p 0x8a67.f88d.7d0d.f03c.1802.225d.4d27.4cd8) => "~figmyn-ponmec-wolser-lapbur--dorbud-lacmus-natped-witret"
907+(scow %p 0x2230.b2b5.99e0.de87.55fa.a27b.cd1c.6d88) => "~lacpyl-lindef-todrem-ribpun--savrep-mogrux-lagryt-tasres"
908+(scow %p 0xc7b6.8cc2.eab8.7eb8.de39.1095.445b.de33) => "~migbus-nibfur-danrun-misrun--ribdex-rigpyx-sanhep-ribmev"
909+(scow %p 0xbe25.f2cc.dc1d.05d8.6e53.4c59.f303.5664) => "~hildel-pitryd-ristyv-litret--maltev-witteb-namwes-possyr"
910+(scow %p 0x50c2.b6f3.df33.adc0.475d.b4cd.78d5.18ff) => "~ritfur-bitnyt-hocmev-pocwed--sitmus-hoslyd-fabhes-dorfes"
911+(scow %p 0x7072.66a4.cabf.c378.10e0.c2ae.2ae1.d429) => "~sivmeb-doltec-masmyl-fasfel--rigrem-filhut-ticlys-milled"
912+(scow %p 0xc102.e900.3fef.a7e4.713f.9348.3af3.f62d) => "~digbud-donzod-lopmun-ricryc--tagpur-ridreg-rocnyt-tonbyn"
913+(scow %p 0x27fa.d683.d357.b3c2.4443.2ae1.111a.deff) => "~librep-famrum-nosnyx-dibfur--sannut-ticlys-sollug-ribfes"
914+(scow %p 0xf99d.5291.d49c.3357.a12b.7c40.d0d8.62ba) => "~sovnem-motleb-milleg-pilnyx--labmet-nisrys-mopret-botpex"
915+(scow %p 0xfaa0.df51.2516.47e8.4631.1dc9.8fb7.2d4c) => "~nocten-hocmul-dallup-sitlyn--novdul-togrup-ralbep-bolwyn"
916+(scow %p 0x337a.41a3.37c6.2f42.fb99.69ac.64e1.857b) => "~pilnux-narsyl-tadlen-dotden--sortyr-davber-soclys-rolrux"
917+(scow %p 0xebc8.a6cf.256b.d475.85bc.ae13.8f78.d6aa) => "~ladlex-lomwel-dalbel-mildeg--rolbyt-hacnup-ralfel-famlep"
918+(scow %p 0xada5.a6eb.b96f.ca6b.57b5.ac49.3649.7740) => "~pocmex-lomlux-parpel-masbel--napdef-battyd-wintyd-sidrys"
919+(scow %p 0x79.42c9.adf9.ca70.69c5.61ef.9e3c.7d6e) => "~doztud-daprup-poctel-massyp--davluc-normun-fanbur-wolmyr"
920+(scow %p 0xe1d6.805d.58a6.09bc.f45f.cbeb.84b8.3b91) => "~larfet-lasmus-noppub-lisbyt--bonsym-hallux-tobrun-widleb"
921+(scow %p 0xd7c6.efdb.3940.f4f3.a421.21a2.30f9.9483) => "~datlen-polnyr-difrys-bonnyt--sonmep-pasnus-lostel-mocrum"
922+(scow %p 0xe091.6cdf.0485.1e69.043f.0868.8da9.0679) => "~nimleb-timlud-samseg-sildet--sampur-fidmes-pagfyl-sigtud"
923+(scow %p 0x3c85.1f9b.70b5.6387.d110.9dc0.5cf8.c66c) => "~bisseg-mirtyc-sivdef-wicpun--habryp-micwed-banmur-hartux"
924+(scow %p 0x6a13.3233.02d5.8575.c364.2d83.aeb8.3ff5) => "~bidnup-formev-binhes-roldeg--fassyr-bolrum-hacrun-lopsen"
925+(scow %p 0x54e7.0bde.6bf5.debe.7f2d.aef2.6c1f.1ba6) => "~tolnyl-dirryl-balsen-riblev--palbyn-hacmud-timnex-folpub"
926+(scow %p 0x9538.f786.a603.1278.ea20.5cc7.f16b.57f4) => "~pacbes-fodlyx-lomwes-dopfel--danlun-banner-talbel-napbyr"
927+(scow %p 0x963c.4eb5.c1cd.e5af.3fd2.e6d1.84be.cd5e) => "~ravbur-pandef-diglyd-sartun--loprel-nalhus-toblev-lagwyx"
928+(scow %p 0xb84e.a1be.c7c7.3f31.9380.a865.c58d.2df6) => "~racmeg-lablev-migner-lopdul--ridlyt-tapwet-hobmec-bolweg"
929+(scow %p 0x7807.1a50.4e33.b858.7c1d.c4f8.9049.a277) => "~fablet-hodsec-panmev-racrex--nistyv-mitmur-biltyd-mogsur"
930+(scow %p 0x4ac5.e7fb.052b.27d1.045e.6cef.973e.f46b) => "~sicluc-mosteg-litmet-libhus--samwyx-timmun-ripnep-bonbel"
931+(scow %p 0xec79.2f50.1f98.26a4.9118.5976.b2bd.8adc) => "~dovtud-dotsec-mirfep-sattec--haddys-somtex-lintyp-figseb"
932+(scow %p 0xdb38.83e0.926d.5ad3.73cb.068d.f830.7efc) => "~nacbes-rabrem-doctug-finrud--sallec-sigmec-ponpyl-mispec"
933+(scow %p 0x6b4e.2bab.652d.6d99.3eb0.69f9.c25d.bd51) => "~balmeg-piddeb-watbyn-tastyr--midbyl-davtel-filmus-tocmul"
934+(scow %p 0x6442.1948.6e6a.93ad.0ac7.2a24.3599.ed8c) => "~socden-lorreg-malbet-ridmug--sogner-ticpes-tirtyr-rivref"
935+(scow %p 0x2d90.a7a0.69a1.5dbc.f52e.db19.1c8c.c6bb) => "~bolbex-ricten-davlus-morbyt--roshex-nacput-rinref-hardyt"
936+(scow %p 0x1285.e4e5.9c0d.43e5.2c92.1cd9.db53.5f0d) => "~dopseg-rapsug-hapser-molsug--tordux-rindun-nactev-sipser"
937+(scow %p 0x5e53.c2e5.b88f.de3d.c92a.c3bd.e99a.7c0d) => "~wortev-filsug-racrus-ribder--radtul-fastyp-dontus-nisser"
938+(scow %p 0xc20a.75b8.bf6b.bec5.2ba8.c6a5.c96f.3f0a) => "~filsyt-dacrun-macbel-hilluc--pidtuc-harmex-radpel-lopsyt"
939+(scow %p 0x833c.d4a2.1032.6bc4.cdbd.e5aa.3ac4.ea87) => "~rabbur-milnus-righet-balnul--lagtyp-sarlep-rocnul-danpun"
940+(scow %p 0x44ab.018f.2083.c6c7.0874.c102.6143.117f) => "~sandeb-marrus-holrum-harner--fiddut-digbud-nornut-solmed"
941+(scow %p 0x5857.6cbd.075d.5c4c.8075.dbb7.acc2.73fb) => "~nopnyx-timtyp-hidmus-banwyn--lasdeg-nacbep-batfur-salteg"
942+(scow %p 0x4650.bd73.ccf2.d3a6.0f58.612a.8227.722f) => "~novsec-tocset-ragmud-nospub--sibrex-nortul-mapped-padfeb"
943+(scow %p 0xe9e6.c087.0486.fb8c.8489.7ecf.bae1.921b) => "~donnys-tompun-samlyx-sorref--tobred-miswel-lodlys-dochec"
944+(scow %p 0x7b31.d043.f267.8336.695c.0662.0b91.2a6a) => "~randul-mopnut-pitmyn-rabwyd--davben-sigdec-dirleb-ticbet"
945+(scow %p 0x49d0.e84d.c111.696c.b51f.a91f.07c9.a994) => "~tipnyd-lanrec-digsyx-davtux--dabnex-firnex-hidrup-firnum"
946+(scow %p 0xda20.0eed.3b0f.2304.2ebe.0b6a.e211.2911) => "~hatlun-wissed-widsun-rovsev--foslev-dirbet-fitsyx-hansyx"
947+(scow %p 0xfd1.1c44.937b.1696.7d6f.1db7.9f65.d17b) => "~sibhus-rinsub-ridrux-hopryg--wolpel-togbep-patwet-habrux"
948+(scow %p 0x7215.582b.3e4f.1fdf.4b6b.1cf7.e49b.9538) => "~padpeg-nopmet-midnet-mirlud--ropbel-rinfyr-raptyc-pacbes"
949+(scow %p 0x2946.8671.436a.c9f1.2cd1.20e3.757f.36ee) => "~hanrul-latter-molbet-radtes--torhus-holwer-dacmed-winbec"
950+(scow %p 0x4fcc.cc46.bdd6.2efa.a4ca.0693.6adc.c718) => "~minryd-ragrul-tocfet-fosrep--sonned-sigryn-bidseb-migdys"
951+(scow %p 0xe929.60dc.0270.e89a.e4cd.9ff1.85b1.56c7) => "~donled-ronseb-binsyp-lantus--raplyd-pattes-rolsud-posner"
952+(scow %p 0x5379.734e.a78c.cb10.d580.fb99.4ac9.3510) => "~tamtud-salmeg-ricref-halryp--foplyt-sortyr-sicrup-tirryp"
953+(scow %p 0x3481.48db.9841.9b43.a0e1.7631.f466.7f83) => "~ramdus-nidnyr-falreb-tinnut--taclys-tandul-bondyl-palrum"
954+(scow %p 0x852.4cc6.b354.bd66.2f7f.aa39.e9ab.d3f2) => "~fidnym-witlen-dibweb-tocdyl--dotmed-hasdex-dondeb-nosmud"
955+(scow %p 0xbd19.1aeb.a102.2fe5.4eae.a9af.a70b.7315) => "~tocput-hodlux-labbud-dotsug--panhut-firtun-ricdur-salpeg"
956+(scow %p 0x49e.e299.eba5.a913.7fec.95f1.9b29.95e0) => "~samfer-fittyr-ladmex-firnup--palfed-pactes-tinled-pacrem"
957+(scow %p 0xa592.e0c9.6f4b.e278.2995.3ae8.6cbe.c78d) => "~pindux-nimrup-ligsem-fitfel--hanpyx-roclyn-timlev-migmec"
958+(scow %p 0xde91.8c34.496d.5e47.9ea1.a1b9.56cc.3e90) => "~ribleb-nibrut-tiptug-worsyn--fanlus-labmel-posryd-midbex"
959+(scow %p 0x1478.e630.e37f.8c18.f47e.f783.1813.38b2) => "~fogfel-nalpyl-walmed-nibdys--bonnub-fodrum-dornup-bicpem"
960+(scow %p 0x6b42.e4df.bbec.00e0.93b4.b54a.d553.34f0) => "~balden-raplud-dosfed-dozrem--ridlur-dabsup-foptev-ramlyr"
961+(scow %p 0xfbd3.8033.55dd.4ad2.e581.34ab.91e1.d843) => "~sorrud-lasmev-savhul-sicrel--sardus-ramdeb-hadlys-nolnut"
962+(scow %p 0x9bb.2645.9a01.5083.b547.466f.8708.5bee) => "~lisdyt-satpet-tilnec-ritrum--dabsyn-novpel-lonful-fonbec"
963+(scow %p 0xf227.1ff8.6887.5f5f.cd24.e752.a88d.396a) => "~pitped-mirmur-picpun-sipsym--lagpes-mosnym-tapmec-difbet"
964+(scow %p 0x67e5.d6f1.2ec4.0975.863a.0160.7135.410e) => "~magsug-famtes-fosnul-lisdeg--latsef-marsel-tagtyl-narwyl"
965+(scow %p 0x847e.9b78.e6a4.4d38.8e32.8c22.1b4d.3eec) => "~tobnub-tinfel-naltec-natbes--sophet-niblut-folrec-midfed"
966+(scow %p 0x84a5.4df8.966f.885d.a6da.df4c.54ca.67c7) => "~tobmex-natmur-ravpel-nodmus--lomler-hocwyn-tolned-magner"
967+(scow %p 0xa7c3.e11d.30ab.9d3c.893c.9528.f940.3532) => "~ricfex-lartyv-losdeb-micbur--navbur-pactem-sovrys-tirhet"
968+(scow %p 0x100.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000) => "~marzod--dozzod-dozzod-dozzod-dozzod--dozzod-dozzod-dozzod-dozzod--dozzod-dozzod-dozzod-dozzod"
969+(scow %p 0x201a.7d6a.834b.8307.b218.a754.5164.bef9.16fb.daae.657a.1262.19a8.c70f.8488.d965) => "~hollug-wolbet-rabsem-rablet--lindys-ricweb-podsyr-hiltel--hopteg-hathut-watnux-dopdec--lortuc-migsun-tobres-dinwet"
970+(scow %uv 0) => "0v0"
971+(scow %uv 0x1) => "0v1"
972+(scow %uv 0x9) => "0v9"
973+(scow %uv 0xa) => "0va"
974+(scow %uv 0x1f) => "0vv"
975+(scow %uv 0x20) => "0v10"
976+(scow %uv 0x3e7) => "0vv7"
977+(scow %uv 0x3e8) => "0vv8"
978+(scow %uv 0x3e9) => "0vv9"
979+(scow %uv 0xffff) => "0v1vvv"
980+(scow %uv 0x1.0000) => "0v2000"
981+(scow %uv 0x9c.cfe7) => "0v9pjv7"
982+(scow %uv 0xee.5432) => "0vesl1i"
983+(scow %uv 0x1.0000.0000.0000.0000) => "0vg00.00000.00000"
984+(scow %uv 0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff) => "0v7.vvvvv.vvvvv.vvvvv.vvvvv.vvvvv"
985+(scow %uv 0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff) => "0v1v.vvvvv.vvvvv.vvvvv.vvvvv.vvvvv.vvvvv.vvvvv.vvvvv.vvvvv.vvvvv"
986+(scow %uv 0xce) => "0v6e"
987+(scow %uv 0xc2) => "0v62"
988+(scow %uv 0x66) => "0v36"
989+(scow %uv 0x5b) => "0v2r"
990+(scow %uv 0x75) => "0v3l"
991+(scow %uv 0x7f) => "0v3v"
992+(scow %uv 0x44) => "0v24"
993+(scow %uv 0x2c) => "0v1c"
994+(scow %uv 0x8096) => "0v104m"
995+(scow %uv 0xc421) => "0v1h11"
996+(scow %uv 0x2dff) => "0vbfv"
997+(scow %uv 0xfa82) => "0v1uk2"
998+(scow %uv 0x6620) => "0vph0"
999+(scow %uv 0xd7e1) => "0v1lv1"
1000+(scow %uv 0x6ef2) => "0vrni"
1001+(scow %uv 0xbf7a) => "0v1frq"
1002+(scow %uv 0xc6.b698) => "0vcddko"
1003+(scow %uv 0x6c.d8b5) => "0v6pm5l"
1004+(scow %uv 0x4d.f730) => "0v4rtpg"
1005+(scow %uv 0xec.4789) => "0veohs9"
1006+(scow %uv 0x5b.a603) => "0v5n9g3"
1007+(scow %uv 0xad.7813) => "0vaqu0j"
1008+(scow %uv 0x2a.7a74) => "0v2kujk"
1009+(scow %uv 0x2c.f469) => "0v2pt39"
1010+(scow %uv 0x1e0.4bc5) => "0vu0iu5"
1011+(scow %uv 0x143.f8ca) => "0vk7u6a"
1012+(scow %uv 0x19c.4922) => "0vpoi92"
1013+(scow %uv 0x2b.acd3) => "0v2nb6j"
1014+(scow %uv 0x113.dd63) => "0vh7nb3"
1015+(scow %uv 0x7.cd7b) => "0vfjbr"
1016+(scow %uv 0x1b3.6933) => "0vr6q9j"
1017+(scow %uv 0xd0.a79e) => "0vd19su"
1018+(scow %uv 0x14b0.62ae) => "0va.b0ole"
1019+(scow %uv 0xd23.29d9) => "0v6.i6aep"
1020+(scow %uv 0x210f.4d0c) => "0vg.guj8c"
1021+(scow %uv 0xb713.b4c7) => "0v2r.h7d67"
1022+(scow %uv 0x2d0f.d6d9) => "0vm.gvlmp"
1023+(scow %uv 0x1c1b.5b32) => "0ve.1mmpi"
1024+(scow %uv 0x2bd2.ed1c) => "0vl.t5r8s"
1025+(scow %uv 0x6b45.1936) => "0v1l.ka69m"
1026+(scow %uv 0xd9.efca.6c3a) => "0vr7n.skr1q"
1027+(scow %uv 0x2c.a1cb.b723) => "0v5ig.sndp3"
1028+(scow %uv 0x9e.2495.da17) => "0vjoi.9bmgn"
1029+(scow %uv 0x2.9e0d.5425) => "0vaf.0ql15"
1030+(scow %uv 0xa8.618d.3f43) => "0vl1g.oqfq3"
1031+(scow %uv 0x66.67c3.b9fe) => "0vcpj.s7efu"
1032+(scow %uv 0x1c.f94c.f6b0) => "0v3js.kptlg"
1033+(scow %uv 0x6c.a3f3.760f) => "0vdih.v6tgf"
1034+(scow %uv 0xbf49.e557.c768.beaf) => "0vbui.f5av3.mhflf"
1035+(scow %uv 0xf0f4.0471.9ba8.272c) => "0vf1t.04e6d.qg9pc"
1036+(scow %uv 0x18b3.205e.1fd6.8335) => "0v1hc.p0bof.td0pl"
1037+(scow %uv 0x4bc4.f0a0.24c7.0f48) => "0v4nh.7gk0i.ce3q8"
1038+(scow %uv 0x2d7a.2d12.fdba.24d0) => "0v2qu.hd2bu.rk96g"
1039+(scow %uv 0xec94.09ff.2540.3cfd) => "0vep5.09vsi.k0f7t"
1040+(scow %uv 0x8f9b.7341.e163.e7ce) => "0v8v6.rj87g.m7pue"
1041+(scow %uv 0xd89b.acfa.3be1.4460) => "0vdh6.tcv8t.u2h30"
1042+(scow %uv 0xb.d285.6dcb.db9d.1cca.1e01.b04f) => "0vnkk5d.n5tn7.8sp8f.03c2f"
1043+(scow %uv 0xc.3760.e399.5662.2305.0c34.9a2a) => "0vodr0s.eclco.h30k6.396ha"
1044+(scow %uv 0x5.17b8.d928.88cc.5593.5ef8.52e5) => "0va5tor.4k8hj.2lidf.fgkn5"
1045+(scow %uv 0xb.ec4c.1908.099f.a987.e12b.6479) => "0vnr2c3.440j7.t9gvg.imp3p"
1046+(scow %uv 0xe.4bdf.5899.6230.a1f3.3fed.b013) => "0vsiuvb.2cm4c.51ucv.urc0j"
1047+(scow %uv 0x9.80ce.7b57.11d6.5409.4197.2530) => "0vj06ef.dbh3l.ik150.pe99g"
1048+(scow %uv 0xa.dfe5.5489.af4f.905a.e83e.a120) => "0vlnv5a.i4quj.sgbbk.3t890"
1049+(scow %uv 0x4.8882.afe0.7a23.1ebe.c9c7.bf75) => "0v9242l.vg7k8.ounr4.sffrl"
1050+(scow %uv 0x31c8.e93d.70a7.58d3.b6fe.d184.aab2.348c) => "0v1.hp3kj.qs57b.39rdv.mhgil.b4d4c"
1051+(scow %uv 0x586a.fe5f.3299.a5c0.e331.f1f5.563b.9c31) => "0v2.odbv5.uckpk.n0e6c.fhulb.3n71h"
1052+(scow %uv 0xdc9b.ce16.dc4a.1504.6882.cca9.3b32.5271) => "0v6.sjf71.dn2a2.k26h0.mcl4t.j4kjh"
1053+(scow %uv 0x85a8.5ac8.0054.b3fa.33d6.e711.8820.aa64) => "0v4.5l1dc.g02km.ft37l.n7264.21aj4"
1054+(scow %uv 0xce48.62a9.c8ed.57cc.82d8.7edc.2ad8.1531) => "0v6.e91ha.ji7da.v685m.3urgl.dg59h"
1055+(scow %uv 0x4999.d6f0.c4d9.0124.9b1e.c183.c3d9.c44b) => "0v2.9j7bf.1h6p0.4i9m7.m1gf1.tjh2b"
1056+(scow %uv 0xc1b4.b1b9.31aa.3d01.4b90.6d61.932d.76ed) => "0v6.1mior.icda7.k0kn4.3dc69.iqtnd"
1057+(scow %uv 0x6508.8190.cb2a.a305.dd1c.0115.fde7.f97b) => "0v3.5120p.1ipak.c2tq7.012nu.ufubr"
1058+(scow %uv 0x543d.1120.75f8.7337.43ca.0ebb.2c18.c4bf.ca0c.2e68) => "0vag.uh283.lv1pj.egua1.qtio6.64nv5.0obj8"
1059+(scow %uv 0x3526.0731.400a.fd64.5c1e.6b81.2dc8.11b7.b70d.1bb7) => "0v6k.j0eca.01bum.8n0ud.e0iri.0hmur.gq6tn"
1060+(scow %uv 0xb322.eb50.e3c7.8e5e.d827.0e93.929a.15ea.7691.ca9f) => "0vmc.hemk7.3ou75.tm171.q9p56.glt9r.93ikv"
1061+(scow %uv 0xff86.0945.efdb.d373.e9a5.2ea1.d3a0.57f0.2f04.a4ab) => "0vvu.30ihf.frf9n.7qd55.qgt78.2nu0n.g995b"
1062+(scow %uv 0xea7e.c403.e3a6.f1e5.a037.cd69.e497.05b3.92a2.dbf2) => "0vt9.vc80v.3krou.b81np.lku95.o5me9.a5mvi"
1063+(scow %uv 0x881b.1a78.1322.8339.fafd.d23d.2b63.266d.aa3e.3c3d) => "0vh0.dhku0.j4a1j.juntq.8uimo.p6dml.3sf1t"
1064+(scow %uv 0x1d67.f240.03f2.1ae3.85ee.caaa.75ba.2d1b.ff47.7d36) => "0v3l.jv4g0.3u8de.71fep.al7be.hd3fv.kev9m"
1065+(scow %uv 0x42f4.1d12.69f7.e5e0.51af.94dd.953b.d742.0312.d463) => "0v8b.q1q4j.9uviu.0kdfi.jepae.un881.h5l33"
1066+(scow %uv 0x2bfd.80fd.7c12.c7de.1dfe.adae.3040.1201.ce2b.b381.3ac7.13bb.6aef.8423.c668.6cf6) => "0va.vtg3u.no4m7.roevt.bde61.0140e.e5epo.2em72.etmlr.s44f3.6gr7m"
1067+(scow %uv 0x8b9a.4c2f.2673.59c1.230f.5e9a.d337.f7e5.202d.df26.b989.1455.1d12.95d4.ad89.29e1) => "0v12.sq9gn.icsqp.o4hgu.nkqqc.rvfp9.05nfi.dec92.hahq4.klqim.oiaf1"
1068+(scow %uv 0x293e.6cfd.9c70.32e2.f7f7.fcee.c2b3.a9cb.9bb7.40bf.9d6f.978f.3b47.498b.b789.84db) => "0va.9udju.pos1i.sbrvf.v7eoa.pqjis.rmt0b.v7bfi.u7jmh.q9her.oj16r"
1069+(scow %uv 0x3020.2084.6fa6.a63d.a004.11b7.cf36.ae3e.57a7.aacf.e4a1.2fec.0d85.0e9c.c5ee.c892) => "0vc.10422.6v9l6.7mg08.4dnps.rasfi.nkulc.vp515.vm0r1.8ejj2.uti4i"
1070+(scow %uv 0xd6ac.fa6f.e3ea.9d91.e01c.5eeb.2953.af29.d138.5524.e05e.1d49.1486.99c5.e3eb.eedf) => "0v1l.lcv9n.u7qkt.i7g1o.nnb55.9quae.h71ai.9o2u3.l4h91.kponh.unrmv"
1071+(scow %uv 0x9597.9195.0bac.7b02.96dc.240d.503a.be5a.40c4.c040.5f84.872d.71a5.3456.c627.d1b4) => "0v15.cni6a.gnb3r.0abdo.90da0.tbsmi.0oj04.0ns4g.smn39.9kar3.2fkdk"
1072+(scow %uv 0xe186.54eb.02e3.df95.f192.9eff.21fb.a889.28a8.f301.f2d6.c990.4cf6.96b5.0537.a3be) => "0v1o.c6ajl.g5ouv.inop5.7nv47.tqh29.8l3pg.3smmp.684pt.kmmk2.jf8tu"
1073+(scow %uv 0xcd36.eb61.6d70.6801.7e10.c4cf.1fa4.91d3.4a44.2526.4bb5.1611.8e56.321d.cf47.63f5) => "0v1j.9mtdg.mqs38.05v11.h6f3u.i93kq.a8gii.citl2.o8osl.hi3n7.keovl"
1074+(scow %uv 0xa76.a9fb.4d98.55a4.0bee.d8b9.859f.513c.459b.aefd.6267.9b42.4156.29f8.ecd6.5a8e.d6b6.0a50.c11b) => "0vktl9v.d6pgl.d41fn.dhec5.jt8jo.hcrlr.um4ps.r890l.cafot.jb5l3.mmmo5.51g8r"
1075+(scow %uv 0xcc9.bd2b.c4b0.49ed.1d26.f056.ebfd.dcf3.e064.c381.e7ef.04bf.21af.aca4.6039.b191.d35f.2697.5eb5) => "0vpidt5.f2b0i.fd3kj.f0lnb.vnef7.o34oe.0ufro.4nsgq.vb54c.0sr34.ejbsj.9enll"
1076+(scow %uv 0xde9.180d.888b.9132.cc3c.d7f3.26dc.29f1.1e5b.4282.b268.a5d6.9d6a.3de4.530e.3650.4eb4.0df8.776c) => "0vrq8o1.m48n4.9ipgu.dfsp6.rgkv2.7ir8a.1b4q5.5qqem.kff4a.c73ck.2emg6.vgtrc"
1077+(scow %uv 0x5c0.f40f.d3f6.e68b.c7c1.21ba.b640.33b8.a38b.d670.8c38.0759.dd69.f190.15d6.90c8.a542.011b.0b83) => "0vbg7k1.v9vdp.kbov0.i3elm.80prh.8sbqp.o8oe0.7b7em.jscg2.nb91i.55880.hm2s3"
1078+(scow %uv 0xdc7.c3a2.972c.946a.c53b.2ee8.9017.412d.6471.e152.1740.fd3c.5494.0faf.7f43.4ceb.b0f1.a117.367f) => "0vrhu3k.abip5.3aokt.itq4g.2t0iq.p3hs5.91eg7.t7ha9.83tff.t1kpq.tgu6g.hedjv"
1079+(scow %uv 0x6ab.7c11.5f52.92a8.62b2.bbcf.7971.3b8d.0a76.28ed.d00d.8080.9a70.5e9c.7787.174b.626d.9cba.95f3) => "0vdars2.5fl54.l8cap.bnjrp.e4toq.2jm53.mt03c.0g2d7.0nkse.u3hei.r2dme.bl5fj"
1080+(scow %uv 0x44e.2c6f.9f32.ccf7.1d1a.e7fe.9bb8.33a1.0b7c.9c83.c19d.6fb3.c23f.19b7.8bf9.dacf.edd7.b728.f623) => "0v8jhcd.ufj5j.7n3kd.efvkr.n0pq2.2rsji.1s37b.fmf13.u6dnh.fstlj.vdqur.ihth3"
1081+(scow %uv 0x782.13c6.92d2.8b10.8e3e.d9be.ce79.dee4.1144.eac5.b094.8126.fe4b.ccfd.487e.ac18.b6ea.840d.2b55) => "0vf0gjo.q9d52.oghov.djfme.f7fe8.4a4tb.2r154.14rv4.nj7t9.1vao6.5mta2.0qaql"
1082+(scow %ux 0) => "0x0"
1083+(scow %ux 0x1) => "0x1"
1084+(scow %ux 0x9) => "0x9"
1085+(scow %ux 0xa) => "0xa"
1086+(scow %ux 0x1f) => "0x1f"
1087+(scow %ux 0x20) => "0x20"
1088+(scow %ux 0x3e7) => "0x3e7"
1089+(scow %ux 0x3e8) => "0x3e8"
1090+(scow %ux 0x3e9) => "0x3e9"
1091+(scow %ux 0xffff) => "0xffff"
1092+(scow %ux 0x1.0000) => "0x1.0000"
1093+(scow %ux 0x9c.cfe7) => "0x9c.cfe7"
1094+(scow %ux 0xee.5432) => "0xee.5432"
1095+(scow %ux 0x1.0000.0000.0000.0000) => "0x1.0000.0000.0000.0000"
1096+(scow %ux 0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff) => "0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff"
1097+(scow %ux 0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff) => "0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff"
1098+(scow %ux 0xce) => "0xce"
1099+(scow %ux 0xc2) => "0xc2"
1100+(scow %ux 0x66) => "0x66"
1101+(scow %ux 0x5b) => "0x5b"
1102+(scow %ux 0x75) => "0x75"
1103+(scow %ux 0x7f) => "0x7f"
1104+(scow %ux 0x44) => "0x44"
1105+(scow %ux 0x2c) => "0x2c"
1106+(scow %ux 0x8096) => "0x8096"
1107+(scow %ux 0xc421) => "0xc421"
1108+(scow %ux 0x2dff) => "0x2dff"
1109+(scow %ux 0xfa82) => "0xfa82"
1110+(scow %ux 0x6620) => "0x6620"
1111+(scow %ux 0xd7e1) => "0xd7e1"
1112+(scow %ux 0x6ef2) => "0x6ef2"
1113+(scow %ux 0xbf7a) => "0xbf7a"
1114+(scow %ux 0xc6.b698) => "0xc6.b698"
1115+(scow %ux 0x6c.d8b5) => "0x6c.d8b5"
1116+(scow %ux 0x4d.f730) => "0x4d.f730"
1117+(scow %ux 0xec.4789) => "0xec.4789"
1118+(scow %ux 0x5b.a603) => "0x5b.a603"
1119+(scow %ux 0xad.7813) => "0xad.7813"
1120+(scow %ux 0x2a.7a74) => "0x2a.7a74"
1121+(scow %ux 0x2c.f469) => "0x2c.f469"
1122+(scow %ux 0x1e0.4bc5) => "0x1e0.4bc5"
1123+(scow %ux 0x143.f8ca) => "0x143.f8ca"
1124+(scow %ux 0x19c.4922) => "0x19c.4922"
1125+(scow %ux 0x2b.acd3) => "0x2b.acd3"
1126+(scow %ux 0x113.dd63) => "0x113.dd63"
1127+(scow %ux 0x7.cd7b) => "0x7.cd7b"
1128+(scow %ux 0x1b3.6933) => "0x1b3.6933"
1129+(scow %ux 0xd0.a79e) => "0xd0.a79e"
1130+(scow %ux 0x14b0.62ae) => "0x14b0.62ae"
1131+(scow %ux 0xd23.29d9) => "0xd23.29d9"
1132+(scow %ux 0x210f.4d0c) => "0x210f.4d0c"
1133+(scow %ux 0xb713.b4c7) => "0xb713.b4c7"
1134+(scow %ux 0x2d0f.d6d9) => "0x2d0f.d6d9"
1135+(scow %ux 0x1c1b.5b32) => "0x1c1b.5b32"
1136+(scow %ux 0x2bd2.ed1c) => "0x2bd2.ed1c"
1137+(scow %ux 0x6b45.1936) => "0x6b45.1936"
1138+(scow %ux 0xd9.efca.6c3a) => "0xd9.efca.6c3a"
1139+(scow %ux 0x2c.a1cb.b723) => "0x2c.a1cb.b723"
1140+(scow %ux 0x9e.2495.da17) => "0x9e.2495.da17"
1141+(scow %ux 0x2.9e0d.5425) => "0x2.9e0d.5425"
1142+(scow %ux 0xa8.618d.3f43) => "0xa8.618d.3f43"
1143+(scow %ux 0x66.67c3.b9fe) => "0x66.67c3.b9fe"
1144+(scow %ux 0x1c.f94c.f6b0) => "0x1c.f94c.f6b0"
1145+(scow %ux 0x6c.a3f3.760f) => "0x6c.a3f3.760f"
1146+(scow %ux 0xbf49.e557.c768.beaf) => "0xbf49.e557.c768.beaf"
1147+(scow %ux 0xf0f4.0471.9ba8.272c) => "0xf0f4.0471.9ba8.272c"
1148+(scow %ux 0x18b3.205e.1fd6.8335) => "0x18b3.205e.1fd6.8335"
1149+(scow %ux 0x4bc4.f0a0.24c7.0f48) => "0x4bc4.f0a0.24c7.0f48"
1150+(scow %ux 0x2d7a.2d12.fdba.24d0) => "0x2d7a.2d12.fdba.24d0"
1151+(scow %ux 0xec94.09ff.2540.3cfd) => "0xec94.09ff.2540.3cfd"
1152+(scow %ux 0x8f9b.7341.e163.e7ce) => "0x8f9b.7341.e163.e7ce"
1153+(scow %ux 0xd89b.acfa.3be1.4460) => "0xd89b.acfa.3be1.4460"
1154+(scow %ux 0xb.d285.6dcb.db9d.1cca.1e01.b04f) => "0xb.d285.6dcb.db9d.1cca.1e01.b04f"
1155+(scow %ux 0xc.3760.e399.5662.2305.0c34.9a2a) => "0xc.3760.e399.5662.2305.0c34.9a2a"
1156+(scow %ux 0x5.17b8.d928.88cc.5593.5ef8.52e5) => "0x5.17b8.d928.88cc.5593.5ef8.52e5"
1157+(scow %ux 0xb.ec4c.1908.099f.a987.e12b.6479) => "0xb.ec4c.1908.099f.a987.e12b.6479"
1158+(scow %ux 0xe.4bdf.5899.6230.a1f3.3fed.b013) => "0xe.4bdf.5899.6230.a1f3.3fed.b013"
1159+(scow %ux 0x9.80ce.7b57.11d6.5409.4197.2530) => "0x9.80ce.7b57.11d6.5409.4197.2530"
1160+(scow %ux 0xa.dfe5.5489.af4f.905a.e83e.a120) => "0xa.dfe5.5489.af4f.905a.e83e.a120"
1161+(scow %ux 0x4.8882.afe0.7a23.1ebe.c9c7.bf75) => "0x4.8882.afe0.7a23.1ebe.c9c7.bf75"
1162+(scow %ux 0x31c8.e93d.70a7.58d3.b6fe.d184.aab2.348c) => "0x31c8.e93d.70a7.58d3.b6fe.d184.aab2.348c"
1163+(scow %ux 0x586a.fe5f.3299.a5c0.e331.f1f5.563b.9c31) => "0x586a.fe5f.3299.a5c0.e331.f1f5.563b.9c31"
1164+(scow %ux 0xdc9b.ce16.dc4a.1504.6882.cca9.3b32.5271) => "0xdc9b.ce16.dc4a.1504.6882.cca9.3b32.5271"
1165+(scow %ux 0x85a8.5ac8.0054.b3fa.33d6.e711.8820.aa64) => "0x85a8.5ac8.0054.b3fa.33d6.e711.8820.aa64"
1166+(scow %ux 0xce48.62a9.c8ed.57cc.82d8.7edc.2ad8.1531) => "0xce48.62a9.c8ed.57cc.82d8.7edc.2ad8.1531"
1167+(scow %ux 0x4999.d6f0.c4d9.0124.9b1e.c183.c3d9.c44b) => "0x4999.d6f0.c4d9.0124.9b1e.c183.c3d9.c44b"
1168+(scow %ux 0xc1b4.b1b9.31aa.3d01.4b90.6d61.932d.76ed) => "0xc1b4.b1b9.31aa.3d01.4b90.6d61.932d.76ed"
1169+(scow %ux 0x6508.8190.cb2a.a305.dd1c.0115.fde7.f97b) => "0x6508.8190.cb2a.a305.dd1c.0115.fde7.f97b"
1170+(scow %ux 0x543d.1120.75f8.7337.43ca.0ebb.2c18.c4bf.ca0c.2e68) => "0x543d.1120.75f8.7337.43ca.0ebb.2c18.c4bf.ca0c.2e68"
1171+(scow %ux 0x3526.0731.400a.fd64.5c1e.6b81.2dc8.11b7.b70d.1bb7) => "0x3526.0731.400a.fd64.5c1e.6b81.2dc8.11b7.b70d.1bb7"
1172+(scow %ux 0xb322.eb50.e3c7.8e5e.d827.0e93.929a.15ea.7691.ca9f) => "0xb322.eb50.e3c7.8e5e.d827.0e93.929a.15ea.7691.ca9f"
1173+(scow %ux 0xff86.0945.efdb.d373.e9a5.2ea1.d3a0.57f0.2f04.a4ab) => "0xff86.0945.efdb.d373.e9a5.2ea1.d3a0.57f0.2f04.a4ab"
1174+(scow %ux 0xea7e.c403.e3a6.f1e5.a037.cd69.e497.05b3.92a2.dbf2) => "0xea7e.c403.e3a6.f1e5.a037.cd69.e497.05b3.92a2.dbf2"
1175+(scow %ux 0x881b.1a78.1322.8339.fafd.d23d.2b63.266d.aa3e.3c3d) => "0x881b.1a78.1322.8339.fafd.d23d.2b63.266d.aa3e.3c3d"
1176+(scow %ux 0x1d67.f240.03f2.1ae3.85ee.caaa.75ba.2d1b.ff47.7d36) => "0x1d67.f240.03f2.1ae3.85ee.caaa.75ba.2d1b.ff47.7d36"
1177+(scow %ux 0x42f4.1d12.69f7.e5e0.51af.94dd.953b.d742.0312.d463) => "0x42f4.1d12.69f7.e5e0.51af.94dd.953b.d742.0312.d463"
1178+(scow %ux 0x2bfd.80fd.7c12.c7de.1dfe.adae.3040.1201.ce2b.b381.3ac7.13bb.6aef.8423.c668.6cf6) => "0x2bfd.80fd.7c12.c7de.1dfe.adae.3040.1201.ce2b.b381.3ac7.13bb.6aef.8423.c668.6cf6"
1179+(scow %ux 0x8b9a.4c2f.2673.59c1.230f.5e9a.d337.f7e5.202d.df26.b989.1455.1d12.95d4.ad89.29e1) => "0x8b9a.4c2f.2673.59c1.230f.5e9a.d337.f7e5.202d.df26.b989.1455.1d12.95d4.ad89.29e1"
1180+(scow %ux 0x293e.6cfd.9c70.32e2.f7f7.fcee.c2b3.a9cb.9bb7.40bf.9d6f.978f.3b47.498b.b789.84db) => "0x293e.6cfd.9c70.32e2.f7f7.fcee.c2b3.a9cb.9bb7.40bf.9d6f.978f.3b47.498b.b789.84db"
1181+(scow %ux 0x3020.2084.6fa6.a63d.a004.11b7.cf36.ae3e.57a7.aacf.e4a1.2fec.0d85.0e9c.c5ee.c892) => "0x3020.2084.6fa6.a63d.a004.11b7.cf36.ae3e.57a7.aacf.e4a1.2fec.0d85.0e9c.c5ee.c892"
1182+(scow %ux 0xd6ac.fa6f.e3ea.9d91.e01c.5eeb.2953.af29.d138.5524.e05e.1d49.1486.99c5.e3eb.eedf) => "0xd6ac.fa6f.e3ea.9d91.e01c.5eeb.2953.af29.d138.5524.e05e.1d49.1486.99c5.e3eb.eedf"
1183+(scow %ux 0x9597.9195.0bac.7b02.96dc.240d.503a.be5a.40c4.c040.5f84.872d.71a5.3456.c627.d1b4) => "0x9597.9195.0bac.7b02.96dc.240d.503a.be5a.40c4.c040.5f84.872d.71a5.3456.c627.d1b4"
1184+(scow %ux 0xe186.54eb.02e3.df95.f192.9eff.21fb.a889.28a8.f301.f2d6.c990.4cf6.96b5.0537.a3be) => "0xe186.54eb.02e3.df95.f192.9eff.21fb.a889.28a8.f301.f2d6.c990.4cf6.96b5.0537.a3be"
1185+(scow %ux 0xcd36.eb61.6d70.6801.7e10.c4cf.1fa4.91d3.4a44.2526.4bb5.1611.8e56.321d.cf47.63f5) => "0xcd36.eb61.6d70.6801.7e10.c4cf.1fa4.91d3.4a44.2526.4bb5.1611.8e56.321d.cf47.63f5"
1186+(scow %ux 0xa76.a9fb.4d98.55a4.0bee.d8b9.859f.513c.459b.aefd.6267.9b42.4156.29f8.ecd6.5a8e.d6b6.0a50.c11b) => "0xa76.a9fb.4d98.55a4.0bee.d8b9.859f.513c.459b.aefd.6267.9b42.4156.29f8.ecd6.5a8e.d6b6.0a50.c11b"
1187+(scow %ux 0xcc9.bd2b.c4b0.49ed.1d26.f056.ebfd.dcf3.e064.c381.e7ef.04bf.21af.aca4.6039.b191.d35f.2697.5eb5) => "0xcc9.bd2b.c4b0.49ed.1d26.f056.ebfd.dcf3.e064.c381.e7ef.04bf.21af.aca4.6039.b191.d35f.2697.5eb5"
1188+(scow %ux 0xde9.180d.888b.9132.cc3c.d7f3.26dc.29f1.1e5b.4282.b268.a5d6.9d6a.3de4.530e.3650.4eb4.0df8.776c) => "0xde9.180d.888b.9132.cc3c.d7f3.26dc.29f1.1e5b.4282.b268.a5d6.9d6a.3de4.530e.3650.4eb4.0df8.776c"
1189+(scow %ux 0x5c0.f40f.d3f6.e68b.c7c1.21ba.b640.33b8.a38b.d670.8c38.0759.dd69.f190.15d6.90c8.a542.011b.0b83) => "0x5c0.f40f.d3f6.e68b.c7c1.21ba.b640.33b8.a38b.d670.8c38.0759.dd69.f190.15d6.90c8.a542.011b.0b83"
1190+(scow %ux 0xdc7.c3a2.972c.946a.c53b.2ee8.9017.412d.6471.e152.1740.fd3c.5494.0faf.7f43.4ceb.b0f1.a117.367f) => "0xdc7.c3a2.972c.946a.c53b.2ee8.9017.412d.6471.e152.1740.fd3c.5494.0faf.7f43.4ceb.b0f1.a117.367f"
1191+(scow %ux 0x6ab.7c11.5f52.92a8.62b2.bbcf.7971.3b8d.0a76.28ed.d00d.8080.9a70.5e9c.7787.174b.626d.9cba.95f3) => "0x6ab.7c11.5f52.92a8.62b2.bbcf.7971.3b8d.0a76.28ed.d00d.8080.9a70.5e9c.7787.174b.626d.9cba.95f3"
1192+(scow %ux 0x44e.2c6f.9f32.ccf7.1d1a.e7fe.9bb8.33a1.0b7c.9c83.c19d.6fb3.c23f.19b7.8bf9.dacf.edd7.b728.f623) => "0x44e.2c6f.9f32.ccf7.1d1a.e7fe.9bb8.33a1.0b7c.9c83.c19d.6fb3.c23f.19b7.8bf9.dacf.edd7.b728.f623"
1193+(scow %ux 0x782.13c6.92d2.8b10.8e3e.d9be.ce79.dee4.1144.eac5.b094.8126.fe4b.ccfd.487e.ac18.b6ea.840d.2b55) => "0x782.13c6.92d2.8b10.8e3e.d9be.ce79.dee4.1144.eac5.b094.8126.fe4b.ccfd.487e.ac18.b6ea.840d.2b55"
1194+(scow %ud 0) => "0"
1195+(scow %ud 0x1) => "1"
1196+(scow %ud 0x9) => "9"
1197+(scow %ud 0xa) => "10"
1198+(scow %ud 0x1f) => "31"
1199+(scow %ud 0x20) => "32"
1200+(scow %ud 0x3e7) => "999"
1201+(scow %ud 0x3e8) => "1.000"
1202+(scow %ud 0x3e9) => "1.001"
1203+(scow %ud 0xffff) => "65.535"
1204+(scow %ud 0x1.0000) => "65.536"
1205+(scow %ud 0x9c.cfe7) => "10.276.839"
1206+(scow %ud 0xee.5432) => "15.619.122"
1207+(scow %ud 0x1.0000.0000.0000.0000) => "18.446.744.073.709.551.616"
1208+(scow %ud 0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff) => "340.282.366.920.938.463.463.374.607.431.768.211.455"
1209+(scow %ud 0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff) => "115.792.089.237.316.195.423.570.985.008.687.907.853.269.984.665.640.564.039.457.584.007.913.129.639.935"
1210+(scow %ud 0xce) => "206"
1211+(scow %ud 0xc2) => "194"
1212+(scow %ud 0x66) => "102"
1213+(scow %ud 0x5b) => "91"
1214+(scow %ud 0x75) => "117"
1215+(scow %ud 0x7f) => "127"
1216+(scow %ud 0x44) => "68"
1217+(scow %ud 0x2c) => "44"
1218+(scow %ud 0x8096) => "32.918"
1219+(scow %ud 0xc421) => "50.209"
1220+(scow %ud 0x2dff) => "11.775"
1221+(scow %ud 0xfa82) => "64.130"
1222+(scow %ud 0x6620) => "26.144"
1223+(scow %ud 0xd7e1) => "55.265"
1224+(scow %ud 0x6ef2) => "28.402"
1225+(scow %ud 0xbf7a) => "49.018"
1226+(scow %ud 0xc6.b698) => "13.022.872"
1227+(scow %ud 0x6c.d8b5) => "7.133.365"
1228+(scow %ud 0x4d.f730) => "5.109.552"
1229+(scow %ud 0xec.4789) => "15.484.809"
1230+(scow %ud 0x5b.a603) => "6.006.275"
1231+(scow %ud 0xad.7813) => "11.368.467"
1232+(scow %ud 0x2a.7a74) => "2.783.860"
1233+(scow %ud 0x2c.f469) => "2.946.153"
1234+(scow %ud 0x1e0.4bc5) => "31.476.677"
1235+(scow %ud 0x143.f8ca) => "21.231.818"
1236+(scow %ud 0x19c.4922) => "27.019.554"
1237+(scow %ud 0x2b.acd3) => "2.862.291"
1238+(scow %ud 0x113.dd63) => "18.079.075"
1239+(scow %ud 0x7.cd7b) => "511.355"
1240+(scow %ud 0x1b3.6933) => "28.535.091"
1241+(scow %ud 0xd0.a79e) => "13.674.398"
1242+(scow %ud 0x14b0.62ae) => "347.103.918"
1243+(scow %ud 0xd23.29d9) => "220.408.281"
1244+(scow %ud 0x210f.4d0c) => "554.650.892"
1245+(scow %ud 0xb713.b4c7) => "3.071.521.991"
1246+(scow %ud 0x2d0f.d6d9) => "756.012.761"
1247+(scow %ud 0x1c1b.5b32) => "471.554.866"
1248+(scow %ud 0x2bd2.ed1c) => "735.243.548"
1249+(scow %ud 0x6b45.1936) => "1.799.690.550"
1250+(scow %ud 0xd9.efca.6c3a) => "936.030.923.834"
1251+(scow %ud 0x2c.a1cb.b723) => "191.693.043.491"
1252+(scow %ud 0x9e.2495.da17) => "679.218.633.239"
1253+(scow %ud 0x2.9e0d.5425) => "11.241.608.229"
1254+(scow %ud 0xa8.618d.3f43) => "723.191.152.451"
1255+(scow %ud 0x66.67c3.b9fe) => "439.827.544.574"
1256+(scow %ud 0x1c.f94c.f6b0) => "124.441.654.960"
1257+(scow %ud 0x6c.a3f3.760f) => "466.607.109.647"
1258+(scow %ud 0xbf49.e557.c768.beaf) => "13.783.800.299.714.559.663"
1259+(scow %ud 0xf0f4.0471.9ba8.272c) => "17.362.507.349.409.408.812"
1260+(scow %ud 0x18b3.205e.1fd6.8335) => "1.779.801.866.374.644.533"
1261+(scow %ud 0x4bc4.f0a0.24c7.0f48) => "5.459.753.218.882.342.728"
1262+(scow %ud 0x2d7a.2d12.fdba.24d0) => "3.276.981.238.454.953.168"
1263+(scow %ud 0xec94.09ff.2540.3cfd) => "17.047.261.480.950.447.357"
1264+(scow %ud 0x8f9b.7341.e163.e7ce) => "10.347.991.295.605.336.014"
1265+(scow %ud 0xd89b.acfa.3be1.4460) => "15.608.259.124.329.006.176"
1266+(scow %ud 0xb.d285.6dcb.db9d.1cca.1e01.b04f) => "936.662.945.351.859.439.715.852.857.423"
1267+(scow %ud 0xc.3760.e399.5662.2305.0c34.9a2a) => "967.876.757.395.797.065.129.753.090.602"
1268+(scow %ud 0x5.17b8.d928.88cc.5593.5ef8.52e5) => "403.482.435.649.275.609.753.778.148.069"
1269+(scow %ud 0xb.ec4c.1908.099f.a987.e12b.6479) => "944.640.246.544.465.345.164.817.949.817"
1270+(scow %ud 0xe.4bdf.5899.6230.a1f3.3fed.b013) => "1.132.675.659.791.753.337.656.994.279.443"
1271+(scow %ud 0x9.80ce.7b57.11d6.5409.4197.2530) => "752.917.165.061.581.255.270.141.994.288"
1272+(scow %ud 0xa.dfe5.5489.af4f.905a.e83e.a120) => "861.574.025.564.116.049.690.395.320.608"
1273+(scow %ud 0x4.8882.afe0.7a23.1ebe.c9c7.bf75) => "359.160.602.304.316.268.113.899.798.389"
1274+(scow %ud 0x31c8.e93d.70a7.58d3.b6fe.d184.aab2.348c) => "66.175.361.834.388.038.692.892.008.112.874.206.348"
1275+(scow %ud 0x586a.fe5f.3299.a5c0.e331.f1f5.563b.9c31) => "117.527.606.370.452.053.813.642.967.404.217.343.025"
1276+(scow %ud 0xdc9b.ce16.dc4a.1504.6882.cca9.3b32.5271) => "293.239.145.073.328.579.801.543.861.174.336.115.313"
1277+(scow %ud 0x85a8.5ac8.0054.b3fa.33d6.e711.8820.aa64) => "177.661.470.574.226.893.328.874.644.309.991.795.300"
1278+(scow %ud 0xce48.62a9.c8ed.57cc.82d8.7edc.2ad8.1531) => "274.196.813.633.391.732.183.778.957.152.229.004.593"
1279+(scow %ud 0x4999.d6f0.c4d9.0124.9b1e.c183.c3d9.c44b) => "97.832.424.622.990.276.455.521.690.122.167.370.827"
1280+(scow %ud 0xc1b4.b1b9.31aa.3d01.4b90.6d61.932d.76ed) => "257.479.221.280.105.514.886.965.312.107.095.881.453"
1281+(scow %ud 0x6508.8190.cb2a.a305.dd1c.0115.fde7.f97b) => "134.296.193.851.716.056.635.921.961.997.574.863.227"
1282+(scow %ud 0x543d.1120.75f8.7337.43ca.0ebb.2c18.c4bf.ca0c.2e68) => "480.917.062.161.019.171.424.826.508.831.640.889.991.256.354.408"
1283+(scow %ud 0x3526.0731.400a.fd64.5c1e.6b81.2dc8.11b7.b70d.1bb7) => "303.424.565.716.172.814.011.221.554.685.780.607.332.139.211.703"
1284+(scow %ud 0xb322.eb50.e3c7.8e5e.d827.0e93.929a.15ea.7691.ca9f) => "1.022.688.072.226.771.543.092.115.310.022.491.381.779.685.034.655"
1285+(scow %ud 0xff86.0945.efdb.d373.e9a5.2ea1.d3a0.57f0.2f04.a4ab) => "1.458.781.754.225.565.778.343.980.054.455.122.805.093.833.155.755"
1286+(scow %ud 0xea7e.c403.e3a6.f1e5.a037.cd69.e497.05b3.92a2.dbf2) => "1.338.730.809.599.284.615.532.788.739.466.115.977.232.794.180.594"
1287+(scow %ud 0x881b.1a78.1322.8339.fafd.d23d.2b63.266d.aa3e.3c3d) => "777.027.170.731.155.290.002.002.490.519.699.912.859.668.331.581"
1288+(scow %ud 0x1d67.f240.03f2.1ae3.85ee.caaa.75ba.2d1b.ff47.7d36) => "167.878.812.065.851.734.502.102.445.377.896.513.777.747.590.454"
1289+(scow %ud 0x42f4.1d12.69f7.e5e0.51af.94dd.953b.d742.0312.d463) => "382.237.305.225.045.605.595.629.764.945.591.743.112.056.853.603"
1290+(scow %ud 0x2bfd.80fd.7c12.c7de.1dfe.adae.3040.1201.ce2b.b381.3ac7.13bb.6aef.8423.c668.6cf6) => "19.897.355.053.934.732.029.046.073.507.244.966.893.894.028.087.362.548.864.880.666.565.612.333.985.014"
1291+(scow %ud 0x8b9a.4c2f.2673.59c1.230f.5e9a.d337.f7e5.202d.df26.b989.1455.1d12.95d4.ad89.29e1) => "63.144.106.204.939.068.178.830.202.833.649.372.733.035.293.060.668.449.762.590.967.684.661.038.885.345"
1292+(scow %ud 0x293e.6cfd.9c70.32e2.f7f7.fcee.c2b3.a9cb.9bb7.40bf.9d6f.978f.3b47.498b.b789.84db) => "18.655.123.535.877.015.321.248.660.968.947.535.743.217.254.858.854.826.800.457.632.229.632.539.985.115"
1293+(scow %ud 0x3020.2084.6fa6.a63d.a004.11b7.cf36.ae3e.57a7.aacf.e4a1.2fec.0d85.0e9c.c5ee.c892) => "21.767.780.264.423.972.255.272.684.980.172.737.073.864.080.201.320.191.106.086.072.027.198.505.535.634"
1294+(scow %ud 0xd6ac.fa6f.e3ea.9d91.e01c.5eeb.2953.af29.d138.5524.e05e.1d49.1486.99c5.e3eb.eedf) => "97.100.575.745.104.072.341.561.226.973.191.584.016.771.503.650.861.003.787.071.549.747.470.483.058.399"
1295+(scow %ud 0x9597.9195.0bac.7b02.96dc.240d.503a.be5a.40c4.c040.5f84.872d.71a5.3456.c627.d1b4) => "67.662.413.117.169.955.512.538.548.076.424.037.312.307.095.665.465.738.539.941.589.444.107.570.762.164"
1296+(scow %ud 0xe186.54eb.02e3.df95.f192.9eff.21fb.a889.28a8.f301.f2d6.c990.4cf6.96b5.0537.a3be) => "102.007.734.520.500.204.327.281.061.632.952.066.355.514.426.890.077.662.984.333.317.438.893.737.878.462"
1297+(scow %ud 0xcd36.eb61.6d70.6801.7e10.c4cf.1fa4.91d3.4a44.2526.4bb5.1611.8e56.321d.cf47.63f5) => "92.821.168.238.099.245.916.833.607.084.929.461.344.369.984.317.265.685.287.493.391.306.132.352.885.749"
1298+(scow %ud 0xa76.a9fb.4d98.55a4.0bee.d8b9.859f.513c.459b.aefd.6267.9b42.4156.29f8.ecd6.5a8e.d6b6.0a50.c11b) => "1.332.161.845.174.467.997.397.854.660.753.248.720.123.794.215.036.614.548.326.915.153.302.591.543.639.527.501.044.171.035"
1299+(scow %ud 0xcc9.bd2b.c4b0.49ed.1d26.f056.ebfd.dcf3.e064.c381.e7ef.04bf.21af.aca4.6039.b191.d35f.2697.5eb5) => "1.628.106.449.327.924.548.668.646.193.117.235.492.709.238.551.762.775.078.042.650.734.063.193.626.780.688.425.839.713.973"
1300+(scow %ud 0xde9.180d.888b.9132.cc3c.d7f3.26dc.29f1.1e5b.4282.b268.a5d6.9d6a.3de4.530e.3650.4eb4.0df8.776c) => "1.771.014.771.607.396.487.956.204.652.656.832.408.638.142.313.566.472.828.715.860.215.246.117.535.727.686.163.960.985.452"
1301+(scow %ud 0x5c0.f40f.d3f6.e68b.c7c1.21ba.b640.33b8.a38b.d670.8c38.0759.dd69.f190.15d6.90c8.a542.011b.0b83) => "732.533.935.316.379.044.118.161.663.535.697.755.128.852.839.093.484.221.313.852.843.252.933.233.355.179.058.641.177.475"
1302+(scow %ud 0xdc7.c3a2.972c.946a.c53b.2ee8.9017.412d.6471.e152.1740.fd3c.5494.0faf.7f43.4ceb.b0f1.a117.367f) => "1.754.439.109.077.680.850.077.795.735.541.399.990.609.993.871.687.406.986.696.143.543.188.916.043.891.784.640.527.545.983"
1303+(scow %ud 0x6ab.7c11.5f52.92a8.62b2.bbcf.7971.3b8d.0a76.28ed.d00d.8080.9a70.5e9c.7787.174b.626d.9cba.95f3) => "849.171.787.325.122.809.685.232.258.236.616.794.553.215.101.623.588.864.872.305.041.065.971.372.059.153.303.284.127.219"
1304+(scow %ud 0x44e.2c6f.9f32.ccf7.1d1a.e7fe.9bb8.33a1.0b7c.9c83.c19d.6fb3.c23f.19b7.8bf9.dacf.edd7.b728.f623) => "548.136.531.003.016.313.444.862.287.212.921.193.227.804.584.545.970.992.082.127.435.870.344.453.948.594.397.957.912.099"
1305+(scow %ud 0x782.13c6.92d2.8b10.8e3e.d9be.ce79.dee4.1144.eac5.b094.8126.fe4b.ccfd.487e.ac18.b6ea.840d.2b55) => "955.893.677.973.757.912.723.335.500.856.386.247.121.100.149.882.610.849.910.102.183.881.494.349.433.951.853.256.452.949"
jammer/tests/vectors/dockets.txtnew file+136
@@ -0,0 +1,136 @@
1+# spit-docket output from `urbit eval` (vere 4.6) with landscape's lib/docket.hoon. name | roundtrip | jam(docket) LE hex | file hex
2+site-image | roundtrip-same | 718097a86c6e4d2dac1d007aa882637b0b230309732303217bbb73637b0b2303212b9b5b9b03497303212b9b5b530b6b03317b936b0ba303214b932b1ba363cb0331937b6b03c97bab930399434b837303e2f93367017f2e8dae1cf0c3e0e0e6015eb2b2b935b5b0b62500afd0e8e8e0e6745e5eda5ce8d2dcdceae65adcc2e0c4eae65cf0f2f45ee0eac45ec8cae6d6d4c2da5ce6ecce658c1b007fd0e8e8e0e6745e5eced2e8d0eac45cc6deda5ee8d2dcdceae65adcc2e0c4eae65ec8cae6d6d4c2dac06b4aa202 | 3a7e0a20207469746c652b274465736b6a616d270a2020696e666f2b2755706c6f616420616e6420646f776e6c6f6164206465736b7320696e206465736b6a616d20666f726d6174206469726563746c792066726f6d20796f757220736869702e270a2020636f6c6f722b307839632e636665370a202076657273696f6e2b5b30203120355d0a2020776562736974652b2768747470733a2f2f6769746875622e636f6d2f74696e6e75732d6e61706275732f6465736b6a616d270a20206c6963656e73652b274d4954270a2020696d6167652b2768747470733a2f2f6d2e74696e6e75732d6e61706275732e78797a2f7075622f6465736b6a616d2e737667270a2020736974652b2f617070732f6465736b6a616d0a3d3d
3+glob-http | roundtrip-same | 71000fa63037b2b9b130b872003a83dc40c2e0e040d8c2eadcc6d0cae440ccdee440aae4c4d2e8dc802843e55ec03f637b1307f0600b73239b1b0b832b0720a0f57b8354fa9ec7986c262df525e53c3ce0a3d1d1c101f0061a1d1ddc9ccecb8bd8db1bdd1c9d5c189c4b9d9c581a9dcb9bdcd9cb19db9b580b8cdd8d4bccdcdc5a9a8b5d98588e8d0b59dad89899cb8d5b5c589a4b8c9b1b1d99cb19db9b388b837d0ee08d46470737a7f3f242c7f6e6e692f6065e531215 | 3a7e0a20207469746c652b274c616e647363617065270a2020696e666f2b27416e20617070206c61756e6368657220666f722055726269742e270a2020636f6c6f722b307865652e353433320a202076657273696f6e2b5b3120333120315d0a2020776562736974652b2768747470733a2f2f746c6f6e2e696f270a20206c6963656e73652b274d4954270a2020626173652b276c616e647363617065270a2020676c6f622d687474702b5b2768747470733a2f2f626f6f7473747261702e75726269742e6f72672f676c6f622d3076372e3173736b692e76616239362e64696363662e376e7161692e316e6e74642e676c6f6227203076372e3173736b692e76616239362e64696363662e376e7161692e316e6e74645d0a3d3d
4+glob-ames-sampel | roundtrip-same | 7180af290f24080e1ec023c9cdec0d04ad4cae1c17f0cfd8dec401beb6bc96303878007f1c5a7329f2a653f99b064f1387938972c00f6b2b9b03ff275e1b5c02f845a3a3839bd3797929c30b6b83632b73197b6b7b491b7b737381733b1732211a8017343a3ab8399d979732bcb03638b63297b1b736a03d82626a99718101 | 3a7e0a20207469746c652b274d7920417070270a2020696e666f2b27496e666f2068657265270a2020636f6c6f722b3078310a202076657273696f6e2b5b3220302031305d0a2020776562736974652b2768747470733a2f2f6578616d706c652e636f6d270a20206c6963656e73652b2747504c2d332e30270a2020696d6167652b2768747470733a2f2f6578616d706c652e636f6d2f69636f6e2e706e67270a2020626173652b276d792d617070270a2020676c6f622d616d65732b5b7e73616d70656c2d70616c6e6574203076332e3532636a672e73396b75312e6b727635392e71647368392e656464316f5d0a3d3d
5+site-no-image | roundtrip-same | 7180078a2d2ccd1de0777a03496b0b3b2b0784ffff02fe5c1a5d39e087c1c1cd033c706c61696eb34c0ec00b1a1d1ddc9ccecb4b195e581b1c5b99cb9bdc19784d4954 | 3a7e0a20207469746c652b27506c61696e270a2020696e666f2b274e6f20696d616765270a2020636f6c6f722b3078666666660a202076657273696f6e2b5b30203020315d0a2020776562736974652b2768747470733a2f2f6578616d706c652e6f7267270a20206c6963656e73652b274d4954270a2020736974652b2f617070732f706c61696e0a3d3d
6+weird-strings | roundtrip-crash | 71007e92e84ee64044e2eadee8cac84440b840c4c2c6d601881a0b331b4e0511c7ac0419e6751b7e1d1bb6251b4e0d1800000bf8736974e5801f0607370f7eb704f8a5b59d01dca5d1a1b5b8d5b18bd300f4a0d1d1c1cde9bcbce0b98cbdb5bdfcc4f59c849d007fba30b104b432b93205b7b23bb634b732 | 3a7e0a20207469746c652b2749742773202271756f74656422205c206261636b270a2020696e666f2b27636166c3a920e2989520c3bc6ec3af63c3b664c3a9270a2020636f6c6f722b3078312e303030300a202076657273696f6e2b5b31203020305d0a2020776562736974652b2768747470733a2f2f782e636f6d2f3f713d276127270a20206c6963656e73652b2774616209686572650a6e65776c696e65270a2020696d6167652b27696d67776974682d6e756c270a2020736974652b2f617070732f770a3d3d
7+big-numbers | roundtrip-same | 71c0134a3b07789deadac4cae4e659c03f637b1367067c343a3ab859a0d00f5887d6d214 | 3a7e0a20207469746c652b27426967270a2020696e666f2b274e756d62657273270a2020636f6c6f722b3078300a202076657273696f6e2b5b312e30303020312e3233342e35363720305d0a2020776562736974652b27270a20206c6963656e73652b27270a2020626173652b27270a2020676c6f622d687474702b5b2727203076305d0a3d3d
8+empty-site | roundtrip-same | 71663059c09f4ba32b5b5a8e3bc79d | 3a7e0a20207469746c652b27270a2020696e666f2b27270a2020636f6c6f722b307831320a202076657273696f6e2b5b30203020305d0a2020776562736974652b27270a20206c6963656e73652b27270a2020696d6167652b27270a2020736974652b2f0a3d3d
9+comet-ames | roundtrip-same | 718067e8adad8c1e7c1c60f5e6d5c4b3a29105fc33b63771a37e000480221d3585fee362987959942041f8915e817c498c40c7c0c04feeeceac28ca90cf8616d6573803e400002923acfd5398c3760820f33629f850ec9c1ef7036 | 3a7e0a20207469746c652b27436f6d6574270a2020696e666f2b2763270a2020636f6c6f722b307831322e333435362e373839612e626364650a202076657273696f6e2b5b33203220315d0a2020776562736974652b2777270a20206c6963656e73652b276c270a2020626173652b2763270a2020676c6f622d616d65732b5b7e6461737265732d7261676e65702d6c69736c79742d72696270796c2d2d6d6f736e79782d62697364656d2d6e696466756c2d6d61727a6f6420307631352e39686a31652e6c723765392e76306331682e71306868342e6e703061752e69377334322e38346b62352e7370676f6e2e337671326a2e61373932675d0a3d3d
10+moon-ames | roundtrip-same | 71c06f7a7b73076f07d102fe19db9b388047dbdb9b5b0b59d9dc3a037e585bd91cf0fec46b83f908e24b78ba20197c0ceafce077381b | 3a7e0a20207469746c652b274d6f6f6e270a2020696e666f2b276d270a2020636f6c6f722b3078610a202076657273696f6e2b5b39203939203939395d0a2020776562736974652b2777270a20206c6963656e73652b276c270a2020696d6167652b2769270a2020626173652b276d6f6f6e2d6465736b270a2020676c6f622d616d65732b5b7e646f7a66656c2d6d61706e65702d73616d70656c2d70616c6e6574203076305d0a3d3d
11+zod-ames | roundtrip-same | 71c0d37a2307d7074c00008005fc33b63771c0d37b23c7013facad6c6e9669f03b9c0d | 3a7e0a20207469746c652b275a6f64270a2020696e666f2b277a270a2020636f6c6f722b30783130302e303030300a202076657273696f6e2b5b30203020305d0a2020776562736974652b2777270a20206c6963656e73652b276c270a2020626173652b277a6f64270a2020676c6f622d616d65732b5b7e7a6f64203076315d0a3d3d
12+odd-path | roundtrip-crash | 71c07b2223078707e2bf803f9746570ef8617070f300afd1db1b8850983ce0a7d19dcccde29883dfe16c | 3a7e0a20207469746c652b274f6464270a2020696e666f2b2770270a2020636f6c6f722b307866660a202076657273696f6e2b5b31203120315d0a2020776562736974652b2777270a20206c6963656e73652b276c270a2020736974652b2f617070732f466f6f204261722f697427730a3d3d
13+parse-deskjam | roundtrip-same | 718097a86c6e4d2dac1d007aa882637b0b230309732303217bbb73637b0b2303212b9b5b9b03497303212b9b5b530b6b03317b936b0ba303214b932b1ba363cb0331937b6b03c97bab930399434b837303e2f93367017f2e8dae1cf0c3e0e0e6015eb2b2b935b5b0b62500afd0e8e8e0e6745e5eda5ce8d2dcdceae65adcc2e0c4eae65cf0f2f45ee0eac45ec8cae6d6d4c2da5ce6ecce658c1b007fd0e8e8e0e6745e5eced2e8d0eac45cc6deda5ee8d2dcdceae65adcc2e0c4eae65ec8cae6d6d4c2dac06b4aa202 | 3a7e0a20207469746c652b274465736b6a616d270a2020696e666f2b2755706c6f616420616e6420646f776e6c6f6164206465736b7320696e206465736b6a616d20666f726d6174206469726563746c792066726f6d20796f757220736869702e270a2020636f6c6f722b307839632e636665370a202076657273696f6e2b5b30203120355d0a2020776562736974652b2768747470733a2f2f6769746875622e636f6d2f74696e6e75732d6e61706275732f6465736b6a616d270a20206c6963656e73652b274d4954270a2020696d6167652b2768747470733a2f2f6d2e74696e6e75732d6e61706275732e78797a2f7075622f6465736b6a616d2e737667270a2020736974652b2f617070732f6465736b6a616d0a3d3d
14+parse-landscape | roundtrip-same | 71000fa63037b2b9b130b872003a83dc40c2e0e040d8c2eadcc6d0cae440ccdee440aae4c4d2e8dc802843e55ec03f637b1307f0600b73239b1b0b832b0720a0f57b8354fa9ec7986c262df525e53c3ce0a3d1d1c101f0061a1d1ddc9ccecb8bd8db1bdd1c9d5c189c4b9d9c581a9dcb9bdcd9cb19db9b580b8cdd8d4bccdcdc5a9a8b5d98588e8d0b59dad89899cb8d5b5c589a4b8c9b1b1d99cb19db9b388b837d0ee08d46470737a7f3f242c7f6e6e692f6065e531215 | 3a7e0a20207469746c652b274c616e647363617065270a2020696e666f2b27416e20617070206c61756e6368657220666f722055726269742e270a2020636f6c6f722b307865652e353433320a202076657273696f6e2b5b3120333120315d0a2020776562736974652b2768747470733a2f2f746c6f6e2e696f270a20206c6963656e73652b274d4954270a2020626173652b276c616e647363617065270a2020676c6f622d687474702b5b2768747470733a2f2f626f6f7473747261702e75726269742e6f72672f676c6f622d3076372e3173736b692e76616239362e64696363662e376e7161692e316e6e74642e676c6f6227203076372e3173736b692e76616239362e64696363662e376e7161692e316e6e74645d0a3d3d
15+parse-dupes-order | roundtrip-same | 717061707c20fe0bf8736974e5c670f0bc25e0314b939ba317874407bf03af29890a | 3a7e0a20207469746c652b2742270a2020696e666f2b2778270a2020636f6c6f722b307866660a202076657273696f6e2b5b31203220335d0a2020776562736974652b2777270a20206c6963656e73652b274d4954270a2020696d6167652b276669727374270a2020736974652b2f782f790a3d3d
16+parse-escapes | roundtrip-crash | 71c04fa33b9907204a2c6c6c8d6b8e2d6c0e0d44842c4e04642ccc6c38350bf8676c6fe2009e7465726d2d6e6f742d636f72e40cf86874747080271a1d1ddc9ccecb4b988bd8cb380b14fa21d1c1ef7036 | 3a7e0a20207469746c652b2769742773270a2020696e666f2b276261636b5c736c617368202264712220636166c3a9270a2020636f6c6f722b3078300a202076657273696f6e2b5b312e303030203220335d0a2020776562736974652b2777270a20206c6963656e73652b276c270a2020626173652b277465726d2d6e6f742d636f7264270a2020676c6f622d687474702b5b2768747470733a2f2f612e622f6327203076305d0a3d3d
17+fuzz-1-0 | - | 71803e8b6b0e4a87c56b0c80c0615a5c27c3a92061616130610a270a6130275a5a30272027275a5c610a30c3a9e298955cc3a920300a30275c5c5a300a615a270a0ae2989501baa7093f463fbc803f974657b604e0150bf8cd5472affe03bc4567f295159d8199dd332ced7aa200b1509a3687bdaed934be2d752c40aaba439db680e1b33270e30637e6003c084290a8209c1f3cfe25e278c1528e5445019f386625179716 | 3a7e0a20207469746c652b27595c73503a2c5e23270a2020696e666f2b27615a5c27c3a92061616130610a270a6130275a5a30272027275a5c610a30c3a9e298955cc3a920300a30275c5c5a300a615a270a0ae29895270a2020636f6c6f722b3078333837652e386337652e313334660a202076657273696f6e2b5b37362e36303620312e37363320312e3633355d0a2020776562736974652b278210242a08e7078f7f89385eb094235551270a20206c6963656e73652b27e298955c5c5a270a2020696d6167652b272ce03753c9bdfa0ff0169dc9575674066676cfb0b4eb8902c44269da1cf6ba66d3f8b6d4b1a9ea0e755a270a2020736974652b2f0a3d3d
18+fuzz-1-1 | - | 7100ba4f60c2144014c2b8c4312b4fc2b886538753b9b8c4312b15b8c4312bc3e000dd89963866251c7601ff8ced4d1c40fb4421ec244c411cb3324c1cb3520b0e406d7366dce2258043f5664e1e4efcc686fefb23703ed0905e28553dcc011f8d8e0e0e78a396aa70825a80591c20a955aa6475c0ca9c6976198e8d0378d74515817d8924b94d1189d0e424a1c1ef0801 | 3a7e0a20207469746c652b272730610a200a615ce2989527615cc3a9c3a95c5ce298950a5ce298956130270a2020696e666f2b27275ae2989530270a2020636f6c6f722b3078360a202076657273696f6e2b5b312036332e3032352e3030372e32383520362e3939332e3836322e3836302e3338302e3632305d0a2020776562736974652b27755145605f22496e53442234394968270a20206c6963656e73652b273b42270a2020626173652b27270a6127610ae2989561e298955a30270a2020676c6f622d687474702b5b27514b5538412d40272030763663376c2e6169676e6b2e67713076372e30387672762e71336364762e32653370372e36647461332e67306975352e6e333665645d0a3d3d
19+fuzz-1-2 | - | 71007fde56579d4a3cfda21a520ae812e1256000bc5a834a4933e3415311722a422373d11a6a29290a71ab0b59a17229c1f159aba271c9aab9f3a169b9822a2b06b0d0e4fb06721461b1f505fc33b637710002604e8653c38653c5312bc5312bc5312b03d88a327112d5c0550977033554d1cc801fd656360790d1a96880a5b290a17e55481e9e833067edaf59d0342020891a80c6cb51736450661010d1f41c4037785bc22eb5ce323fdc94c716d0a6c92df5441d95 | 3a7e0a20207469746c652b27bcadae3a9578fa4535a414d025c24b40270a2020696e666f2b276b502969663c682a424e4568642e5a432d2545216e75212b542e25383e6b55342e5935773e342d57506545270a2020636f6c6f722b307831652e623632632e323238652e343064662e376339610a202076657273696f6e2b5b34312034312e35343420305d0a2020776562736974652b2797a3e6c8a0cc2020a2e939806ef0b6845d6a9d657eb8298f2d270a20206c6963656e73652b2769724b3d514725270a2020626173652b273027c3a961c3a9e29895e29895e29895270a2020676c6f622d616d65732b5b7e736f676e65762d2d66616d7365742d6669646465782d6c61727279632d726f6c6e79782d2d64616e7075742d64697274756c2d6e6f707375742d6669676e656d203076396a2e386c3864382e336573346c2e6267366c322e396f6a35325d0a3d3d
20+fuzz-1-3 | - | 71001e5384cc52fa5244e2428a9abeb86658c09252c2ea6c6c66649001d026acad084647a68c8f04e9cdce0c4fc7c6e88a8cc664afee0708e52eebca0ca80c8b4c2b446aa90ef83d485cfc02fe5c1a5d39e0b394b9a9838a960042133e6a8d808c081b979bb15aeef007b444c1e7f440fbebe7a6590b18fa380306767c204aaa1507a0394cee89264ce7c808cfc8eca9262fcf6f8b4de5a52d2c0dca6606c86a078868f427cb125351b80802 | 3a7e0a20207469746c652b27294266297d29227121454d5f5c332c60492961753636333248270a2020696e666f2b27616d45303a32657c24486f7666783a3646576434267b753f402877595766406558645a21524b35270a2020636f6c6f722b3078376335632e343833640a202076657273696f6e2b5b38302e313232203132372e3434382031322e3933372e3837345d0a2020776562736974652b2761724f34613a47467846664f3579797e5b6c2a2f6d616950363340563b270a20206c6963656e73652b271afdc9b2c454142e82270a2020696d6167652b27091fb5464046848dcbcd582d77f8035aa2e0737aa0fdf573d3ac270a2020736974652b2f2c656e6a2f220a3d3d
21+fuzz-1-4 | - | 71001ed142ca8aecf8e66252c09ed28eaec0ec9ac692405eb0f6b29801d00f446b385541e104268c0be6440b46814b1cb3520be60404264c414101643855eb842b4c411cb352814b1cb37238350c0c3068dd74312fe09fb1bd8903411c4034a8ea1629ea1ef0f959e075e33ce0a3d1d1c101f81ce3467797a782e503752555938405f3e6b5f372c54646f5425734f4037506823386a5263202d7f613a463432453962374a40674975553d6e4552d01682d8e59a9c5a5258e59a90513c605339caa309c0a329caa15268e5909238e59399caac0c57502737202c2359c3a9c0a03a2050313268e59c905c305167066d61bd88f0cd0b81d9e3263d6ff60e903f0e8a6b4c713e4a6f74337f2368574f523849302d757a386a5d607cfa505 | 3a7e0a20207469746c652b2768216545767c733129604f69475760764d6349202f587b594c270a2020696e666f2b27205ac3a90a0a2730615c30275a300a5ce298955a30272030610a0a0a20c3a95a275c610ae298950a5ce29895c3a96120270a2020636f6c6f722b3078312e636335642e333735610a202076657273696f6e2b5b3338372e38393120362e34333120312e3132382e3433302e3932342e3131342e3032362e3534362e3130335d0a2020776562736974652b276e4a7b3c416e7a3f74236f5348573f423829707d356a586a7d270a20206c6963656e73652b275c5a270a2020696d6167652b27e298955a5c5ae298955a30615c30c3a90ac3a920c3a95a61e2989530e29895c3a90a5c5c27302727205cc3a9c3a930205a303061e298955c305c20270a2020626173652b27c4270a2020676c6f622d687474702b5b27316e7477792a583e505752354958306f5e3b2f576c64542f7445433f5067203863586a2223706d3f413a364432653942476a40775935654d5e5527203076652e65367466302e62377376302e376e6135342e62656c61315d0a3d3d
22+fuzz-1-5 | - | 71900d80105a305a205cc3a9e29895c3a9270a275a5a0ae2989527275c5a300a275a5c205ac3a901179190fe05fc33b637717069007a46ce16fcb04972dbe7d12f33a6cf1fe92d2c9fdf808f464707070884e37a36efeda471669980dd29f439aa66298e07109c24638e6a7a383056ea1016043f6704 | 3a7e0a20207469746c652b270b270a2020696e666f2b275a305a205cc3a9e29895c3a9270a275a5a0ae2989527275c5a300a275a5c205ac3a9270a2020636f6c6f722b30783766342e383438380a202076657273696f6e2b5b30203020322e3034392e3530312e3939372e3830382e3139372e3936365d0a2020776562736974652b279364cc514d0f07c64a1dc282270a20206c6963656e73652b277346270a2020626173652b2752270a2020676c6f622d687474702b5b27e1b89ecd7b3b699c2720307635762e377063356e2e6b68766a742e3636636e742e33707572652e39347231762e306d7070335d0a3d3d
23+fuzz-1-6 | - | 71007e86ee70e8b2a294e08482e4e27e7e4ed6d66c98ac01a049983010180598131885e154aee1542e71cccae15471cccab01385b0b0e154182d1890e154903071cc4a98302d71cc4a05ad3018aee1542e90e154ae3085302e1071cc4a2e85e154aee154851318981310101805ade15471cc4a2d18181898302e71ccca133040288b555608605ac03f637b13073d03abff033e1a1d1d1cf08863568671b3c0080cd870da9a10d0d4e78142cf01f853e00201e10281810983194e15c7ac040101194ed5e2d2d2021661140e220c | 3a7e0a20207469746c652b274377387459514a70424172713f3f276b6b364c56270a2020696e666f2b27306120300a3027300ac3a95cc3a95ce29895c3a9e2989561270a6161c3a9305a3020c3a92061e2989530615ae298950a5a61305cc3a95c20c3a95c610a615c20e298955c0ac3a95cc3a90a273030272020300a5ac3a9e298955a30303030615ce298952720270a2020636f6c6f722b3078613630302e383536352e353862320a202076657273696f6e2b5b323538203237382e3538332e393230203232322e3534332e3531302e3030355d0a2020776562736974652b270a5c20205c2030306130c3a9e29895202020c3a95a5c5a5a270a20206c6963656e73652b27c2281c4418270a2020626173652b2727270a2020676c6f622d687474702b5b27e29895615c272030763776715d0a3d3d
24+fuzz-1-7 | - | 71003e5c6ea8fade4c58caee84f65a748250e0d2c60184deb0ba1778f323e1ccf80336739ece2ee09fb1bd89037882808028706981c0c0886356860398a4e62000056bbcbf120ff8616d6573408fa20d8b9cc403ed97001a4fb48653b9144ec2601460b48653156014c240b4c24eb8c2e0c20e389519044463ce85f8c6017883e18251808151e0d2e212c7ac84d112c7ac8411c7ace40a | 3a7e0a20207469746c652b272e37547d6f262c6577427b2d3a4128706963270a2020696e666f2b276f58dd0bbcf9917066fc270a2020636f6c6f722b3078316233612e373963630a202076657273696f6e2b5b36203236382e363933203835342e3537332e3034332e3239395d0a2020776562736974652b27305c300a30300a5c5a5ce29895305ae2989530e298955c270a20206c6963656e73652b27270a2020696d6167652b27275ac3a95c0a2761300a305ac3a90a300a61205a61275c6130270a2020626173652b272020200a5c5a203030e2989561270a2020676c6f622d616d65732b5b7e6d69646e79642d6269737479642d6d6f6372756e2d6c696e77657220307637326176722e6f716f35302e30676564395d0a3d3d
25+fuzz-1-8 | - | 71802e4c1cb3920b0c40089702170c9702970288d6899682c270ea706aa08be902fe19db9b388050828b8b007162fba2af7d00ae3413e4c69e448ce9ba4a689b9120a73b00b0aaf7581bf0c3dacae600bb3501dfb576497933957f0259028469029fea6598bf025ab435deb0638b83be000dfdb1eb561d69b6b53a0051878e5bf5186d2cc73fe596fec93bf5364cc5675583806e92bf8d159f06 | 3a7e0a20207469746c652b2761e298955c20270a2020696e666f2b275c0a5c305c0a5c0a205a275a0a0ac3a9c3a9270a2020636f6c6f722b3078333463350a202076657273696f6e2b5b31203437203938352e3431382e3232382e3931352e3631352e3738352e3937385d0a2020776562736974652b27878e5bf5186d2cc73fe596fec93bf5364cc5675583270a20206c6963656e73652b2793fc6dacf834270a2020696d6167652b2734814ff532cc5f012dda1a6fd8b1270a2020626173652b27092e2e02c489ed8bbef6270a2020676c6f622d616d65732b5b7e646f707365762d66697074756c2d2d646f6c6d75642d646f637365642d62616c6c65762d62696e62656c20307631646366662e616c6730302e74716538342e686a646b342e6c656e39682e68323974682e6e343263715d0a3d3d
26+fuzz-1-9 | - | 71c0c7a13a8307805f3a98a6151feb3911f7dbfe79e1f75ab5b8cc993a4c8bda42f9eb5179828e06e75a32b076e6cc3ab14f051438e156e41e5d1385177c50638f7400932a624f5e683245ff1233721f508e987df781fcd602fe5c1a5d39a0c36119b10189eb44cd324060ec08360e9856adfbeacb0734393e1820a2f8ec326f9dac09a709 | 3a7e0a20207469746c652b2738546770270a2020696e666f2b271d4cd38a8ff59c88fb6dffbcf07bad5a5ce64c1da6456da1fcf5a83c414783732d19583b73669dd8a7020a9c702b728fae89c20b3ea8b1473a804915b1272f3499a27f8919b90f2847ccbe7b270a2020636f6c6f722b30786164660a202076657273696f6e2b5b3020332e3831342e3735362e30333820322e3038372e3137372e3830332e3433355d0a2020776562736974652b27727c30270a20206c6963656e73652b278acf2ef3d6c99a709a270a2020736974652b2f7058462c2f5c270a0a3d3d
27+fuzz-1-10 | - | 7100fe8a6866a0b0a65ed864848aeaeee2da42c842e460a0ce58b40111bbb137406a28867f56149b05fc33b63771a0d5e400bace1bec7b006d72d74bfb0dcd4976c047a3a38303f8f7d9b91a2a130122131a0be28299d26b7942992b732a3be303bb531b793bcaba97008db0b0b0e15471cc4ab88010361cd01e884640e1bcdd867c00426ce5fbf46c6ea3762840b1630339bca9816f81ef1a9e9704 | 3a7e0a20207469746c652b274534335058532f6c3242457577716d216421723050672c5a270a2020696e666f2b27d88dbd270a2020636f6c6f722b3078392e623134352e363766382e363238360a202076657273696f6e2b5b35302e30313720322e3331312e3138332031362e3234372e3534342e3738325d0a2020776562736974652b27b6f27d7a36b7513b14a0d8b1811cded4c0b7270a20206c6963656e73652b27aee17949270a2020696d6167652b27616161c3a9e2989530270a2020626173652b276a32270a2020676c6f622d687474702b5b273e3b5743652240644263415c30537a2d4f2873654e65677c60776a236f4759772720307672342e6a6a3864762e64357465732e6a643031742e756f3675655d0a3d3d
28+fuzz-1-11 | - | 71003c80d102194e0dd33a5110c7ac5438194e3d010139d1d202d13a51d05280194ed5e206d087aa6a19994bb9d182aa03239a99196b036eb1ce330bf8736974654b0002d1210bfc61476dbdea994865012fbc69e351849df82dbbf36018b7c0d45fe819287cba3b525711891630c68b06e48bc8ef07fce35494cdfa5ee8ea003c48e545e57c0d39cd04a10dd9f0bda9f4390111a160a02c31 | 3a7e0a20207469746c652b27305a20c3a9615a270ae298950a27c3a9272020275a5a205a270a5a0a30c3a95a5c270a2020696e666f2b2750552d237329375a507560443333632d270a2020636f6c6f722b30783463662e336163350a202076657273696f6e2b5b38332e343237203236362e3330392e39313120372e3730332e3531312e3539332e3930312e3131382e3639315d0a2020776562736974652b27527951395f434e33416843367c6f2a7d4e270a20206c6963656e73652b27848281b2c4270a2020696d6167652b2774c8027fd8515baf7a265259c00b6fda781461277ecbee3c18c62d30f5177a060a9fee8ed45544a2270a2020736974652b2f0a3d3d
29+fuzz-1-12 | - | 71005a4fc4312b61b4144e86538753c5312bc3c2141414c4312bc5312be100f0840f540c510e1d9f54930d8f919e0c931759d14a98918ede9648d1db5a8bdb1316d0db8909120d0c5c8bcb55dcda8f0e1898d48bde4e149d58cc9e1a101fc8de4b0e1a145d3780c4beec44674e44c30e16f0e7d2e8ca19f83972f0b259b01a60700adf050c385f277def11c218000d14b8b48653c5312bc5312b61c2b4c2b44e1460404e86538753c314b44ec4312b61e015e39201 | 3a7e0a20207469746c652b2727e29895305a0a27c3a9c3a9e2989561610a0a0ae29895e2989530270a2020696e666f2b273e50314439747c524d363c467a324c5e64452b61463a7a5b22456f6b2d6e4f58406f2726483430702d2e57716b3f3a6060522f7a3b517462317b6a407c207b2f396850745d270a2020636f6c6f722b3078382e336230642e313133392e396431332e623266620a202076657273696f6e2b5b323120362e3435342e3736332e38353420312e3233322e3539312e3035302e3933372e3236335d0a2020776562736974652b270a5c5ac3a9e29895e2989530615a615a270a302027c3a9c3a9610a5a27e2989530270a20206c6963656e73652b27c5b864270a2020736974652b2f2f73642f650a3d3d
30+fuzz-1-13 | - | 71001d750bad2549788b10e53700626a2627d2c267d023686f8a323986e0522333e8481ccca8b2f246f496e0ab90af064899e2b6292ddd388b5ee3ba16f24e80f81d06187b5dcc19574516f0e7d2e8ca9600bcd1ab11a252812132e3930bf2d16119e2eb32f10b33d2294b31435a2a128b21c172221941aaa2391707581cba74672bc02759efee7b49e1d330961da029bbd311f2811e06a20502 | 3a7e0a20207469746c652b27ea165a4b92f01621ca2f270a2020696e666f2b279ac989b4f019f408da9ba24c8e21b8d4c80c3a120733aaacbc11bd25f82ae4ab0152a6b86d4a4b37cea2d7b8ae85bc13207e87270a2020636f6c6f722b3078312e323261622e386365362e326562640a202076657273696f6e2b5b312039332e3230392e3634382e333938203531332e3239302e3335342e3333342e3431312e3634312e3032372e3237335d0a2020776562736974652b2765773a423e270a20206c6963656e73652b2761205a20270a2020696d6167652b277a3542542a3044667c72413e3a2c437c5d267e61463a652966484b45623124584e242348553467270a2020736974652b2f0a3d3d
31+fuzz-1-14 | - | 71808fe8c70bec291900214bc6b8c030b5e57517f2156aba5f5b4baec7fae9cde3a912359509053d0f2ed4d56dedc6d7e6579588bf37706c0904373883b3807fc6f6260ee0e3455203645612d2a3320e204694c98ff03e34228f557318f0c3dacae600b5f965211cb7857a76c276ddb3808230780c066a806f38551cb3920bf84fc2c4312b6140b8 | 3a7e0a20207469746c652b27443f5e604f49270a2020696e666f2b2725635c6098daf2ba0bf90a35ddafad25d763fdf4e6f154899aca84829e0717eaeab676e36bf3ab4ac4df1b38b604821b9cc1270a2020636f6c6f722b3078300a202076657273696f6e2b5b3233302e3135322031203333365d0a2020776562736974652b27c3a9e298955c270a20206c6963656e73652b272761e2989530205c270a2020626173652b275e2435406625213d2a63270a2020676c6f622d616d65732b5b7e77616e7065782d72697674796e2d2d646f7673656e2d6469726d79722d62696364656e2d68616c6e79742030763437362e6c636634382e71337473342e66703661345d0a3d3d
32+fuzz-1-15 | - | 71006f9991a72bb3a5302fa4361735b9227300f4e21a4ed51a4e8539e1520039091b4e85818111c7ac14c7ace40a1b4e3d090b53081b4e0d0b0b830101090b83036a571bf005fcb934ba7280b7a40cdaae624f1ef084d79a01025c80ce71b3c0a40c50906a08160a5cc33cec06e84e7ccef2b248c0f32282760306 | 3a7e0a20207469746c652b2732234f57664b615e486d2e6a724566270a2020696e666f2b275cc3a95ac3a930275c0a202761c3a9303030e29895e298955c61c3a92761610a61c3a9616161302020616130270a2020636f6c6f722b30783363302e366435640a202076657273696f6e2b5b3239372031302e3832392e3833302e3832302039382e3037322e3936365d0a2020776562736974652b27273e67795924270a20206c6963656e73652b272f22683760270a2020736974652b2f2565d076157b722fc26b4d2fb89de30a3d3d
33+fuzz-1-16 | - | 7100dd1222a693b515ac9c9eba9b97933c40b8c3022719881df04bb5fbb805fc33b63771c097f1005506089e9e7baa037e585bd91cb0ee594ae61462da2c10c381b329e0d2749fdb227a00f2868b5bb4fbf45fb774f44abe1b37577ba3d375694395e311f0d418a1c19801 | 3a7e0a20207469746c652b2725444c276b2b58393d75372f2739270a2020696e666f2b273b2c709281d8270a2020636f6c6f722b3078356337642e646161350a202076657273696f6e2b5b31393520312e33333420372e3939302e3137332e3439342e3933385d0a2020776562736974652b27c3c52dda7dfaaf5b3a7a25df8d9babbdd1e9bab4a1caf108270a20206c6963656e73652b273546683066270a2020626173652b27321ea04a270a2020676c6f622d616d65732b5b7e646f7a6c75702d66616c7365672d646f736c796e2d646f6c64796e20307633612e6a726a71665d0a3d3d
34+fuzz-1-17 | - | 7100fe8af2d040725862789ec08464687a528ed2c2c2cad8caaeb001a04771cc4a18901385e154ae931371cc4a71ccca933071cc4a2d1071ccca3090e1d49393e154ad131071cccab013102d98b030102d181071cc4aade15418102e90e1542d1085e154aee154903071cc4a9013102e2e2e2d85302e71cc4aaeb0e1540585132e30400fda6c37b99d1c4016f0cfd8dec401bc6a7f5f7bbc802114d73efc01a8c1ced49d82beb581a4a14039c38069f4a9fb0537e0a3d1d1c10168278e59298e59298e59399cca750202228e59c9a5c5359caa208e590903a2359c2a8e5909329c2a8e59a905131676029600bc926962916a1243cbc391ca9ae9cafb21e98bfb51f14b211103e342ebca4b12ab41d3fb920a8932e33bf3cb164c0618fa530bbf02baed762a69963680af1c5d99d10a56130e4d0c5689131af060edd313dc01 | 3a7e0a20207469746c652b2745796820392c313c4f604232343d2947696161656c655758270a2020696e666f2b27e298953020270ac3a95c2727e29895e298952761e298955a20e298956120c3a92727c3a95a2720e298956127205a306161205a3020e298955ac3a930205c20c3a95a200ac3a95cc3a92061e298952027205c5c5c5a0a615ce298955c61c3a90a0a275c20270a2020636f6c6f722b3078323430312e633964622e393337362e636461300a202076657273696f6e2b5b313820352e3839372e3536322e3632312034392e3637302e3533302e3539322e3231395d0a2020776562736974652b27727465462b584d38343158254e68270a20206c6963656e73652b2758fbf40477270a2020696d6167652b27324d2c524d626879385259335d793f247d713f2a7e292462605c685d79496235687a5f522151667c677e59270a2020626173652b27b5bfaf3d5ec0108a6b1f7e270a2020676c6f622d687474702b5b27e29895e29895e29895c3a95c272020e298955c5a5cc3a90ae2989530205ac3a9e2989520c3a9e298955a306161272027203076622e67627574392e75686b6f312e67707038322e67713930642e6c6e7131392e726c36656f5d0a3d3d
35+fuzz-1-18 | - | 7100c2b94e6014b4b46060b8c4312b6160b84eb8c4312bc3408653151460c4312b03c03be6e2ba691b2b3631c646e3cb5df3e51263e6fac794b2588b5c0e1f2175e4a3e2ab34c61bef8ed1eea93154cddaf44ca34ab34663736ee84f3434d91ae84dbfa48fcb07c6f9e49a9bc6a0945933fadc00b10339d089f4b77a01ff8ced4d1c20fc093534b2420de0a0873de3e2561be3fc801fd6563607f892f3229e0e1b72a79830c509beaab330038ef043017f7427e53c56cca8350009aea3eca5f8c96f557b667d1aa41fa8d60f68d8d8e8a874 | 3a7e0a20207469746c652b275c27300a5a5a30305ce2989530305c275ce298956120c3a90a0a30e29895270a2020696e666f2b27e6e2ba691b2b3631c646e3cb5df3e51263e6fac794b2588b5c0e1f2175e4a3e2ab34c61bef8ed1eea93154cddaf44ca34ab34663736ee84f3434d91ae84dbfa48fcb07c6f9e49a9bc6a0945933fa5c270a2020636f6c6f722b307865612e646664322e323734302e653430650a202076657273696f6e2b5b34203333312e37313420362e3234322e3838372e3736362e3537392e3132352e3731335d0a2020776562736974652b27aea3eca5f8c96f557b667d1aa41fa8d60f270a20206c6963656e73652b276c6c74543a270a2020626173652b273fa1864656a8270a2020676c6f622d616d65732b5b7e62696474756e2d6d61707465722d77697473756c2d2d68616e7365622d6c61746665782d7269637265732d626f72727963203076337373632e646c646f6e2e33376d33715d0a3d3d
36+fuzz-1-19 | - | 7100bd22962b10a615aa1917b23e3b005e5d2595c5e5890de9e0d0d490d1c8bc60a989c15cc528b5f5e5dd21b1c58834d9f0f8e07dad7991ccadc49003e4ee6d9790d7d7ee5fc03f637b1307f888b3088e67e301b45130bb2106774fa51ffd033905fcd1801fd6563607881852dca0d325fee58066815b6df02360fa858cdd4f18401446ebe4e4248c2b8c0be66438158a03 | 3a7e0a20207469746c652b27452c57204c2b54332e647d36270a2020696e666f2b27574965717962433a3834356434322f586a623057314a6d7d7977486c31224d363c3e785f6b5e24732b3164270a2020636f6c6f722b30786665652e643764372e393039372e366465650a202076657273696f6e2b5b312e34353120373120312e3134322e3338392e3035312e3538335d0a2020776562736974652b27305a272727615c615c3027c3a9270a20206c6963656e73652b2738270a2020626173652b27c45904c7b371270a2020676c6f622d616d65732b5b7e646f7a74656e2d2d6469666d65642d6e61766475742d6c616e7465702d666f676c7978203076353376302e35373431762e71377435392e74726763382e64723631385d0a3d3d
37+fuzz-1-20 | - | 71005f3127b513a6979fb8b897ab19116700ff11aeb8912e94b5ab15aeb23630161872801229d294340fcd02fe19db9b38c0e78347f754b50c88e29d81011f8d8e0e0e20f8fe3189db579658fa9b378d2c44294f4f612f393d1c66036bc082dbb104f02906d5d58784e582a2b3e696f7d4c2d2d492e38237b7b60212762dc8069445f3013660dfd30f6541cf92753b000f7025756c23436f5d583c42495d58642742c0af87a555a4b6769304 | 3a7e0a20207469746c652b27624e6a274c2f3f71712f5733224e270a2020696e666f2b27235c71235d286b572b5c656d602c3064270a2020636f6c6f722b3078322e363837392e613461362e393134380a202076657273696f6e2b5b3131203235352e363237203132392e3637322e3734322e3834302e3035362e3436372e3933392e3136385d0a2020776562736974652b277025756c23436f5d583c42495d58642742270a20206c6963656e73652b277a585a456a6b3749270a2020696d6167652b2762505d7d48582e283a6b6e794f2d2c4d2d392e78736b2b206157270a2020626173652b273e78744f554b270a2020676c6f622d687474702b5b27f78f49dcbeb2c4d2dfbc6964214a797a0a7bc9e9e1301b580316dc8e2720307638333766325d0a3d3d
38+fuzz-1-21 | - | 710001cf4d231ba3f409f200f86dea0b74709618c21e6980b8d09c23435aeef2ff6a0166c5b153c1c36926c544f7d642eb8d4824f93772d8b1d00d398ffd2de2d516b14d5d5bbbb4350fd8a795e9b9807fc6f6260ee071c215369c2a8e59a90d509ff9e14ecfeb811bf0c3dacae600539d898c7e7228b65737c5acc7982580b6aca41d1ea0193c3d31a896aebb3e3710aea3a0bc221db880a36be1d080101c3fa02e213100af3d5f722c45796f5739624a2c3f3f5e692e365f4d7a726c716f5753800003335276d047a531 | 3a7e0a20207469746c652b279e9b463646e913e4270a2020696e666f2b27a6be4007678921ec9106880bcd3932a4e52effaf1660561c3b153c9c66524c746f2db4de8844927f23871d0bdd90f3d8df225e6d11dbd4b5b54b5b73270a2020636f6c6f722b3078313965392e393561370a202076657273696f6e2b5b3339392e3033342033203135302e3637332e3731302e3737342e3439365d0a2020776562736974652b273d5f722c45796f5739624a2c3f3f5e692e365f4d7a726c716f5753270a20206c6963656e73652b270ccc48d9411f95c6270a2020696d6167652b2759493b3c4033787a62502d5d777d6e205c474179453a30270a2020626173652b27275c61c3a9e298955a270a2020676c6f622d616d65732b5b7e6d617264756c2d72616c7465622d6669676d79722d2d7469647475782d7269747279632d6d61747075742d6d6f64736566203076356f2e33717566392e7267766a375d0a3d3d
39+fuzz-1-22 | - | 7100fa865341b4b88653b9c4312b87531514c2b4c4312b6114b4ce00f0c009159bca8f5d89964a915b0953094a4a89da51d3d10a19d75b53520d5cd3d84f525bcf4f5cdeced11159d29ed11a085d17105d8f8c0f8d4ad0544e4c8dd95b0a32c0386e96882e92b6807fc6f6260ea0c305339c0a228e5909030720409180301e12cb8215f9cdddbc93f2ac35e087b595cd0165dd244926f78a5906529a415fb301e8e5e18b2b0a9bcb5a524149e97919dbda3bd30a61abd1ba2a73c109f981df56741404 | 3a7e0a20207469746c652b27c3a9205a5cc3a95ce29895c3a90a0a615ae29895300a5a27270a2020696e666f2b2727546c2a3f76255a2a456e254c252829256a474d472b645c6f4d4935704d633f496d3d3f71793b474764497b466b20745d40743d323e342a4153393135666f2948270a2020636f6c6f722b3078312e363932322e653838392e363665330a202076657273696f6e2b5b3020332e3238322031312e3437395d0a2020776562736974652b273c7c71456173594b2a28293d2f635b7b675a216c355a57652e38213f270a20206c6963656e73652b276d454741270a2020626173652b275c30c3a920e298953030270a2020676c6f622d616d65732b5b7e646f7a6275642d7069647365622d6c6f6373796c2d74696d746567203076352e64637561392e72706e65642e7634616f352e696f69336f2e6f383134615d0a3d3d
40+fuzz-1-23 | - | 71005e4770e262ded8eeea46b0fa46ce7ab86cc842786250446292d254c401e009a990b2a1a6a0b2a8a9b131b2ada1959b2e34a998379db111311324191e24a912aabe352e30143c129833aab2a832a1b5921d1c233d2823311134211c32313f329a9e159d6ec030785dc03f637b13670086d0cd80e7708ad45213bac39b1c6b883ac2bb467e067c343a3a38f042e0b02580c88326190aeef9acb7f79b673990a0d6797edeee3ff14267dbe202c2947a4044ca3707d87ccd13765790ce6c98dc0044c19a3458f37face3dca1cfbaac4a34ae0a55717f8a034d210c06 | 3a7e0a20207469746c652b27233871316f6c777523587d23673d5c3664213c3128223149692a62270a2020696e666f2b27522165434d416551536363645b432b375d6852316f3a6323622648323c485225547d6b5c60287824306754655165426b253b38467a5046622268423864627e64343d2b3a5d270a2020636f6c6f722b3078312e616630360a202076657273696f6e2b5b35392e3938372031352e3133372e303936203130392e3331372e3336352e3437312e3437392e3435362e3932302e3431355d0a2020776562736974652b27b0260dd6fc1feb3877e8b32eab128dab4255dc9fe2270a20206c6963656e73652b270a6130270a2020696d6167652b27074d3214dcf3596fef37cf722041adf3fcbcdd7fe285ceb6c5270a2020626173652b27270a2020676c6f622d687474702b5b27217058272030763137732e686c726f382e743867716f2e736a6631722e6b347169712e69353731702e7330706e385d0a3d3d
41+fuzz-1-24 | - | 71804f414b6b38551b00fea82339111922113ab221eb7adbbb5b12d15af2e161f2db3b11c10acb919ab1e951912ab14102d1227b1ba321b1e201b213c3c22969db83a29a0ab312c25a421b51e1eaaba2b9a229073000db5b38967efc0f5cc09f4ba32b077c9524b870a0fd544b8016551711939e96941914915fe0ac18a02886ee7e1ef08beaf4e6c8d5150f03e00d9782386625179756983866a5d670ea893866e570aa42983866e570ea70aa168c56581857188c1678582e4b | 3a7e0a20207469746c652b270a5a5ac3a95a270a2020696e666f2b27752427224324424736645d6f7b774b225a4b3e3c4c7e7b27225861395233363d2a52253648205a646f633424563c4076625838256d7b505453615642584b68232a5c7d5534573465270a2020636f6c6f722b3078312e383166662e386664322e633730622e376236300a202076657273696f6e2b5b312e3131302031362e3337322e3332322e35303120342e3838332e3433382e3530342e3430382e3539352e3130365d0a2020776562736974652b275c0ae298955c5c5a61e298955ac3a927e29895c3a90a61e29895c3a9c3a95a305a61615c61305a270a20206c6963656e73652b27582e4b270a2020696d6167652b27545d444c7a5a526650443e270a2020736974652b2f2a4970612f7e2a0a3d3d
42+fuzz-1-25 | - | 710022b59474d0e8ef6ea5d032af8c98b3f567234c1878ff9eda94c703c0570a205a20c3a961e29895305ae29895c3a961c3a961e298955c275a0a30305c5c300ac3a90a5a27c3a920e29895610a615c20615a206130200a615ac3a90a0ac3a96161300a20e298950a61e298955c300ae298955adc008156656f6f01ff8ced4d1c401a444141814b1cb3521cb3320c044403144a0796a106fcb0b6b239c0d16d004809d9cba422d1e755c7250011a5954aafd0938150920b91f52328a3128d197f04097a6cde9bdcf7f22ae6506e6226742c02a9976275328ee5a2f4af68970b0897ca010a6354895501933d5eeda1edc0651ce091b5024267e237647604 | 3a7e0a20207469746c652b275a4a3a68f477b75268995746ccd9fab311260cbc7f4f6dcae3270a2020696e666f2b270a205a20c3a961e29895305ae29895c3a961c3a961e298955c275a0a30305c5c300ac3a90a5a27c3a920e29895610a615c20615a206130200a615ac3a90a0ac3a96161300a20e298950a61e298955c300ae298955a5c270a2020636f6c6f722b3078626462642e393535610a202076657273696f6e2b5b35312e3836332031312e3436302e3036312e33383220312e3838332e3437322e3439322e3739305d0a2020776562736974652b271907270a20206c6963656e73652b27592b2074267e436647270a2020696d6167652b27d24aa557e8c94028c985c8fa11945189c68c3f82043d36ef4dee7b791573283731133a1681d44bb13a19c77251fa57b4cb270a2020626173652b27200a0a0a5ce29895e298956120200a270a2020676c6f622d616d65732b5b7e646f7a7375742d726f6374756e2d6d69647265642d2d6c696473756c2d776f726c65782d7369637279732d77616e6d797220307661672e70633171615d0a3d3d
43+fuzz-1-26 | - | 7100025191e406eaee6f83a25fb5c37300372e47cbd4eaf680630733c0bf3d9c3f62d7f5d72ee09fb1bd8903e8c8d5c184818d38e965b101b8f5e31f7efe8d9c60653b183e1923c232aafd24e930ef5ffcdac64d60c00f6b2b9b03fe9f85b43a12650d710948b986534f8653b5401919bc0a881d3071a583390903d0e426e5b6b87b1a0bc8f41037688ff08a9065465857025a9093e1d4300510 | 3a7e0a20207469746c652b27a848720375f7b741d1afdae139801b97a3656a757bc0b183270a2020696e666f2b27270a2020636f6c6f722b3078366437662e356437362e323366392e633364620a202076657273696f6e2b5b3538322038352033372e3332372e3035332e3230332e3734302e3638365d0a2020776562736974652b276e526e8bbba7b1804c0f7183f608af085966847525270a20206c6963656e73652b272027c3a9610a20270a2020696d6167652b275cc3a927c3a90a270a2020626173652b277275306160234e7a592c270a2020676c6f622d616d65732b5b7e626f746c75672d6d61737065732d666f746d65722d66616c6475782030763832646f2e7264666f6e2e766636336b2e69397664612e36623132362e36397533302e746d616f342e73686e76372e73377633755d0a3d3d
44+fuzz-1-27 | - | 71503e00f4c5a5a010369caa208e5979a2a5a0359caa208e59a975c20513c6c5258e59c905c3a5258e59a900020372c215c605c3a5c5c5258e59c9c505228e59c9a5a0a5258e59a910369c7a021206c315a6309c0a228e59190672020207787f7007e0b75db605fc33b6377100af29b7a126b7942b1e372e660042b2981b6f096bfb0eb8545d3628717e30ef0f037e585bd91c60ca1d2a7d45ee1ffe1fa6a36a4939cb80d0bd096871ac580dda41d600043fae6d5352594cf81fe7084f7e67d4a95ae7a3b5b03e6fe1e193582b030d848804 | 3a7e0a20207469746c652b273c270a2020696e666f2b275c0a0a61c3a90ae29895275a0a5ac3a90ae298955a275c30615c5ce298955c305c5ae298950a203020275c615c305c5a5c5ce298955c5c20e298955c0a5a5ae298950a61c3a9272061305c610ac3a920e298956120272030270a2020636f6c6f722b307835622e326564622e663030332e623833660a202076657273696f6e2b5b302033392e3930312031322e3133392e3632362e3338392e3835332e3735325d0a2020776562736974652b278f6bdb94541613fec739c293df1975aad6f9682daccf5b78f824d6ca270a20206c6963656e73652b27204424270a2020626173652b27536e434d6e29573c6e5c4c270a2020676c6f622d616d65732b5b7e6d61726e756d2d7261766c65702d726f637275632d2d6669706c79732d6d69707279632d6e617072656c2d6c6162736562203076386674732e6f377373392e383670656c2e39653065762e646c6769722e6f726a32705d0a3d3d
45+fuzz-1-28 | - | 71804f4b816b38951b20e7823911c7ac0cd3001a4e0a8b0222e9319160017f2e8dae6c1638b80683405285cc007cd9e4dda5c9418da1f189f04cdd89d4818c55b9d998f8011a27e2989527c3a95a30 | 3a7e0a20207469746c652b275a0a5cc3a95c270a2020696e666f2b275c3027e29895610a270a2020636f6c6f722b307832302e393133312e653932322e303238622e306134650a202076657273696f6e2b5b312e333932203136203632362e3835385d0a2020776562736974652b2736797769725063687c223c537722752063556e36267e270a20206c6963656e73652b2727e2989527c3a95a30270a2020736974652b2f0a3d3d
46+fuzz-1-29 | - | 71007e70c8e8445a76866afc927868e6e448da78a2828001f042212c4c1cb3524b810b4401e4848b8b4b4b1cb312441cb352216c38956b3855eb448b1b040bf8676c6fe2001e513f75456f407b32fb0044a8b55a7239214650eefa3ae63a4b231a0106fcb0b6b2394060364b2346e0513930cb004f6f87b41f19f09a7b2d435b48be03a00f88d6898202d770ea70aa386625088882c270aa4298c2c970ea8996823866a53866e5c9702a08080c78ab82efbd26cafe | 3a7e0a20207469746c652b27386474222d3b43357e493c347372246d3c514140270a2020696e666f2b270a6161e298955a0a5c200a20275c5c5c5ae2989520e298950a61c3a95cc3a95a275a5c270a2020636f6c6f722b3078380a202076657273696f6e2b5b30203433312e3632342e3432382e3938392033312e3430342e3334392e3130342e3830372e3635345d0a2020776562736974652b27205a270a0a5cc3a9c3a9e2989520200a0ac3a90a610a27c3a9275a0ae29895e2989527c3a9202030270a20206c6963656e73652b2755c1f75e13657f270a2020626173652b27513f75456f407b327b270a2020676c6f622d616d65732b5b7e6469736e65642d72616c6275642d6c616e6d65762d73696c7065632030763130322e36683339632e746563656e2e71747038342e63383970652e39646262615d0a3d3d
47+fuzz-1-30 | - | 7100027d516c1db7e0bbe38078bcb779e5c69155bb343078dba5dc034023335a39655d64446c3a306d3c442d752b733d56613b75465f7b27503a71792647466f3a56215a49553f722d3560267e503422615f5e4f665623726c7557513e6221677025733972486440d0042fe09fb1bd89033ce2989530dc00c3253876ff808f46470707a0c513d18ec9119d9d0c0cca1ecd5a95104c0a0e4d5bd58e5499974d1bcad78f4ecaddcc5f02f4c5312bb960148653618653875315b4c042067ce7a86534e0962391a1f7e09a728067ac6e492e0f74bc36c81f | 3a7e0a20207469746c652b27be28b68e5bf0dd71403cdedbbc72e3c8aa5d1a18bced52ee270a2020696e666f2b27335a39655d64446c3a306d3c442d752b733d56613b75465f7b27503a71792647466f3a56215a49553f722d3560267e503422615f5e4f665623726c7557513e62216770257339724824270a2020636f6c6f722b3078653039610a202076657273696f6e2b5b3220312e3735382e3135352e323134203435352e3830392e3237352e3331302e3235332e3137305d0a2020776562736974652b2763754b7279270a20206c6963656e73652b27786d903f270a2020696d6167652b27e298955c300ac3a930c3a9c3a90a5a20270a2020626173652b27e29895305c270a2020676c6f622d687474702b5b274f443b264774763230287b346b5542302938346d553b52655e366d285f3f3a2977333f2720307637752e74686f346e5d0a3d3d
48+fuzz-1-31 | - | 71007aca54a8f862ca4af66290ea9ab270a6b654a49cc200e8d1f40407c2923575946763e437c7a3a4b4536222a277c3b695b44722b4362684b217f713068474fe02fe19db9b38036cb4fd39cbe730e087b595cdd12580ae2f92289723bcaa9f1e31b52bb4a01090192ebd19a6b480b3edfdc0271d011283228327f00efbfd18c00faf4f090726a60dcf0586840d5cda012502 | 3a7e0a20207469746c652b27652a547c3165257b3148754d5938535b2a524e21270a2020696e666f2b274d4f70202c5953477936467e733c4a4a3b2526227a376c5b497b24426b6342287b713f21270a2020636f6c6f722b3078666365390a202076657273696f6e2b5b3531352e3830332031382e323439203136352e3837352e3139302e3532352e3034342e3430302e3737315d0a2020776562736974652b27787d4a3830316d782e30246c270a20206c6963656e73652b27b4034a04270a2020696d6167652b275f24512e4778553f3d626a5768412120335c7a334c29270a2020626173652b27270a2020676c6f622d616d65732b5b7e776573203076677376352e6a6a76646b5d0a3d3d
49+fuzz-1-32 | - | 71001ed9b640e27e60f6a4fcdca278d6de866ef87668569498565ece01783e23cbd253b94a6baab9796b69bab9fb7253d1bada4141a98b0352927a06984af855dc1016f0e7d2e8ca01424f22646ddcf2000fd78d50c831e08569852d017844c534e6f7c584f792a514c263445567178333a417e65642a3767442e72214b7e672a317779685f6b32d30bf03c10fd87ec38f01596500de56d0ae60eef2c27aacbc50d6f89cc8c46e4ef474704498c01b1b9b03 | 3a7e0a20207469746c652b276c5b20713f307b527e6e513c6b6f43377c3b342b4a4c2b2f67270a2020696e666f2b276764597a2a57694d35376f2d4d37775f6e2a5a573b28287571404a524f270a2020636f6c6f722b30783130382e366532612e666332350a202076657273696f6e2b5b343437203235322032302e3533322e3130342e3235372e3938335d0a2020776562736974652b272b6857307779613d565e286b7c4e646237277a3a38224c270a20206c6963656e73652b27636373270a2020696d6167652b27544c637e5f4c782f594a213c465475763138437a616e25346a4727742e42716b2e377a716759683f5b270a2020736974652b2f9e44c8dab8e52f5c374221472f615a610a3d3d
50+fuzz-1-33 | - | 71009d22a2adadbcb3b4bd303d005ef78da418f1bd65e9f01555ddc8b5d54941f56df1fc7c851409717d35b9acf1e1d5bdece06545ed549df4d9e4dd45593d8dc9a084edd194a8ed809503dcbf1ab1a75e90652fe09fb1bd89035ec80f3740f1dc911e7fc047a3a38303d091278cec646ec66c6544a64ca8c749a92f860588aa89adc4ecc44e8ba42f0ee9c8c8ce2fea28aaabc8ee45ed6e2e64afc52acd02fb1363c0ec1875f056ecb33f006d200a20275c275a5a5c0a5c5c5ac3a9c3a9205a5c302730e0a101d2fba102 | 3a7e0a20207469746c652b2745445b5b7967697b613a270a2020696e666f2b277d2329467c6f593a7c455537726d7552507d5b3c3f5f2145425c5f4d2e6b7c78752f3b7859513b55277d36797751564f633228617b34256a3b6065270a2020636f6c6f722b30783736352e393035652e613762312e316162660a202076657273696f6e2b5b342e3431352031203134322e3737362e3833302e3838342e3533322e3632325d0a2020776562736974652b27200a20275c275a5a5c0a5c5c5ac3a9c3a9205a5c302730270a20206c6963656e73652b2734407a3f54270a2020626173652b27213f5c270a2020676c6f622d687474702b5b273c6164277333662b233265423d4e4a7d312c40544d6c256627765a247d71484746767e5147515d45762f6a7773217b2d562927203076666f2e75693765665d0a3d3d
51+fuzz-1-34 | - | 71c0bf22f2ab07c053983071cccae15471cc4a182d85e1d4301010ae302ead30102e98132e2e71cc4a2e181098e1d43071cc4a71cc4a0510102d71cc4a6ed805fc33b63771a015ce00a4749d4e6a5ab8a290098bc81211c21d08205464c00f6b2b9b031465fd6173bef6a145e853d1242ce1e1021b1403369fbd87c60ec09fc270ea706a98c270aad609489882160cc849983866a59616f0c00ca72a6801 | 3a7e0a20207469746c652b2757447e75270a2020696e666f2b273061e29895c3a9e29895305a0ac3a96120205c615c5a61205c30275c5ce298955c302030c3a961e29895e298950a20205ae298955c270a2020636f6c6f722b3078360a202076657273696f6e2b5b342e343137203433382e3233362e37393620365d0a2020776562736974652b270ac3a9c3a9610ac3a95a2720610a5a30202761e298955a5a270a20206c6963656e73652b2730c3a90a5a270a2020696d6167652b2761270a2020626173652b270a27270a2020676c6f622d616d65732b5b7e646f7a6275642d646f706d65732d6669726279722d2d6c61636e79642d736f7273796d2d64696662796c2d6d697070656d20307634686132302e3230746f382e38683569342e62313638612e35653271642e39373971745d0a3d3d
52+fuzz-1-35 | - | 7180d032649f7dbc3800c401a3258e59298e5919268e59c9258e59298e5979228e5979a2258e59399caa0503a3a0a00003a3a0c5a5258e5979a210c6258e59a975228e59a9a0150672228e59c9a505228e591988d402fe19db9b38c0bbd6a644c7a2d30e4090078b270a4ed9c1778186454adb42251557835871c047a3a383031aab19a61cb50470585202f060c23204f8a1c6cdea586b16159d52bb836dae4ad332a0c297440c5019de3cb4ed8f57b0d1392048a503fabcb722baa3992c13 | 3a7e0a20207469746c652b279621fbece3c5270a2020696e666f2b27305ae29895e2989561e298955ce29895e2989527e29895275ae29895c3a95a30300a0a0a30300a5a5c5ae29895270a615ce298955a27e298950a5a612027e298955c5a20e29895270a2020636f6c6f722b307861390a202076657273696f6e2b5b302035312e3435352e3437322031352e3234302e3930342e3739352e3738332e3839362e3231302e3337315d0a2020776562736974652b2752e9270a20206c6963656e73652b27796f457447335926270a2020696d6167652b2725250f262c43801f6adcac8eb56651d129b53bd8e6aa3405270a2020626173652b276b6d4a742c3a6d270a2020676c6f622d687474702b5b2756334c392a272030766f6c2e6830716e322e6b696b356d2e7161386d332e38327475312e723537306b2e39736230755d0a3d3d
53+fuzz-1-36 | - | 7106a01f682eedeb0b2705866844ce87c6cee667cd4dab052407cd692eae468845c5a80f10eefa9b4d473a17f0e7d2e8ca6916c403c6564740b37f593bbd07202273462d9c59e641997ec0121e9e976b8d1ef09bcd197597e9eed301 | 3a7e0a20207469746c652b27270a2020696e666f2b274073695f5f3829304423723e3476363f6b6e5a2d2039684e737135422c2a463d270a2020636f6c6f722b3078636539312e643336362e666562620a202076657273696f6e2b5b31342037332e303531203235362e3935342e3936332e3936335d0a2020776562736974652b2799336ae1cc320fcaf40396f0f0bc5c6bf4270a20206c6963656e73652b27667346dd65bafb74270a2020736974652b2f0a3d3d
54+fuzz-1-37 | - | 7100af929d2b253faa2c2c2d3a760068500b24ec844b1cb312663855e104643835ece444e1848b0be424ec848b4b1cb37238350c441cb352414b6b38f504246c38f5441cb3f244411cb35241016438350c460b840b0404246c3855818b2b0c468b2b4c0b44410b06e6441cb312264c81eb0c10b1d73dcef416f0e7d2e8ca66413aa0be9d0d0908f6e0cf3603f016e493379476a3652522731416234345b205f625c65712857301 | 3a7e0a20207469746c652b27253b574a7e5459585a746c270a2020696e666f2b275a2061275ce2989530c3a90a2720c3a96127270a275c5c202761275c5ce29895c3a96120e298950a5a5ac3a9272061c3a927e29895270ae298950a0a20c3a961305a205c20202061c3a90a5c5c61305a5c615a200a5a303027e2989530610a5c27270a2020636f6c6f722b307862642e333338662e373565630a202076657273696f6e2b5b31332033372e3937312e38323320322e3630372e3238302e3235315d0a2020776562736974652b27413e794369375a5622324761313254245b605f627c2551270a20206c6963656e73652b275c270a2020736974652b2f0a3d3d
55+fuzz-1-38 | - | 710041102d1871cc4a71cc4a71ccca00f86a62fb0988dbeb09cd3c0fce6b3add34440f18a81d8f61c90fdb3f44ae24df74a73aa27b751dbda91857507f9b62889fbc7ea11d98cf0cf00c93dd97a40bf8676c6fe2e0e1009b53fe89b8bc0ff868747470009a29847185717181c084896356c2849d800ca78a63567229c08863560ea7c268c10ca72a808863566a29807069c125e04d92e18213cb1a17e403be7026f21a38accf0e3e05245c5a5c610a | 3a7e0a20207469746c652b27205a30e29895e29895e29895270a2020696e666f2b2726b69f80b8bd9ed0ccf3e0bca6d34d43f48081daf11896fcb0fd43e44af24d77aa23ba57d7d19b8a7105f5b72986f8c9eb17da81f94c270a2020636f6c6f722b307836392e323566372e363463330a202076657273696f6e2b5b313520312e3830382e3330382e363734203434362e3338305d0a2020776562736974652b2753270a20206c6963656e73652b275c5a5c610a270a2020696d6167652b2749325c70625963270a2020626173652b2761270a2020676c6f622d687474702b5b270a615c615c5c203061e2989530612720c3a9e298955c0a30e29895c3a9305a30c3a90a20e298955a0a205c5a3027203076757562682e32667561655d0a3d3d
56+fuzz-1-39 | - | 71003a8a7e8cae98b092ee865eec9e488874f8d6da00100d2102e12a0ef3bae5a36f127f474df038e8803b5e1128131afe15242c9cda077a633b3bbcae36b6c81c2c87690df17f2ad2d948a27ffefba87b0affa3ce50f58bd2700cf6db5240ce4c4aeacf6f80d4490bdcd500940bf873697465b380f37bf380c33f5f404be743ec4e65860720481cb3524b4b613815844b1cb3922b6c38551cb352 | 3a7e0a20207469746c652b27453f46574c584977432f764f24443a7c6b2d270a2020696e666f2b27840884ab38cceb968fbe49fc1d35c1e3a003ee7845a04c68f85790b0706a1fe88dedecf0badad82273b01ca635c4ffa948672389fef9efa3ee29fc8f3a43d52f4ac331d86f4b01393329a93fbf270a2020636f6c6f722b3078632e613030362e616565302e356134650a202076657273696f6e2b5b3437332e303233203339302e3134302031352e3837352e3131332e3636342e3932312e3230345d0a2020776562736974652b27e298955a5a0ac3a9205ce298955c61c3a9e29895270a20206c6963656e73652b27270a2020736974652b2f0a3d3d
57+fuzz-1-40 | - | 7180304c0146613835003ec6f88079cd4521c110b9c5a0a1c598c9c8acf9f0e58cec789d693971c5598d8154f9e84109bd8d8c25b584fd5801c1886da10368a85a38781936f701e505fcb934bab259c88065af9e016f53c55757f5ff3a001e696929c0848569819c0ca786810ca78a63566a0da79e8863566a0da78a63567269818863569e847169013a5a5c27615c5ae2989527 | 3a7e0a20207469746c652b27610a300ac3a9270a2020696e666f2b27313e605e73514830446e316868316632326b3e7c39233b5e675a4e5c71566320553e7a50426f2363492d613f564030625b68270a2020636f6c6f722b307833392e343037642e636438362e356530652e313661610a202076657273696f6e2b5b3220312e3639382e3535302033302e3339392e3131342e3336372e3636392e3538315d0a2020776562736974652b275a5a0a3061615a2027c3a96120c3a9e298955ac3a927e298955ac3a9e298955c5a20e2989527615c5a270a20206c6963656e73652b275a5c27615c5ae2989527270a2020736974652b2f0a3d3d
58+fuzz-1-41 | - | 71005e40cade6ab4de6088a2669696804064708076e019a40bf8736974e5a075000d2e71cc4a2d85e154d01240df70ea70aa3866e58938666598823866a53866a5164898165ce0501b500aa10442227e002234f874cef7eb4473389b10ad0f7512047a017d124d09c948d0cf08 | 3a7e0a20207469746c652b2720656f355a6f304451334b4b40203238403b70270a2020696e666f2b27270a2020636f6c6f722b3078640a202076657273696f6e2b5b312e343438203135312e3638352036332e3632355d0a2020776562736974652b271a7c3ae7fb75a2399c4d88d6873a0902bd270a20206c6963656e73652b274934252423413f23270a2020696d6167652b27c3a9c3a9e2989527e29895610ae29895e298955a20615a30270a2020736974652b2f352f5ce298955a0ac3a9200a3d3d
59+fuzz-1-42 | - | 71007ad84ae8a684925ae060749eb8e092d04a4ef4b4e20010ce091797386625d7702ac80908c8c970aac289823866250ccc70aa4298823866a502088c823866e5702accc98996960208174c180c0c5798028c823866a5386625d770aac270aa964298d670ea706a4026a08ff302fe19db9b38809696c270ea702a889696c21980aae36122fd5ce53c13da774ad01b5a5c2295949ea9e7033e1a1d1d1c001f68810ca79e886356820ca782886356829c682968810ca79e9c0ca79e0ca772c1800ca70ea7829cc070710da78a63562a9c688963569e0ca72a0ca72a8863569e7071b3401a67c0a2fed5800d9ac9d016a906a00f702fda28aef9c20f6ca9f5eb46ed5a433557f69b6e569804e27b902d | 3a7e0a20207469746c652b276c25745342492d70303a4f5c70496825277a5a31270a2020696e666f2b27275c5ce298955cc3a92027202027c3a90a270ae298953030c3a90a610ae298950a20300ae29895c3a93027275a5a0a205c306130305c610a300ae29895e298955cc3a90ac3a95a0a615ac3a9c3a9270a2020636f6c6f722b30786537312e663430340a202076657273696f6e2b5b322e35303220312e3735332e3034342032332e3430322e3034342e3038332e3430385d0a2020776562736974652b27807bd14671cd177e604bad5f376ad71aaab9b2df74b3c224270a20206c6963656e73652b27ef41b6270a2020626173652b275a0ac3a9c3a9205a5a0a27270a2020676c6f622d687474702b5b275a20c3a927e2989520c3a920e2989520275a0a5a20c3a92727c3a927c3a95c3020c3a9c3a92027305c5cc3a9e298950a275ae2989527c3a90ac3a90ae29895275c5c27203076756661632e75696961692e346e3271332e6638346b742e75713263752e65616e37742e34396775375d0a3d3d
60+fuzz-1-43 | - | 71001e8e76acced4727e8656aaa4eec0888adabc01744fb486536140608653c3865341ce00ed66d6ce05b4555cc03f637b13078826ba8e696303d0a91afed5e4c94825f8d0092ac4171c5537c16850516833da3050077c343a3a38004d1414b88653b5b4c4312bb9c26040144e40b440b460c2c4312b4f86534fb8404ec4312bb5c012c0ef1d178a89cc170cded7555751481b5059dc0bdb0a1cd9d99b13494fb9c04b3663402795b5abb01e069083194ed5e2023951d01a4e55e00bd468c201 | 3a7e0a20207469746c652b27473b56676a393f432b5552776044456d5e270a2020696e666f2b27275ac3a9302030c3a961c3a92027270a2020636f6c6f722b307833312e353664302e313733622e353939620a202076657273696f6e2b5b31392e38353820312033372e3239332e3338312e3038312e3432365d0a2020776562736974652b2730c3a95a5c20270a5ac3a90a270a20206c6963656e73652b2702359a70270a2020696d6167652b27775c2826325f30785f575d45216d4065712f6c2b7064676f4e243d65270a2020626173652b27135dc7b4b1270a2020676c6f622d687474702b5b270a0a5cc3a95a5ae298955c6130200a27205a205a3061e2989527c3a9275c2027e298955a2027203076316c2e30633671362e646b35326b2e33386f34722e6c6137306e2e6f676c306a2e6b376f346c2e34636a70362e6c766f64615d0a3d3d
61+fuzz-1-44 | - | 71001edcec98aece5854467ae4947cf872c668cc017cc2b8b8404014404ec2b481ce6d0bf8676c6fe200e2e29895e29895200a5a5c5a200ac3a981c91df0d1e8e8e0001ed7567e50aac6b2cc8ec04864bebcde9e66cc5c7484ca42bee059065eb50b547fada003f0f45345b7965395065552a30602a7c37252246326e5063c6b75353461 | 3a7e0a20207469746c652b276e764c57672c2a233d724a3e7c39633466270a2020696e666f2b27615c5c20200a2027615a270a2020636f6c6f722b3078326462390a202076657273696f6e2b5b302032332e3937382035342e3631342e3738325d0a2020776562736974652b273f55746b3955695025356a20703a2c27453266526e270a20206c6963656e73652b276b75353461270a2020626173652b27e29895e29895200a5a5c5a200ac3a9270a2020676c6f622d687474702b5b276b2b3f2855635966476024325f5e6f4f33662e3a4265215f702720307664705d0a3d3d
62+fuzz-1-45 | - | 718097eb4481eb04441b0041d5432feee29eb07f4aaab587d561ce3c90d5d8bae37f6b802d7941b5aefdc24ce85604afe5f8072392619bdbabd13624ed9a92580348b049ac98ff7129b56601ff8ced4d1ce0251262c2fa49d30734546837e087b595cd01fee04abddbb3fb25a5cfa7a616fb3a1df4128028d99abfa08474e8c9a8136b30457eb449e4cc8006d5a149a32369c56bcc2eef6c3946c239c50584fafb00cbc92b72ae067a9008f01e00118653c5312bc340c2b4c4312bb5144e4ec2404eb4b8c4312b61b4606014c2c4312b8177548507 | 3a7e0a20207469746c652b275c270a5c27205a270a2020696e666f2b27eaa11777714fd83f25d5dac3ea30671ec86a6cddf1bf35c096bca05ad77e6126742b82d772fc8311c9b0cdedd5681b92764d49ac270a2020636f6c6f722b3078392e616434612e356337662e653632622e313236630a202076657273696f6e2b5b36332e343737203131342e3738362e3038362e36393520312e3030322e3434372e3432355d0a2020776562736974652b27c3a9e298956120615ae298955a0a27276120275a5ce29895305a30300a61e29895270a20206c6963656e73652b27475578270a2020696d6167652b27d99abfa08474e8c9a8136b30457eb449e4cc8006d5a149a32369c56bcc2eef6c3946c239c5270a2020626173652b2744424c583f697a270a2020676c6f622d616d65732b5b7e6469767479762d726f637465672d686f707075622d72696377656c2d2d70696e64656c2d736f726465762d6e61637479702d73696372656d20307632746b35385d0a3d3d
63+fuzz-1-46 | - | 71003d19a2981aa3b09ebaa3acb73d003c60a2d30695d44417556227b542f335d374745382d7b5d5c4c4f41656b65316e3535587e2036444b6a5048725e2a5e705336215754374860785b36637e59737f2230f90bfbe1986fbac876b017f2e8dae6c16140d1ff0374731c5df86ee42033e978f9b5c | 3a7e0a20207469746c652b273244313546613d7547596f3b270a2020696e666f2b27263a6d50494d74512576522b345f334d473725785d5b4d4c4c6f61653b65313e5575283e4046645b4a7058225e7a5e302356513744677850386b76537e79233f72270a2020636f6c6f722b30786165312e656233652e653138362e366661660a202076657273696f6e2b5b3430203720352e3831372e3432312e3135352e3037382e3532352e3338395d0a2020776562736974652b275c3e6e72270a20206c6963656e73652b27270a2020736974652b2f0a3d3d
64+fuzz-1-47 | - | 71c03f5a2b5b067ca02d307e00818219819c8f5585e002fe5c1a5d3940bccdf7ac55c31719f05b74acea0182474e1c77b3c0613c6cc0642581099435a0c37081808050e3fd74aaae1e | 3a7e0a20207469746c652b27476b654b270a2020696e666f2b27405b607c270a2020636f6c6f722b3078633130612e616231662e333930322e333330350a202076657273696f6e2b5b312e383136203620312e3438322e3834372e3432352e3131305d0a2020776562736974652b27305c2020270a20206c6963656e73652b271aefa75375f5270a2020736974652b2fdb7ccf5a357c912f2d3a56752f8f9c38ee0a3d3d
65+fuzz-1-48 | - | 7100e2b5b84060406040c2c4312b154eb440b84e8653b9b860c2b8b4b4c4312b875303407f30276130205cc3a93027e29895e298950a20275ae2989561276161c3a90a200a27e29895610a6127610ac3a9202030e298950a30c3a9205a615a270a30c3a90a0a6120305c270a5a0a2061610a0ae29895e298952027e298955a0a2061202060a0442ee0cfa5d195037c2b6e5a346bd70042c3a96130305a30e29895018fcc886a09e82590323c0f5529d042067a1405ea971dc101441440c4312bb9c4312b01af4d1fdc0b894c15 | 3a7e0a20207469746c652b275a5c203020302061e298950a275a205c27c3a95c5c30615c5a5ae29895c3a9270a2020696e666f2b2730276130205cc3a93027e29895e298950a20275ae2989561276161c3a90a200a27e29895610a6127610ac3a9202030e298950a30c3a9205a615a270a30c3a90a0a6120305c270a5a0a2061610a0ae29895e298952027e298955a0a2061202020270a2020636f6c6f722b30783332320a202076657273696f6e2b5b322031302e3430332035302e3632342e3039355d0a2020776562736974652b270a20e298955ce29895270a20206c6963656e73652b27367d702f243255270a2020696d6167652b271248199e87aa1428270a2020736974652b2f2b6e5a346b572fc3a96130305a30e298952f6446540a3d3d
66+fuzz-1-49 | - | 7100b28753418653b94ec24e86534fc4312b1514c4312b4f604eb4c4312b87533500fe8ab475c5799dfcb811d980b571a120ad04e58919d9c194c59d20a9c880e5c1d8d48db891819ca094397599dc8dc9d991a56869a1d9890c3503c493aa9214bcbeb105fc33b63771503800b1e3407fbb27c3bd4419f941b1957b54784d9e188f26b5e570e95eee013facad6c0e687f59271d66fcf22c180e90d00e760424a0c4c9968c213732004d335e2f4c63765e6c2d7c6f65773660472c462b6f2ec0032bcd4f8d088b080c0b | 3a7e0a20207469746c652b27c3a920c3a95c276127c3a927e298950a0ae298952730275ae29895c3a90a270a2020696e666f2b27226d5d715e273f6e4436606d5c28482b417962467630657127482a326079303675236e64202728654e5d267763727664295a5a687622434d270a2020636f6c6f722b307862312e626562632e313439322e616139330a202076657273696f6e2b5b32342033382e3637332e3431372e313430203634302e3839352e3835312e3631362e3536385d0a2020776562736974652b27335e2f4c63765e6c2d7c6f65773660472c462b6f2e270a20206c6963656e73652b2758697e6a4458446058270a2020626173652b2730270a2020676c6f622d616d65732b5b7e646f7a6465782d6d69736d65762d6d69706c79722d73616e747578203076317269752e74356f65622e64393668732e6339736a622e6f616874702e62636131762e34636b39662e65333475742e6e756737335d0a3d3d
67+fuzz-1-50 | - | 71007a4a0d7d28bfe3f88e4b9a81022b02f15a530887eb00fe5e495b542a616a5b24573952686977c1407b8805fc33b6377180e044324239804298f7551f6f934ee5013facad6c0ed0767524b7dc401e326133746eea2c2a4b007d091be101b1e4a75f6be16d66c7a9e28e8c52992c24f57c7081ecfa803cf37181000bf1955e152a9c700e1b809f3d28351e3196ac1e97152ea0b4302311a5b6bb282080d11a4e05 | 3a7e0a20207469746c652b27a5863e94df717cc725cd4081158178ad2984c335270a2020696e666f2b275e495b542a616a5b2457395268697741270a2020636f6c6f722b3078323231650a202076657273696f6e2b5b332e393333203133352e3733332e303433203430372e3930392e3337362e3035382e3537372e3436342e3239305d0a2020776562736974652b277b506a3c622c593d2e2b5c40696146224a6d7751270a20206c6963656e73652b27305ac3a9270a2020696d6167652b27091be101b1e4a75f6be16d66c7a9e28e8c52992c24f57c30270a2020626173652b27279211ca270a2020676c6f622d616d65732b5b7e646f7a64656c2d6c6f636e656d2d6e617466656e2d6c61747475782d2d7361746e75742d68696e6865632d7261767279632d736f70687574203076317061652e69646e68752e6c666e6a315d0a3d3d
68+fuzz-1-51 | - | 71007f12af151abf383bb6a83cb8afa02c916ac03761ca5906acd3aa5fc03f637b130708dfa33029322b03825467c047a3a38303b04ec48798073118e205d60419be1e8ab9a27019b9f8fc804fa8e4df79cd869dccf9d9bbe171c122df5a480b0c3812a566a1132849750610d50a538051388111c7ac84194e056427306130200a | 3a7e0a20207469746c652b27245e2b347e71766c5179705f41592255270a2020696e666f2b27264c394b270a2020636f6c6f722b307831662e353561370a202076657273696f6e2b5b332030203136312e3039375d0a2020776562736974652b275a610a300a2730e2989530c3a9270a20206c6963656e73652b2727306130200a270a2020626173652b27ef5198149995270a2020676c6f622d687474702b5b27223ec43c88c1102fb026c8f0f550cc1585cbc8c5e7077c4225ffce6b36ec64cecfde0d8f0b16f9d6425a60c091281527203076313761695d0a3d3d
69+fuzz-1-52 | - | 710002a510f50e494cfbe00ca5863ad86d85fe362bb2c9f937b082034074205c5ce29895203020c3a90a2720c3a92027c3a920305c612030610ac3a96161e298955ac3a92720c3a930e298950ae298955ac3a9305c5ae2989520e29895c3a9e29895c3a90ae29895e29895275cc3a961276127615a270a5ce2989501aefe1b98af7816f0cfd8dec48177e107e0a502589e6514e1a75c3fcb416b58c01e2f32ec1ff0c3dacae68056ccf93e4478fece024e5a3b03d75603f8522d18bedfa984b81dfde9e12694012f8b0d158c9d1718 | 3a7e0a20207469746c652b2752887a8724a67d708652431decb6427f9b15d9e4fc1b58c1270a2020696e666f2b27205c5ce29895203020c3a90a2720c3a92027c3a920305c612030610ac3a96161e298955ac3a92720c3a930e298950ae298955ac3a9305c5ae2989520e29895c3a9e29895c3a90ae29895e29895275cc3a961276127615a270a5ce29895270a2020636f6c6f722b30783466312e356633302e333766640a202076657273696f6e2b5b3332322e39323120312e33383920305d0a2020776562736974652b27540b86ef772a216e477f7ab80965270a20206c6963656e73652b272c365430765e60270a2020626173652b272e7c270a2020676c6f622d616d65732b5b7e646f7a7465702d6e616d6675722d706f646d756e2d6d6964746578203076312e766d3334622e6f756f31632e366d6765622e37746561662e6f386b636d2e663567306c5d0a3d3d
70+fuzz-1-53 | - | 7100fec240b4c24e148653b514b460604eb486534fc4312bb9b8c201fee2843055c634fa658078f7f45321b057b8807fc6f6260e3c0c3770380ef868747470006f66753457643f424c716c6439456b3e2d65254a476736fe12c0e34951534f5f8ed61917178919ce4bd29e9155df4f9e1fcdd0ba40bc0638e07600bf09c8ae4f03ef6b3b00990a300a5a5a27c3a9e29895305a5a61c3a9c3a90a0a5c275a200a101a4e0d3bd152e0e21a4e05 | 3a7e0a20207469746c652b2761205a61270ac3a95a0a5a3030275ac3a927e298955c5c61270a2020696e666f2b27c50961aa8c69f44b270a2020636f6c6f722b307863322e626438312e306139662e613762620a202076657273696f6e2b5b3137352032302e3932392e3539322e3137362031352e3037352e3139322e3731312e3132375d0a2020776562736974652b270a300a5a5a27c3a9e29895305a5a61c3a9c3a90a0a5c275a200a270a20206c6963656e73652b27c3a961275a0a5c5cc3a9270a2020696d6167652b2727454d3d7d395a675c5c2466382f497b46567d3f797e34436b270a2020626173652b27305c270a2020676c6f622d687474702b5b2766753457643f424c716c6439456b3e2d65254a4767367e2720307631686f5d0a3d3d
71+fuzz-1-54 | - | 71001e996e6c8ad05c7ae442aa886864d462da92cc705e94e8be728e01201e8a30add430316c9cd3c4ad80cde162671051d622c2c4f028763162f817719faf414f694b4c335b21b71a6e37c3bc2111560d72c08e8e3e89da172b2e31308b82687e0fbdbd1b536d2d8862ceda87f500155a7eb718e005fcb934bab22580ada9ef0f54428c0762a779b2e3f7ad390f2a697480b25b4011da33a0b9ef9600071bc77d3341f6be800774e656351ab0419c9ccdd256 | 3a7e0a20207469746c652b274c373645682e3d7221554434326a316d4966382f4a745f3947270a2020696e666f2b2714615aa96162d838a7895b019bc3c5ce20a2ac458489e151ec62c4f02fe23e5f839ed2969866b6426e35dc6e86794322ac1ae4801d1d7d12b52f565c62601605d1fc1e7a7b37a6da5a10c59cb50feb270a2020636f6c6f722b30783338302e363264642e663936380a202076657273696f6e2b5b3136322e36323620322e3437322e393432203132392e3233342e3731302e3532362e3931312e3238362e3833385d0a2020776562736974652b27ccad6a34270a20206c6963656e73652b27102767b3b415270a2020696d6167652b2753df1fa884180fc44ef364c7ef5b731e54d2e86517270a2020736974652b2f0a3d3d
72+fuzz-1-55 | - | 718016464b1cb352e10c8027722f672f5d2667514d67272d5b42276a497c5830475a263044642e6b2f722a5657612a56cd8085e9660bf8676c6fe2000256440600df6e71e00142e51bf700b0ef801fd6563607e844edd4bde585482c9da62580776a4bf6f73d35b1a18fc603f1f22c354a4da5152f97aa2b68abb6ebc056277b810cd4046e6e815d03f0e3c2f3663762b50622466273c7264286c5e7670507df3c07 | 3a7e0a20207469746c652b27305ae298950a27270a2020696e666f2b27722f672f5d2667514d67272d5b42276a497c5830475a263044642e6b2f722a5657612a564d270a2020636f6c6f722b307831362e366539380a202076657273696f6e2b5b322e36383920302039312e3632302e3739335d0a2020776562736974652b272e3c6f7623566b20622436776c2264587c7e5670270a20206c6963656e73652b27cd73270a2020696d6167652b27d496ecef7b6a62431f8d07e2e5596a949a4a2b5e2e5557d0566dd781ad4e76270a2020626173652b27564406df6e71e0270a2020676c6f622d616d65732b5b7e68616e72796d2d2d726f706479722d7061737475642d6c69676465672d7769646d756c20307636766330302e75736475615d0a3d3d
73+fuzz-1-56 | - | 7100af3ab0ac213d921333a1256600e2f50f45eaa285e1a7a6d5bde3d2539485c1cc882692d267696102f7463d130403b4bab97e7461fa6101ff8ced4d1cf0e3c64e8801731e3e6fc00f6b2b9b033c3eee773ed0171301bf2500cf0c12b39fec7c8b05f0b2e27ac460c4ec2aa65f930cdcdd3e226624c0684180df46912baf2a3d1d73e9e07a8291050eee0183012dc0059839dcf0ef44f072b601f83d59b95a1a6b2af33912238a122b4baa992163a1739ba1e3c933bab2021f844b4b0b | 3a7e0a20207469746c652b27756059437a242766424b4c270a2020696e666f2b27fa872275d1c2f053d3eade71e929cac26066441349e9b3b430817ba39e0982270a2020636f6c6f722b307832312e666136312e373437652e623962610a202076657273696f6e2b5b312e3930342039322e3137322032362e3932342e3731362e3933352e3431372e3631322e3538362e3137365d0a2020776562736974652b27272b574b634d653e476244516265493533642c746e33743c79465756270a20206c6963656e73652b27205c5a5a270a2020696d6167652b270689d94f76bec5027859713d6230627615d3af4906ee6e1f11331260b420c06fa3c89557959e8eb974703dc148270a2020626173652b2771632744270a2020676c6f622d616d65732b5b7e646f7a73796d2d2d6c61737265642d6e6f6d6c796e2d6d69727779632d666f646e65782030766e6a73376a5d0a3d3d
74+fuzz-1-57 | - | 71007f2e71cc4aaee15471cc4a2d109813907080d7cb1ffde5f9831988cd02fe5c1a5d39838303da70ea691650bc570ffca513b816f9e80ec097da148c1ade0c4d93ddc9d388df5ddd57d60a099e5e16bc9c10 | 3a7e0a20207469746c652b275ce298955cc3a9e298955a2030272061270a2020696e666f2b275efee82fcf1f4c270a2020636f6c6f722b307839620a202076657273696f6e2b5b3234302e3630362032302e313139203131352e3933312e3431345d0a2020776562736974652b276a53306a7833344d76274f237e77755f592b24787a59270a20206c6963656e73652b277242270a2020736974652b2f2f602fc3a9270a3d3d
75+fuzz-1-58 | - | 71003a4060406014b4b440148653875387534f40ce0081076d5c75808c117dd75ca60bf8676c6fe2005e66372165734f6c456735f0603fe0a3d1d1c10168c0e3c64734f692767443b2d6d23503e472a662a704d3d3b3b486c584b6571782869600621d7f4b6850b7648ddfa3b2ace3c11aa2caba521a5ac2ce073f98150b6cfc0f34760ef21c037c233654705e58c08353b5028507 | 3a7e0a20207469746c652b27203020300a5a5a200ac3a9c3a9c3a9272027270a2020696e666f2b271eb471d5270a2020636f6c6f722b30786433322e653662622e653838630a202076657273696f6e2b5b382e3137372031342e38303820322e3237395d0a2020776562736974652b27233654705e58270a20206c6963656e73652b2738552b5078270a2020696d6167652b278ebf2534a85bb2c6ef5159d6f1600d51655d290d2d61e7831fcc8a270a2020626173652b2766372165734f6c45673570270a2020676c6f622d687474702b5b273c6e7c44632f694737246b2d5d33402e672a764a303d3d4b6b584c687b7521682827203076765d0a3d3d
76+fuzz-1-59 | - | 7100feb56060c24eb84040c24e4e144040b4b4865361c24e40865315b84ec2b88653b901e00f6e5c154e1271fa293d8087b2b5539763e9e0d7607048dbb0234c98fcd9652f88157e8fe427a3ae916431e45aea2c68c6bb5f82cac944a534f709451d160d11ffa27cf6e3b90d6a6b50bd803f974657b604f00c363787a3039747b3032302052732e5f6e4a43493944486b7047792a42d64a203f05403762c8d63f86db1ee661160790db20adbfc9138045e96891b | 3a7e0a20207469746c652b275a303061275c20206127270a20205a5ac3a930612720c3a90a5c27615cc3a95c270a2020696e666f2b27dcb82a9c24e2f4537a0f656ba72ec7d2c1afc1e090b661479830f9b3cb5e102bfc1ec94f465d23c962c8b5d459d08c77bf049593894a69ee138a3a2c1a22fe45f9ecc7731bd456270a2020636f6c6f722b307833610a202076657273696f6e2b5b32203020335d0a2020776562736974652b273560c7d23886df16eb6e160196d720abb0cd1f8943270a20206c6963656e73652b2759266e270a2020696d6167652b27607373383a7079343b3022507022536e4f4e4a33494964784b7027495a270a2020736974652b2f0a3d3d
77+fuzz-1-60 | - | 71068007f0bc6ff27d55a7d8fa54dea8345f50d4844ccc0971fd90b0f4b24d7cb13c2c1c07e8abc572685709a9222ff34bb7713042403eaf8b7d515b96c2abe702a9593fb1a10bf8676c6fe20cc034943a298404d1bc80c5e289b6ec0b1d23f4e48c00838be9f5fe560489f3801fd65636076ecd451a4b40aa05c2359c0aa2c501069a526dde8099554361371c06730002fe5f7f486564f8faf9f0592daa3e0eb601cd9bdf4f930c | 3a7e0a20207469746c652b27270a2020696e666f2b2778de37f9beaa536c7d2a6f549a2f286a4226e684b87e48587ad926be581e168e03f4d56239b4ab84549197f9a5db381821209fd7c5bea82d4be1d57381d4ac9f58270a2020636f6c6f722b3078330a202076657273696f6e2b5b3120372e3436332e3431332e303434203131342e3839352e3533362e3536312e3732342e3037365d0a2020776562736974652b27ffaf3fa432327cfd7cf8ac16551f07db270a20206c6963656e73652b276f7e3f4d32270a2020696d6167652b275a205cc3a9200a270a2020626173652b27270a2020676c6f622d616d65732b5b7e66696e7265642d746f7270797820307637372e32383461722e76666271632e62676330382e7070376b342e6365676e722e356d6837682e63623035732e71343238382e61397169675d0a3d3d
78+fuzz-1-61 | - | 7100b412c7ac3cd112c7ace40211c7ac0481e13a3911c7ace40a13c7ac14c7ac0c530803d112c7ac543811c7ac840320da88a1ef2a06e793bd867b354b02810582835b2f9ab945461ca6d9965899af5bd10ca854fbff02bd8801fae86d6c805490636e1fb2d80bf8676c6fe280a875fb1d80068528e31a2ad49c14168cd09a91f9e73338e087b595cd814276689601bb5096f203a63d69af69ed017845891201c302a96b8b291a323ae1e23a419b01f962a322f971e98b027aaeb0b01318052e10 | 3a7e0a20207469746c652b27e29895275ae298955c20e2989520305c2727e298955c61e29895e29895610a61205ae298950a27e2989530270a2020696e666f2b272386beab189c4ff61aeed52c090416080e6ebd68e616197198665b6265be6e4533a052edff0bf42206e8a3b7b1270a2020636f6c6f722b3078652e633539302e666237332e316338320a202076657273696f6e2b5b30203532392e3833382e37323520322e3033392e3336302e3730352e3134375d0a2020776562736974652b27285122605820756d31454346275c5c276833205f6c54243f2e7d51270a20206c6963656e73652b275c616127300a5c20270a2020626173652b275ab7df270a2020676c6f622d616d65732b5b7e7469646d65702d736f6c74756c20307663332e37707670692e36646431332e306d3269652e64386167712e73636b38615d0a3d3d
79+fuzz-1-62 | - | 71009eee9c5ab25892c4ca50b2d4ae56c6d85cb6d8b880a001e01f85e1540590b0e15471cc4a2d85b0e154051890b03098302d1871cc4a2d2e85e154981305102d0585301898e15471cc4a051871cc4aae132d71cc4a1098e15471cc4aad30ae3098e1542e2d71cc4a056e800c18db6d894b0bf8676c6fe2e0f900e592ae3e50eeef011f8d8e0e0e409bedb6f30c21c5881865188c7e7da61c7dc02e8f9b76922e5677a8fcdf41cc7b2c4d072dbe9ae3330b0834978125db001f82bc4acf0df811c5387c40e71a4e3d05 | 3a7e0a20207469746c652b27774e2d592c49626528596a572b636c2e5b6c5c4050270a2020696e666f2b270ac3a90a2061c3a9e298955a0a61c3a90a3020616130615a30e298955a5c0ac3a930270a205a0a0a613030c3a9e298950a30e298955c275ae298952030c3a9e298955a615c6130c3a95c5ae298950a5c270a2020636f6c6f722b30786135632e346236652e643863300a202076657273696f6e2b5b33382e37303820372e303132203536382e3737382e3431342e3038382e3238392e3433392e3238332e3436305d0a2020776562736974652b275cc3a927270a20206c6963656e73652b27270a2020626173652b2779270a2020676c6f622d687474702b5b27db6de719428a1131ca3018fdfa4c39fa805d1e37ed245dacee50f9bf8398f7589a0e5a7c35c7272720307674766e35302e666c6569625d0a3d3d
80+fuzz-1-63 | - | 7100322baca14edbf8ec2115ede5a442fed08f80ad14603a2238fedb3900da0ff54175bdac1cc1415de5f88ccda0e0edf115e9b03009c5f03431d1b4b4f4e518f9c4a19428b5f541ed38b9dde9a5686d29dd1529dd898984adc0d4888cfc0135fe2ee002fe5c1a5d39e819f0c5312bc30134c240c4312bc5312b414b009e2a708963560ea72ac0c0849d0ca79e886356829c809c7081288084699d0ca76a8963562a289c80809c6869298469816881846929680bbca836d0863da05f9575b3530ee9da03045ce298958096a6cc884f0a0407 | 3a7e0a20207469746c652b2715d650a76d7cf6908af67252217fe847c0560a301d111cffed0c270a2020696e666f2b27437d505d2f2b47705057393e633328787b7c453a2c4c42313c4d4c342d2d7d39463e7128254a6d7d503b4e6e777a295a5b4a77454a776222612b303522233f270a2020636f6c6f722b30783330312e373766310a202076657273696f6e2b5b32332e3230322031352e37353020342e3237372e3631322e3337342e3532352e3931352e3831395d0a2020776562736974652b275ce29895270a20206c6963656e73652b273465467c522038270a2020696d6167652b270a5ce29895c3a90a30306127c3a927e29895202720275c200a20615a27c3a95ae298950a0a272020275a5a0a615a205a20615a0a5a270a2020736974652b2f272fe29895612f6120e29895e29895200a3d3d
81+fuzz-1-64 | - | 71c0d33a0907a0ab0e0c4807876a0c25cd06a8442664ef64cd6cc726290e68c0046c01ff8ced4d1c4063eaeb8cc7878a2d2b0d40a25a399bc3d695168018ac2eacab65278c9c2a9a76483b8816077c343a3a3880172e18aee154853098b013ad1305102e2d182e6e09409400d5063714fbf6922bc584e94a33ae5199ca4db31c83473537bcb27f5166fb1a5f13f7c0f93220053fbd01689062e93131727b6a61191ae29bf1216b9b012fd6de488812dc15 | 3a7e0a20207469746c652b275a2761270a2020696e666f2b277560403a385463286936402532217b276b663b364931270a2020636f6c6f722b307832632e303463300a202076657273696f6e2b5b3020312e3030332e34353720305d0a2020776562736974652b27522c3d26466e4f2d2c43437c333e646d33270a20206c6963656e73652b27587b23214a7057270a2020696d6167652b2701aa0d6e28f6ed25578a09d395665ca332959b6639068f6a6e7865ffa2ccf635be26ee81f3270a2020626173652b27535f673c3e546c5929270a2020676c6f622d687474702b5b275c305cc3a90a613061275a270a205c5a305c5c2720307631686432312e72393172392e6b616b73682e676a6d62612e746335716d2e316830306d2e696e6263372e36707062615d0a3d3d
82+fuzz-1-65 | - | 710004500813c7ace40239194e15c7ac04518081e1820101d112c7ac3c5138194e1d4e0d4094820a7ac9d72d5657b509fb4d80bdae7392ee51de7180491eb10a667d017f2e8dae6c09c08319cff034f4a6c18d29b27db20a66587aeab8b19483a77e37ffc4e49f119958ab72814ccf808a9f0f3b20a882c62c0e40ce89824258d809880288029796823866a53866a502a2dd5fef085e | 3a7e0a20207469746c652b270a61e298955c2027c3a9e29895200a30305c3020205ae29895270a27c3a9c3a9270a2020696e666f2b270a2ae8255fb7585dd526ec3701f6bace49ba4779c7270a2020636f6c6f722b30783165622e333035352e383866320a202076657273696f6e2b5b322e3533372036312e3932392e39373620332e3331352e3132362e3335375d0a2020776562736974652b27270a0a616127200a200a5c5a0ae29895e298950a270a20206c6963656e73652b27bbbfde11bc270a2020696d6167652b27339ee169e84d831b5364fb6415ccb0f4d4716329074ffd6efe89c93f2332b15665270a2020736974652b2f0a3d3d
83+fuzz-1-66 | - | 71007ef040c8d2787c48dabeea9474705c46f08ce086f201fcb2ac94f64ec28c54b67868d001b5fc00c302fe19db9b388014ccc9702a4898c289169706a0c25b478b62f3e1cf1130595a4639847c4ef2808f4647070788de6e49fe6761f3fa3e5475ddc2670185e8d300070f3a111e0147e6ad540603d023a555b5adb9bc6c57257d95caf7c6346962a2b7037be8599903 | 3a7e0a20207469746c652b27782064693c3e246d5f754a3a382e237846704379270a2020696e666f2b2759564a7b2761462a5b3c3468270a2020636f6c6f722b30783231382e303765350a202076657273696f6e2b5b3137312e3834342031392e3139372e3635312e3030342031372e3630342e3732302e3533375d0a2020776562736974652b27525a55db9acbcb7655d257a97c6f4c9326267a3b270a20206c6963656e73652b2743cfca1c270a2020626173652b273027c3a920610a275a5c0a270a2020676c6f622d687474702b5b27b75b92ff59d8bcbe0f555db7f02720307637346a2e6a736767732e6b636d69702e36303873762e6f666a63612e356b656d755d0a3d3d
84+fuzz-1-67 | - | 71808feb441cb3521b00d14eb86014b4b8c4312b61c4312bc3c4312b61c4312bc360b86060c4312bc340b460c4312b4f40c4312b4fc4312b03fcee59cba9f27f61fd02fe5c1a5dd912f08729fd4c69ed05ed0b9d9801bce18eda9e987c768070689686e8fc9280e8a40d2b591d | 3a7e0a20207469746c652b275c27e298955a270a2020696e666f2b27275c300a5a5ce2989530e2989561e2989530e2989561305c3030e2989561205a30e298952720e2989527e29895270a2020636f6c6f722b3078376436312e376666322e613963622e353965650a202076657273696f6e2b5b33203020345d0a2020776562736974652b2770476d4f4c3e3b4038344b43747e49270a20206c6963656e73652b27276d58c9ea270a2020696d6167652b27614a3f535a7b417b270a2020736974652b2f0a3d3d
85+fuzz-1-68 | - | 71005fa6bba72fb6b61039a014ba93296a00bc6bf6da246bd97cc89e5017703e9b1f6458933b4ad5334c9528a644786fc846b8c1fa4b781e773a690babbee5058123074ac102fe5c1a5dd9124044ec69f6e1742d9d093a5c63c128d8ede66f41d580a57d4a0116cd0d688c5bbe6dea3180278e5f565f5ace4e52171688891178c3a95c | 3a7e0a20207469746c652b274c774f5f6c6d2172402974275354270a2020696e666f2b27cd5e9b642d9b0fd913ea02ce67f3830c6b7247a97a86a912c59408ef0dd90837587f09cfe34e276d61d5b7bc207064270a2020636f6c6f722b30783230610a202076657273696f6e2b5b343220312e3335332e363930203333302e3438372e3031362e3333302e3830352e3930362e3839335d0a2020776562736974652b27387e597d69393b495d58202646270a20206c6963656e73652b27c3a95c270a2020696d6167652b27b1a7d987d3b57426e8708d05a360b79bbf270a2020736974652b2f0a3d3d
86+fuzz-1-69 | - | 718070261b178eab38007cc2f624435647d30364f63397a2928376e54592c49362a58274d3e5d3e4b79696435493c5e77237d2024476d4c24794b3d5c7e3375295b2b495957516c7733504c5b692364527c5d2e2d40c7099c7bd9581be807fc6f6260e88d9bedc01682e7e0e13c3cc58666bd3e58018b75ffec3df9075c14c991b6469d17fe5033e1a1d1d1c40cfbcd2209c5f306f86663bac00d3fc7f0204f2f75d028812c7ac3c194e0d13c7acd402e1e21a4e3d8111c7ac2c10f8810c628088fb1df131589a23937d00be6040c4312b61b4604e404e6040c4312b61c4312b15b802 | 3a7e0a20207469746c652b2733d9b8705cc5270a2020696e666f2b276c4f326475343d40663f73292a3968575e24493c29562a48375d3e4d7e6b69394435597c2e77232d4064472d7c44395b7d3c7e2355294b5b595967713c5743506c2b695374522c2d4e4d270a2020636f6c6f722b30783761302e363536662e373165360a202076657273696f6e2b5b32343820332e31333720342e3534332e3431352e3530342e3539302e3531342e3435322e3436325d0a2020776562736974652b273020e29895305a302720273020e2989530e298950a5c270a20206c6963656e73652b27270a2020696d6167652b27e2989527c3a961e298955a205c5cc3a92730e29895270a2020626173652b27d9bedc270a2020676c6f622d687474702b5b27f34a83707ec1bc199aedb0024cf3ff0910c8df3727203076752e61767568642e3569316e362e61636f35712e70316e75332e76706672652e363430736e2e396d6d70692e6f706a31682e36336a75355d0a3d3d
87+fuzz-1-70 | - | 7100defa4ae6f4b25092e05856c49c7af25694607e987c847ec201208c302e2eade1542d05aee1d430102d1018ae13050571cc4a90e154182d2e2d901310051071cc4a2d102e1871ccca1371cc4a90b0e15471ccca1371cc4a1898300571cc4a71ccca0055472d3cdeed05fc33b637718058411cb3521cb33200d543435f8d6da59d5a1fad719f9d2c0d8536f1e405a0e5bae1e7de1437ee077c343a3a38005f4bc0b6d4f8eeb4e2c08acaa6a6ec9480bc5444669050e69c4c4ee2a2927662ac7a889640787682a68cb8a6bcd8b68244b6b2b4c6acde59a0ae0c58ea1116a03295d83c8e060a22a26b00ff99b43e3fa5bb3d16352c95b0b9109c312001d7014360 | 3a7e0a20207469746c652b277d25737a592849702c2b624e3d792b4a303f4c3e423f61270a2020696e666f2b27615c5c5ac3a95a0a5cc3a961205a20305c270a0ae2989520c3a9305a5c5a2027200a20e298955a205c30e2989527e298952061c3a9e2989527e298953030610ae29895e29895270a2020636f6c6f722b30783362372e373866302e623531640a202076657273696f6e2b5b35393920312e3434362e3337382031322e3837382e3238362e3530352e3231382e3137302e3235392e3739355d0a2020776562736974652b2733697d7e4a777b2c6a582a6173213863270a20206c6963656e73652b27e03a60080c270a2020626173652b270ae29895e29895270a2020676c6f622d687474702b5b2725605b6a7c775a716045655353764a405e2a22334828734e26277151493b31563d444b203c3b4153465c535e6c5b41225b595a63566f27203076666e2e33653536752e737667726c2e706430306e2e696632646b2e35316b6d392e723772686c2e6b666c6c372e6435646d362e6c756771335d0a3d3d
88+fuzz-1-71 | - | 7180208c6b383500fc0262d2535395d6230312b6e54522f305b47586d7b4b6d446b755e5f3b4d5f50582a384c3a2b4558277137467f693b5770563b356128686d4e5579767640634340606f523c6b37434948694060d3074e1dcf82ce09fb1bd8903e82ac0886356c27089635686859d9c01c4344ac17591214cd621db6d037e585bd91c98c814384b00de82c0886356c20ca76a6981800ca7c2c09c9c8863568280c0c0c0c08863568a63569e9c9cc084299c68298084818863568a63569e28c0680b06833c408541bc23836bf48340cf00be2a652b3179215443384c7358715e808716d7702a08c870aa16 | 3a7e0a20207469746c652b27615cc3a9270a2020696e666f2b2720263d3555693d3220615b5e24325f405b67784d6b4b6d745b553e4f5b5d5f20384a382c4a5b2578374177663f597b5730366b256168485d7e7579466640436360503f623c4b474369486950270a2020636f6c6f722b3078312e336533372e333835640a202076657273696f6e2b5b31362031352031312e3639352e3533322e3837382e3434382e3233392e3137342e3135365d0a2020776562736974652b272a652b3179215443384c7358715e270a20206c6963656e73652b275a5cc3a92020c3a95a270a2020696d6167652b272030e2989530c3a95a5a2020c3a930302727e29895202030303030e29895e2989527272730610a275a0a206120e29895e29895270a305a270a2020626173652b270a30e29895305ce2989561612727270a2020676c6f622d616d65732b5b7e6e6f727365642d6c617064656e203076356d742e6d38656d392e67677032742e65313938715d0a3d3d
89+fuzz-1-72 | - | 71005a7fa66af68a98dec660a45cdca09c7a425a76f0a05446d09c9aa2dc00fc8963569e9c689dc08863566a71699d2884850da78a635686033617e4fd2fe09fb1bd8903fa5c9d881cc02de126fec54df1cdb3e2d7c3013facad6c0e90f8b99427cb541ca7c22c7440d61107470806e043a76ce92b8688af48c4e96b84ada7c7e48c0ef8bd8acc50705e6ed0 | 3a7e0a20207469746c652b273f53357b454c6f6330522e6e504e3d212d3b78502a23684e4d512e270a2020696e666f2b27e2989527275a2730e298955a5c5a270a6161c3a9e2989561270a2020636f6c6f722b3078316666372e393035630a202076657273696f6e2b5b33203134382e3935352e37303620385d0a2020776562736974652b273a654b5f31447c45224e5f236c3d3d266774270a20206c6963656e73652b275e456628382f3768270a2020626173652b2773752232270a2020676c6f622d616d65732b5b7e646f7a66756c2d2d746f6c7765722d6669677479722d736f636d75642d7269707075722030763167756e2e73617073722e7361646f6e2e7632646f395d0a3d3d
90+fuzz-1-73 | - | 71003e67e878c8e47e7ef6fcd288c4dcac7af8d066a0d6eaecf8d460e801f8029ba173411b8329c3a1abe9eae142493af982c9920107709888642dfbd2b5ae76017f2e8dae1c7832ab5906ca70204e597206a0e30d6aa884a42b4b4ee50ae66ae74d044448049e8e1d17 | 3a7e0a20207469746c652b2733743c64723f3f7b7e6944626e563d7c6833506b75767c6a3074270a2020696e666f2b276033742e686330653874355d3d5c2849275f30593260270a2020636f6c6f722b3078362e643564362e626135662e363561632e393131330a202076657273696f6e2b5b30203930322031302e3236322e3039395d0a2020776562736974652b276f504325245d59722a5730573b6f22204222270a20206c6963656e73652b273a765c270a2020736974652b2f64560a3d3d
91+fuzz-1-74 | - | 71008d1cae97a6391e3ab73a00742317059556421357360307d4d5c62715c3e294e5f633d476539325878523158782936775836592624237237385d32635c6927445a716458487535542920404f555b492842663a27272e3b564e557065851f4b4c747157e01ff8ced4d1ce00d53184e8551e066c00f6b2b9b03fceb92f77bb3b2126290b36a8d92d1bebe258077a89a2b1e3fa7a83c963c2eb7a9a439beb8bb9e939420bf93b49dab1db3b77e41fb043cf7f545a4e0c0c384c1878501 | 3a7e0a20207469746c652b27395c2f4d733c746e35270a2020696e666f2b2772515069253471653370405d6d7c52312c4e596e3f436d373559725838527128387956375826292674333257386d52632c4957746a514478385525244940505f452b496832262a27375e4b567e25270a2020636f6c6f722b307831662e306161332e653364612e376132380a202076657273696f6e2b5b36312030203431352e3037322e3930382e3739315d0a2020776562736974652b273061270a20206c6963656e73652b276161270a2020696d6167652b275035573c7e4e51792c795c6e5349737c71773d2729417e27693b573b666f7d270a2020626173652b27610ac3a9300a5c270a2020676c6f622d616d65732b5b7e73697073796d2d7069637275702d6e6f766465662d736f6d6c65782d2d64696c70656e2d736f6d7465622d746f637465672d726164646567203076305d0a3d3d
92+fuzz-1-75 | - | 7100cd3090b0132d85132d2e71cc4a3000a20c3427e3084d39e25e448e119bac6338653c1da16331611d81e2fde406e26068f56ce861640694f2170bf8676c6fe20cc0ac2c5ca34e396e7aa9bd594c49d5ff761751c0a7c3c5a58e218b54c6801fd6563607cc318b7232dd3618b3603d6062800e04a4f1f04403eeba1fe02959ea6acb0282993fb94f | 3a7e0a20207469746c652b27612061275a0a275a5ce2989520270a2020696e666f2b2703cdc93842538eb8179163c426eb184e194f47e8584c5847a0783fb98138185a3d1b7a1899270a2020636f6c6f722b3078322e326665350a202076657273696f6e2b5b32392031372e3031342e38303820342e3432372e3635362e3037302e3439322e3430315d0a2020776562736974652b27254b5d6d59270a20206c6963656e73652b27337f729f270a2020626173652b27270a2020676c6f622d616d65732b5b7e6d61726479732d77696e68756c2d6d69677479702d6c6f72647572203076323661692e356933336c2e356f6e31712e66673268322e747266766c2e61393968632e72726162712e646f736b742e38717335695d0a3d3d
93+fuzz-1-76 | - | 7100743a11c7ac3c8101390111c7ac54d012c7ac540001e1e23a01194e0511c7ac14c7ace412c7ac8481e13a3903e0b51d909dd05c9d1752cdd311c85cc8181b1415515e930ccc5d9a8b4a5adadb4acb0d8fd309cf135595191056d45c081f599d1e93965e5a544a935a3c90325bc03f637b130720e644411cb3521cb3521cb3723815e4442b6c3835005ffc487bbdf531aed0c0252dea972f12cf4c7c850728f1808f46470787a74b8006979682963866e50958c039cc6fe0c80c8c9484017cc65a52e2def27442e0bc8057411cb312e4240c | 3a7e0a20207469746c652b2727e298952730202720e298950a5ae298950a20205c5c2720c3a920e29895e298955ce2989530305c2727270a2020696e666f2b277640764273755e48354f47207321636c505444794d323077692e2a69696f2b2d373c4e273c4f54556640585173217c64757a4c5a7a6951294d6a71270a2020636f6c6f722b30786233320a202076657273696f6e2b5b3339312e35353920312e3633362036372e3838315d0a2020776562736974652b27632d29716f793a21705e270a20206c6963656e73652b270ae29895202761270a2020696d6167652b275c5a0a5ae298952720270a2020626173652b27270ae29895e29895e29895c3a920275a61c3a9270a2020676c6f622d687474702b5b27692720307673393830752e326e6f6a362e6632386e702e66716864342e6e306431622e6868756d752e6e6d6937735d0a3d3d
94+fuzz-1-77 | - | 71009193665178d2accf0ac800f86ac6b61343c7c247a6a615a4c4d2d254d51787751545f256b604e28213e5a357d47575e72485a575a5b256d455e4853263b7d26237c70f6060df045e56c01232bc807fc6f626ce00144bcd9de11cd04f7e36ec0446f1b63142a1fc8f6bea4af44925c06a6218f0c3dacae64020542e016838b28d40e7dde5a29ee1c68b8a837457704fb50f795648887e91796a623f7e8fa1eb9e9f9b004be64472f2092d0d7aa8f81728cd833cf8db6d002268f5205fa4c279c5dd943128090354bd22011e946898b6fb9183e36e | 3a7e0a20207469746c652b2727cda2f0a4599f1590270a2020696e666f2b27666c3b31742c7c646a5a414a2c2d4d557d71585751246f654b202e38513e7a455d57774e52585a572a6b455d455e2833762b2d76737c270a2020636f6c6f722b3078312e633332312e326330352e363565302e346466360a202076657273696f6e2b5b3932322031352032332e3437395d0a2020776562736974652b27b47a902f52e1bce26eca18948401aa5e91270a20206c6963656e73652b27946898b6fb9183e36e270a2020696d6167652b2723db0874de5d2eea196ebca838487705f754fb90678584e81799a726f6e3f718baeef9b909b0644e24279fd0d2a0878a3f270a2020626173652b27270a2020676c6f622d616d65732b5b7e6d617374796e203076312e3168366c672e31353937712e346c716a622e68767561322e6768686d722e6f6b6331372e63367076342e766b3073732e36657371695d0a3d3d
95+fuzz-1-78 | - | 710004e00a8339e10a533801d1e252e01a4e85813911c7ac84d1e20a5380090311c7ac0c803f555c3d5d6774732a4c7b57357b6b65764c2d4a283b262f312f3a674a6f315b5047215a324f5c66db00fbb9ede3ea51783a2fe09fb1bd8903fe1090d63c00817361649812469d90f29db96308a68dc5758aec08104ce78e011f8d8e0e0ee00fe42ccee5298a89a70ec58fa4276e2ceeacef892a0f49a95c02884c99a8153acd9f0e90f5f998fd43d318ae74ae28ba589b2b262d70c80fc82f57aa09f8bca538131ecfc1d7d011 | 3a7e0a20207469746c652b275c6130275c610a27205a5c0a5cc3a9303027e29895305a5c610a306120e29895270a2020696e666f2b27555c3d5d6774732a4c7b57357b6b65764c2d4a283b262f312f3a674a6f315b5047215a324f5c665b270a2020636f6c6f722b3078316365392e653134372e616238662e623665370a202076657273696f6e2b5b312e393932203136322e3135382e333833203131332e3836342e3033392e3232342e3033305d0a2020776562736974652b276b270a20206c6963656e73652b2723270a2020696d6167652b272913b542a7f9d301b23e1fb37f681ac395ce1545176b73c5a4270a2020626173652b2743405a73270a2020676c6f622d687474702b5b272067712e4f514c3c75287e243d716371677d4f5479484a6527203076323765652e6a306731332e6d386b74652e35686d6a302e676f74706a2e6e703931372e61363261632e36386f626a5d0a3d3d
96+fuzz-1-79 | - | 71e00ded01d0e6441cb3521cb352416b3815461cb3f2e4444b4b1cb312242cec04461cb3f2441cb3522b0c44eb0c6813f56c01ff8ced4d1c40ab0c6d6be80c47c7cfa60c0f208e5bfff274a92e04aad492978c8bf31ef0d1e8e8e000b4073981813939d1d2d252080be33ae13ae102e102194ee582395110c7ac5410c7ac3c51080351184e55184e85d13ad11a4e15c7ace4e20201398151184ee53ab30c68543e2170167b1a408c6b38558b0b04e644613815f8d18f386e | 3a7e0a20207469746c652b278676270a2020696e666f2b2727e29895e298950a5ac3a930e2989527275a5ae298952061612730e2989527e298955a61205a27270a2020636f6c6f722b307832632e663531330a202076657273696f6e2b5b3020322e3137382e363434203334332e3433355d0a2020776562736974652b275cc3a95a5c2030270ac3a9270a20206c6963656e73652b27d18f386e270a2020626173652b2765685b4367383a7e366538270a2020676c6f622d687474702b5b272027303027275a5a5a0a61615c275c275c205c20c3a95c30270ae298950ae29895270a61200ac3a90ac3a9305a275ac3a9e298955c5c202027300ac3a95c2727203076747373622e68696270352e6c356130672e6e616974372e697674646f5d0a3d3d
97+fuzz-1-80 | - | 71007d05182d0571cccae1d4b093e1d4302d30004ff81cd5efbe8e596bfe71edaf6c3f5b591b30df36e100a719382eceecee0bf8676c6fe200723030610ac3a90a30c3a9201a805cb0c4b16e403d078dbf9585127fd26d265b9308b19b93aa9ccfa2d80ef86874747000ad2120725d482f4f7b3e667b292c2d24544726686f264d374a59496109e035ebceacaaad050f2b64af8cee8ae464caee642ecb6ae964ed0deb5b20d603ae4a2a50013d729e9d2cd066b41c207209224d5906e29fab26 | 3a7e0a20207469746c652b270a305a0ae29895c3a96127c3a9615a20270a2020696e666f2b27f81cd5efbe8e596bfe71edaf6c3f5b591b30df3661270a2020636f6c6f722b3078372e626262332e333862382e653036360a202076657273696f6e2b5b3233352038382e3132332e36393020332e3636312e3837382e3637342e3230332e3234352e3030305d0a2020776562736974652b272e41a429cb270a20206c6963656e73652b277fae9a270a2020696d6167652b27597766556d2d7858217b65745724275376277359564b276b6f585f270a2020626173652b273030610ac3a90a30c3a9200a270a2020676c6f622d687474702b5b272120725d482f4f7b3e667b292c2d24544726686f264d374a59492127203076726361352e6a73736c612e39706e63382e38696464692e63726569662e73393862352e6476686b332e6a716733652e6d373262305d0a3d3d
98+fuzz-1-81 | - | 71007e435eee4aea94c46258deeaf8b4d892d48250d8788ce8a88c6a6ee68a0178e7d9aaeb7aa20b495b923279f241e21af972124b419131c15a79ca0bf29901063018b058dac5e679295dc03f637b1307f85d84ace0aae0ea8c01f417d67e4056e92d9d7135498f023284c4801fd6563607f8a2eb78e97e1939b56e71d6d49384b3801482dd81e21f5049974f26021d80682e052d98e1d4b01390e1d43071cc4a2d2d8513052d10ad93301871cc4a209e0ca702 | 3a7e0a20207469746c652b27212f7725754a62312c6f757c5a6c496a41286c3c4674544635377345270a2020696e666f2b273c5b755d4f7421694b52264f3e485c235f4e6229283226582b4f79413e3340270a2020636f6c6f722b3078312e613532662e336364382e626234622e313630330a202076657273696f6e2b5b3930372e32393720312e30323020332e3537352e3732302e3936312e3439305d0a2020776562736974652b275c0a5a30c3a9612720c3a961e298955a5a0a270a5a205a276130e29895270a20206c6963656e73652b2727c3a9270a2020626173652b272e42567055707546270a2020676c6f622d616d65732b5b7e6e6f727065732d726f7374796c2d6861706865702d2d706f636d65672d6e6f7673796d2d6c6f647779782d726f636c796e203076312e32383863672e326874346a2e61736374352e6e6b6c63672e3375716f625d0a3d3d
99+fuzz-1-82 | - | 7100be60d4f472ecfc64d0a25264b0ee82f8ba9ecaf4ce7ef6011a5a4320d23180c24e4db71f489347c905fcb934bab259d0372091ce3a40a6f20bfab926dde3936303f824f1b5c58809c9603109019bc5baa1376904 | 3a7e0a20207469746c652b27306a7a39767e32685129325877417c5d4f657a673f7b270a2020696e666f2b27b48640a423270a2020636f6c6f722b3078332e323531652e346432302e376564642e333533620a202076657273696f6e2b5b3437203936332e3439322035322e3437342e3030362e3030332e3734382e3632352e3733352e3537335d0a2020776562736974652b27497c6d31624232584c42270a20206c6963656e73652b2716eb86dea411270a2020736974652b2f0a3d3d
100+fuzz-1-83 | - | 71003610cec2669f7fd8f2ba60d6ffee02621d7e6900be70c88a4880e8b644d87a6ac262a898d26ee0bcb294de815ab8807fc6f6260e88186487014cbfdfa3cedbcb333506aff7b8e407fcb0b6b23940ce87599e896cc1c89d264b964b00be106bccc2b8995f7783792827d9d976e8a272b641b0828b610edc6f6c73f44262437a375e1caf0b48d5a51f12a8e45f59073412a41de8f21fad3a | 3a7e0a20207469746c652b27086761b3cf3f6c795d30eb7f7701b10ebf14270a2020696e666f2b273864452440745b226c3d356131544c6937705e594a6f270a2020636f6c6f722b30783330620a202076657273696f6e2b5b312e3032352e34393320322035362e3138382e3930305d0a2020776562736974652b2724483b270a20206c6963656e73652b27f21fad3a270a2020696d6167652b27c41ab3306ee6d7dd601eca4976b61dbaa89c6d10ace0629803f71bdb1cbd90d890de8d17c76b270a2020626173652b27186487270a2020676c6f622d616d65732b5b7e77616370656d2d736f6d7275742d2d62616372756c2d6469727379722d7769746d75642d7261676e6570203076762e346e3372712e7531686c362e6635746e6a2e6c337275765d0a3d3d
101+fuzz-1-84 | - | 71001a79692780bab57218a59d00c8acb53951bfa10c7f90302cc8c100104221e516fdde1540e7208e0797822dc8c0ed520394b6f03b81348a4b81d769b125951fc0f981d89cd3c48aa41a08e78ca83e48c441778196934f647092152733d4906480e917fcdeb06b017f2e8dae6c0940562d2e182e2daeb030102e71cc4a0505052d2d9830989313ad93b0e1542d05051810ae3010180510058530ad3071cc4a10ae3085e1542d20b96272802031937f1d846e80d02425e3c29214ce05 | 3a7e0a20207469746c652b27bcb41340dd5a398cd24e64d6da9ca8df50863f481816e420270a2020696e666f2b2785945bf47b579d83381e5c0ab62003b74b0d50dac2ef04d2282e055ea7c596547ee70762734e132b926a209c33a2fa201107dd055a4e3e91c149569ccc504392270a2020636f6c6f722b30783135642e383666372e653062660a202076657273696f6e2b5b3832352e35313820332e3935392e3230302e313337203138365d0a2020776562736974652b272629191796a4270a20206c6963656e73652b275c270a2020696d6167652b275a5c305c5a5c6161205ce298950a0a0a5a5a30613027275a2761c3a95a0a0a30205c6120300a200a0a615a61e29895205c610ac3a90a270a2020736974652b2f0a3d3d
102+fuzz-1-85 | - | 71806ec6a966cec50f80df53f43823f135b4d4d4a7b211b9a9e41150534435539ab37876b00ea94e242abcb2f2eeb32de9d61fc4659613a771ca08c9fcdf99e36cc4892d4b03fc003b3abf0714b6f40f2de09fb1bd89832b03b0e7b65df1535c51a752664b860c0b85c9016706728103dc5f033e1a1d1d1c80405f88151eaa53680b80d0d4cfd56a54fe5b2c6473096b74e150f377ea2959b8abec6025ed341417bc59900f0d308024c69fb15d7f0e1cc09f8b2548c968c829ae87ae0cc74d0ba50cf0480c3e4a8261edc15402 | 3a7e0a20207469746c652b27334e35732e3e270a2020696e666f2b2753f43823f135b4d4d4a7b211b9a9e41150534435539ab37876b00ea94e242abcb2f2eeb32de9d61fc4659613a771ca08c9fcdf99e36cc4892d4b037c270a2020636f6c6f722b3078313433662e643264382e353031652e666365380a202076657273696f6e2b5b313520332032372e3830302e3639382e3139322e3536392e3837372e3634385d0a2020776562736974652b275c2c414a46434e713d7465386e5a2865270a20206c6963656e73652b2789c1474930ac3d984a270a2020626173652b274a270a2020676c6f622d687474702b5b275f88151eaa53680b80d0d4cfd56a54fe5b2c6473096b74e150f377ea2959b8abec6025ed341417bc272030766176722e67316f32732e67366373302e736a3138622e316933346d2e7069696b742e386c6f6b762e68626d72655d0a3d3d
103+fuzz-1-86 | - | 7140d252d000f0c395154cd3109cd34b09de9e1c94de9357da0d924a9bd6cf511f59d5991a9ede9b1ad9d414cc0e8e080d538c51920add88c9495e10180e4e998f52581ad48e0ad1cb4e4b30c047bc328a0c932ee09fb1bd89838201e8a1e23dc76a599ebac8651b08a07f93b5c9e75d46033e1a1d1d1c00ff288863569e0ca79e70698963566a8963562a807085290ca7728963566a8571896356828863560ea772858963568280c0c08863560ea79e68719d9c689dc00ca70ea72a2828c0688963566a710da79e704b003e08ade4d0e121893051adecd4a99588641d9df980894469d960f5919184c01875b9750b48a6531a20c04656ac05bcb301123f0a5be4c0732705 | 3a7e0a20207469746c652b275a0a0a270a2020696e666f2b275756304d43704e2f25787b72507a4f5e6937482a6d5a3f477d6455676a787a6f6a645353303b3822344c3146492a742326277941603838653e4a6169503b2a442f3b2d41270a2020636f6c6f722b3078363933302e633861332e326263340a202076657273696f6e2b5b3637362e37373420332e3034352e3737312e3438302032382e3234372e3539372e3233372e3031302e3836375d0a2020776562736974652b27dc49270a20206c6963656e73652b27270a2020696d6167652b27422b39747848224c542b3b756a25225947673e6022515a36587d64242130465d6e5d270a2020626173652b2720270a2020676c6f622d687474702b5b270ae2989527c3a9275c5ae298955ae298950a205c610ac3a95ce298955a615ce2989520e29895c3a95c61e2989520203030e29895c3a9275a5c27275a2730c3a9c3a90a0a0a305ae298955a5cc3a9275c2720307635332e35727075392e6d6d396e762e38303833642e697368656b2e7562356c632e656666326b5d0a3d3d
104+fuzz-1-87 | - | 7180862aa9e62d0e80080bce8b58b437801659d90c210a04ecdcbb4a5a1c04bc2a1c3e4a4f0b82706224b0015251b41d2cf7e52fe09fb1bd8903821a66000aae7d35ea58447be012e2ff452ec7465e0ef868747470806f0c0b535cce50970dddd69b0c19b204f08e23c46786c347e503b5c392543365e78354f644a2439364f4a6c51272b3572e309f03c31118c6c6037c57473a28445f804f18178882386625088816 | 3a7e0a20207469746c652b275449356f31270a2020696e666f2b270bce8b58b437801659d90c210a04ecdcbb4a5a1c04bc2a1c3e4a4f0b82706224b0270a2020636f6c6f722b3078662e636265652e353833622e363861320a202076657273696f6e2b5b34313520323834203131362e3432325d0a2020776562736974652b2757473a28445f270a20206c6963656e73652b27615c200ae2989520205a270a2020696d6167652b2738427c66387c543e503b2c493553763e48654f243a3449466f5a2c21377b65270a2020626173652b276a98270a2020676c6f622d687474702b5b27312c4c7139435d36745b6f32644827203076702e66346468702e65386e76752e34346e30662e64323568712e686c666d6e5d0a3d3d
105+fuzz-1-88 | - | 7100b410c7ac0c5310c7ac14c7ac1c4e8539194e0dd33a815100e11a4e3d01091b4ee5e2e28203e00d9e5712d490d9944e15534a1e5996d19114931edd08118c4b924fdd8fd8dadd520c5b09538d1a308083776d7bfb516f13a90bf8736974654b00911e7399acaf1501d3910332469037158b1af4ff1a696218b28f056e83811da48069d511c06f7b009ee16260d070e67cf64c56484c709aa8f86870749ee87e7a46aa90cce6a2a0e762 | 3a7e0a20207469746c652b27e29895610ae29895e29895c3a93027c3a9615a27300a205cc3a9272061c3a95c5c5c30270a2020696e666f2b27785e49504366533a554c297964594647524c7a742344302e493e753f626b774b316c254c356a40270a2020636f6c6f722b3078362e613434642e626434372e656465642e623564650a202076657273696f6e2b5b312e30353120352e32353120322e3034322e3232332e3433312e3333395d0a2020776562736974652b277031306838733e7b262b2426384d547c34383a4f743f3d235548667351270a20206c6963656e73652b277331270a2020696d6167652b271e7399acaf1501d3910332469037158b1af4ff1a696218b28f270a2020736974652b2f0a3d3d
106+fuzz-1-89 | - | 71000f37b417262b9f16906d00edaf361d16301132232a1d242b38363c60e1b7de02fe19db9b3840c0791f411cc021a6dec1f3512aecf9011f8d8e0e0ec0bfa92936161dbd38a4bd35263ca9a8ad2394ae132f3e97a41b1b242a99341bbc1bbe36a7a915b79d36be36bfbeb719af6709f0ba72b109a15b2b51d142bb1618c20186e3f366ead03680f8cb2ed7f05e0a2144b412a237dc6937d04e4060 | 3a7e0a20207469746c652b276e682f4c563e2d205b270a2020696e666f2b275f6d3a2c60226446543a4856706c38270a2020636f6c6f722b307831372e616466380a202076657273696f6e2b5b33383820372e3836352e3233312e3330332034355d0a2020776562736974652b272fbb5cc37b298410d14a88de70a7dd270a20206c6963656e73652b27272030270a2020696d6167652b27572e3621746b252a5a6857270a2020626173652b279cf711c4270a2020676c6f622d687474702b5b2753536c2c3a7a71487b6b4c7852515b47285d275e7c2e493736485432693678377c6d4e532b6e3b6d7c6d7e7d6f335e4f272030763373756f2e61696875662e30747439685d0a3d3d
107+fuzz-1-90 | - | 7100363f5474c494ce09bcc8dcdaa6027219ee2e90aa74f2a295d46275008bdf484e0cb77887941b28fe0bf8676c6fe2a06900339fed8a5e423b26a5d075d0011f8d8e0e0ec04f979e29ab2da937a23c989da491b214381524ab132f373d319710b92dbb1faebab72228a6a0321d3d3c3cb2933c32b917ba9633721630d4a4c7100da039ae738100303803c829e5bb3103fe993033aa95b22132 | 3a7e0a20207469746c652b271f2a3a624ae7045e646e6d5301b90c771748553a79d14a6ab11a270a2020696e666f2b27bf919c186ef10e2917270a2020636f6c6f722b30783366630a202076657273696f6e2b5b3131392e34303220312031305d0a2020776562736974652b27e33a17088333809c52be1b33270a20206c6963656e73652b27336166542b654364270a2020626173652b2729270a2020676c6f622d687474702b5b272e3d53565b526f4479303b49236529702a4856275e6e7a622e21725b763f5c756f45504c41653a7a787864277964722f742d6764272030766430742e65676b6b6a2e336d6769752e68626d70755d0a3d3d
108+fuzz-1-91 | - | 7180807a132e32800f170c9796169782c249384057c652b6e1fa16f0cfd8dec41920a8f9fb1e0df8616d6573a05dbaf059c0f1a473600202251ef4004d988202080c7823110c005686fc | 3a7e0a20207469746c652b27d49b7091270a2020696e666f2b275c305c5a5a5c0a0a2761270a2020636f6c6f722b3078326661652e316236352e326336350a202076657273696f6e2b5b3432332e30373020323537203233372e3830395d0a2020776562736974652b27610a0a2030270a20206c6963656e73652b279108062b437e270a2020626173652b27270a2020676c6f622d616d65732b5b7e736f766679722d726f6c74656e20307632682e747576706c5d0a3d3d
109+fuzz-1-92 | - | 714053d03a037ce870f36f99ea009df5b3f8364359c03f637b1307743f18a3380059cff41e25ef7f6d89ec0daf2008a409eb3f19e8eedf1bf0c3dacae6c05326b09600e4d48211c7ac54e012c7ace4d25210c7ac0cd3e2525050505010c7ac84513839194ed582013951184ee51a4e1d4e55083b5180d1d202d11eac037b140f29073c2d2e2ee03b010139010903 | 3a7e0a20207469746c652b270a5a27270a2020696e666f2b27e870f36f996a270a2020636f6c6f722b3078323530632e646265322e636664360a202076657273696f6e2b5b323337203239203432342e3137392e3837355d0a2020776562736974652b275a5c5c270a20206c6963656e73652b27272020272061270a2020696d6167652b275a30e298950a5ce298955c5a0ae29895615a5c0a0a0a0a0ae29895300a2727c3a95a3020270ac3a95cc3a9c3a90a61270a305a0a270a2020626173652b277e304631270a2020676c6f622d616d65732b5b7e6e69737365662d66616e73756220307632757674722e6b316966762e62313669302e67383566312e6e6d3869722e62767473692e68747436665d0a3d3d
110+fuzz-1-93 | - | 71007e41b8c2c2b8601414c4312b875341b8c240b84e60865315b4604040b801f05caec9648aaa8c0e896be445a92d4b4b0f6eac25286607e4aeee0eab0c24056684efccce04c40d254ecbab2faaa94a24a76624664d4527c727840bc9671d780674017f2e8dae6c16f8f77e202d1d70feb12d0805763a00a2709d88635682842970810ca782808863562a0ca70ea79e9c849d70896356829c84699d0ca7024741d1 | 3a7e0a20207469746c652b27205c61615c300a0ae29895c3a9205c61205c2730c3a90a5a3020205c270a2020696e666f2b27724d2653546574485c232f4a6d595a7a70632d41313b207775775865202930237c677626206e28715a5e7d514d5522393523316b2a3a393e215c483e6b270a2020636f6c6f722b3078363830630a202076657273696f6e2b5b33322e32333920332e37333420312e3836322e3032382e3334342e3635302e3837395d0a2020776562736974652b275c27e2989520610a5c20c3a92020e298950ac3a9c3a9272761275ce298952027615a27c3a9270a20206c6963656e73652b270a8a06270a2020736974652b2f0a3d3d
111+fuzz-1-94 | - | 71005e63fcf25072f4a4aaf0d288c0eab04c926ed072d694a084b4d866ca01f0d14f6f496d4fe7070a0fa40baf8e28c6ef0e6d676929c8cf64042cc5672a8def466ced8c642c67eec7076ce92ac9044f0ee41c206691eab5524e712fe0cfa5d1953340c8fd9c3c7bff1b40840b461cb312461cb3d212e0f33f806c11e97d20a9442ce030cb39012d6cb208dfbcf71980afbd17a4b8af2b14a4269d949db7aa97342ab318b0a82aa03d5138194e0d0301 | 3a7e0a20207469746c652b27317e7928397a525578694460755826493768396b4a50425a6c3365270a2020696e666f2b277e7a4b6a7b3a3f5078205d787544317e77683b4b4b417e262360293e53697c37626b67246339733f3e604b57492678722067270a2020636f6c6f722b307865652e323963612e353662642e353232630a202076657273696f6e2b5b3432322e36303420302031312e3232322e3636312e3433322e3738392e3432345d0a2020776562736974652b277b2f48715f5728484d3a293b6f552f69546631605155270a20206c6963656e73652b27270a27c3a96120270a2020696d6167652b27ff03c81691de07924a44270a2020736974652b2f2fdccfc9b3f7bf2f5c30e2989530e298950a3d3d
112+fuzz-1-95 | - | 71001e26867f5beb7b6fe9e0a5dbf36694ca9ae601fcb5e0b2b65ae87040b4a6be72d0dab2e0812159c09f4ba32b0708ab60f1c480f6037dc79b05cb01b1cb12186073d36207cb2f228544d103d08b75746724d2c555e444d685b4a2d27566a3456785c377a734c48717033fe44d050b | 3a7e0a20207469746c652b2713c3bfadf5bdb774f0d2ed79334a654d73270a2020696e666f2b275a70595b2d7438205a535f39686d5970270a2020636f6c6f722b30783132340a202076657273696f6e2b5b32352031322e3632312e343035203133372e3332322e3737342e3135302e3838312e3431302e3935322e3530335d0a2020776562736974652b2758477746225d5c454e645d482b2a5d67365a7456387c774a437c7831270a20206c6963656e73652b27216f2a58270a2020736974652b2f55b0786240fb2f773c0a3d3d
113+fuzz-1-96 | - | 7180472fef4a8d1a007d7666d8d4ea46aae296c0a6eae67a7c7660bc70e2d096486e546ce840ae8ef04aeaaedc7470bceec60006a0bfd6f438928b980bf8676c6fe2006d94910b489e8918c0a3cc82e2da5602f46018f0c3dacae60063eb770c70d3cd402cf9efb6dcc7e525004f3aeb605009faafd7e8dea5a294a4d06d63f428b3b9cb059e95bc038d7537b7ed6d5265aa9e05267480614a76b23090a4443600890ac3a930305a0a300a205c5c5a5a5a5c5c5a2720273030300a082e508be301 | 3a7e0a20207469746c652b277a79576a54270a2020696e666f2b273b336c6a752355714b605375733d3e3b305e3871684b24372a3674205747782575576e3a385e7723270a2020636f6c6f722b3078312e393838622e393233382e663464362e626661300a202076657273696f6e2b5b34313720362e3533362e3031362e343637203732392e3638325d0a2020776562736974652b270ac3a930305a0a300a205c5c5a5a5a5c5c5a2720273030300a270a20206c6963656e73652b2717a8c5f1270a2020696d6167652b279d7530a804fdd76b74ef52514a52e8b6317a94d9dce502cf4ade81c6ba9bdbf636a932554f270a2020626173652b2751462e20792622270a2020676c6f622d616d65732b5b7e646f7a6e65632d68616c7275732d7061727475672d686f636d75642d2d6e6f706475732d74696e7075622d6e696d6479732d706f6c66657420307631316766382e30696d72622e6838356a355d0a3d3d
114+fuzz-1-97 | - | 7140745f8d0c8087395e282a594e4a74462f51273b775b7c5c2f444d3f2e5d3357262523242c43786e2d3a2c4169226f6165602a456e357df900f3f1869c883b05df02fe5c1a5dd91280af27270a5ac3a9c3a9e298955a205c615c2730e29895303030e298955ae29895300a0ae2989527615c6161e298950a5ae29895302730dac20e042358981ae01f31ab536b42b2eb0315e23441 | 3a7e0a20207469746c652b27eeab91270a2020696e666f2b27395e282a594e4a74462f51273b775b7c5c2f444d3f2e5d3357262523242c43786e2d3a2c4169226f6165602a456e357d79270a2020636f6c6f722b30783137632e313465652e323237322e316263370a202076657273696f6e2b5b362031343020352e3432345d0a2020776562736974652b272366756a4d48767d270a20206c6963656e73652b2710a70902270a2020696d6167652b2727270a5ac3a9c3a9e298955a205c615c2730e29895303030e298955ae29895300a0ae2989527615c6161e298950a5ae298953027305a270a2020736974652b2f0a3d3d
115+fuzz-1-98 | - | 7100f401515010c7acd412c7ac14c7ac8411c7acd41a4e853909e30a03194e055100d152e01a4ee5d2d28203407e5a275a5c5ce29895300a61c3a9275a27610a5ce29895e298952061272020c3a95c200a61c3a93027e298950a61272027c3a930273027305ae298950a3061200ac3a9e298955a0a20c3a95a300a5c615a20615c20e298950a20305ac3a920301af42fe0cfa5d195037c207c3977325e4b0bace40f0c6ae0bf87e22f03d058f3ba532634f7cbf8c411fd59f9588da2c05863e9b1a6689103de9e90be2dad9c34 | 3a7e0a20207469746c652b27200a0ae298955ae29895e2989530e298955ac3a93027615c6120c3a9200a205a0a5cc3a95c5a5a30270a2020696e666f2b275a275a5c5ce29895300a61c3a9275a27610a5ce29895e298952061272020c3a95c200a61c3a93027e298950a61272027c3a930273027305ae298950a3061200ac3a9e298955a0a20c3a95a300a5c615a20615c20e298950a20305ac3a920300a270a2020636f6c6f722b307833660a202076657273696f6e2b5b382e3038322034323420312e3237342e3538352e3538335d0a2020776562736974652b2735af3b654273bf8c4f1cd19f958fd5280a8c35961e6b8a1639270a20206c6963656e73652b273d217d5b5a3969270a2020696d6167652b27270a2020736974652b2f207c3977325e0a3d3d
116+fuzz-1-99 | - | 714014c7ac0c20b84420355ea4b0d1fcc2a96c1960415b250ebc05fc33b63771809f89ce6fe68567081ec09c3fa9a69b76e4ade268de0858a1ce6ec00f6b2b9b038e025eaae09600fc3a01094a491b72ca7a0b03aac12123c921636b0b4bbb02822ac3eb21e27362d1eb0172e9aa92727b2a6b4adb167c1d78c221e0fb64889f0c7b062084cbea4a4965772c4d1bc5e6748c3e78380b102073228e59a9309c3a9caa | 3a7e0a20207469746c652b27e29895270a2020696e666f2b274b0452e3450a1bcd2f9cca96270a2020636f6c6f722b307831372e383163342e616236380a202076657273696f6e2b5b3130372031372e3238342038372e3238302e3531322e3437302e3137355d0a2020776562736974652b275c56574a2abb6369da2836a763f4c1c35980270a20206c6963656e73652b2727e298950ac3a9c3a90a270a2020696d6167652b272720412969434e596f61403538642439646c6d616957405065783d447c4e2c7a3d402e5d55526e4f654d695b270a2020626173652b274c747e332f3c4370270a2020676c6f622d616d65732b5b7e646f7a7065722d7769637275742d6e696277796420307631657071672e6c673236752e64336861722e70336d6a652e6a616966735d0a3d3d
117+fuzz-1-100 | - | 71001e66c66c50f0fcb250fc706aa08464ca64ce01e089b720bc9917263d9e9a13a09b181d1bb390b1b1ac92beb222a59cbeb1a51bafb62a1db03993ab9ea6313d209130a6b4969e133f2f3b223693aeb23ba93da8b931a8b2313f67102ce0cfa5d195cd0206cbe8803afe68b00142c92d78c64b5ca0c36871c1 | 3a7e0a20207469746c652b2733633628787e59287e3835504232653267270a2020696e666f2b276f4178332f4c7a3c35274037313a366621636359257d65454a397d634b375e6d553a607326573d4d637a4022614c692d3d277e5e76446c265d6577527b5073635065637e4e270a2020636f6c6f722b3078380a202076657273696f6e2b5b3130372e3330382035302e3736312e36393920305d0a2020776562736974652b27925bf08c97b8270a20206c6963656e73652b27305a5c30270a2020736974652b2f0a3d3d
118+fuzz-1-101 | - | 7100822ebf98fc532f4ad9f3a221c0a5e2e294679ce8620342b77b809883fce8f09fbdbc807fc6f6260e0e0dc000f85674f0cebce2129fb7196ace2a6cdd808f46470707e0eb91124c088f989119dfcd4a0b99195a5b961e4d958dced14e1cdb52ded6160a93d01c1ecd8a120e5290d4924eda58948eda159a1313380b180f7e83de809193510269b96300ef3b13a92295a9b89b3ebc1892b33c39f06caa94 | 3a7e0a20207469746c652b27975f4cfea917a5ec79d110e0527171ca334e74b1270a2020696e666f2b27ddee270a2020636f6c6f722b307865352e656366662e383734372e653431630a202076657273696f6e2b5b39372e383037203535203331342e3233332e3930312e3435322e3434345d0a2020776562736974652b27772652452a5371377d783124677972270a20206c6963656e73652b2736554a270a2020626173652b2750270a2020676c6f622d687474702b5b27474a30213c6246667c372b2d6466686d597a3455363a473b716c4b795b5b284c427378342b4a384841524b3a6963513a6a57684e4c6027203076622e6c6d326c6a2e6a613336722e7075346e322e6e6a3766302e74326d76305d0a3d3d
119+fuzz-1-102 | - | 7100ad9e9bbaa2a4a3129627a23200ba7371fdd8dc2505c929d17cd5a584a1792dd9b44cf195f09864d598c1a58199fc94ac81399dd8bde535b999d1f5c1f0f418599924c180b9f084f9a4a130c9011721dc8705fcb934ba7206788c766292cc96007ce20a13c7ac04d15200194e15c7ac3c810111c7acd4d20251184ed552e082090b0351184e3d0111c7ac0c17660c709074f206aed3d9cd7300fe12e6d2ab14896362327842f7ded6df995637ba71e06842810001ad30aee1d493e154 | 3a7e0a20207469746c652b273d3775454947252c4f4425270a2020696e666f2b275c5c3f36774941724a345f752961685e4b362d537c253c26593566706960263f256b604e27766f794d6e66747d303c3d4656264930602e3c613e69284c32270a2020636f6c6f722b30783433652e653130380a202076657273696f6e2b5b342031203131392e3435322e3133332e3431382e3931322e3836312e3834325d0a2020776562736974652b270973e9558ac43131193ca17b6febef4cab1bdd387034a140270a20206c6963656e73652b275a615cc3a927c3a9270a2020696d6167652b275c61e29895205a0a20c3a9e29895273020e298955a5a200ac3a95a0a5c306161200ac3a92720e2989561270a2020736974652b2f2f463b3149660a3d3d
120+fuzz-1-103 | - | 71007e4c729850d4aef0ccc8b47ad4e450468e48e2fa82017cfcb29888d2f48cf284d401f629d569ca68cc5758c09f4ba32b5b02d08009c90f4e0a652dc4862425462a6b89a7a547262dae44a9074fcae52ae90448cc2d4886069c53ec704057710dc08f3866258c38662557180c08c870aa963866a50217e8a5ef6b2e | 3a7e0a20207469746c652b2726394c286a577866645a3d6a7228234724717d41270a2020696e666f2b277e594c44697a4679426a270a2020636f6c6f722b3078313061662e393864312e393464332e616135330a202076657273696f6e2b5b39203320312e3533312e3939342e3434302e3738392e3037355d0a2020776562736974652b27e2989530e298955c61302020c3a95ae298950a5c270a20206c6963656e73652b27a5ef6b2e270a2020696d6167652b274c487e7052286b213624293152594b3c2d3d326971254a3d78522e57492740622e270a2020736974652b2f0a3d3d
121+fuzz-1-104 | - | 710062b560b44eb860b860b44eb8b48653c5312bc5312b614e40b8c4312b0308143d593b140909b806d49ed5480bf8736974e500c150664175d0d11280282b7f7ced259e77c41f9bc636d20094088f1f8f69c0c6dd7cbb752d0d30a0676850e6b499df056aec04e88ca853b535c3e62e86e9016861b383e2daea1359316103bb4392114bb2e12a01fd8b5ecc9b598fd30d | 3a7e0a20207469746c652b275a305a275c305c305a275c5ac3a9e29895e298953027205ce29895270a2020696e666f2b27454fd60e454202ae270a2020636f6c6f722b30783239312e616233640a202076657273696f6e2b5b3836372030203237352e3639362e3635382e3435342e3432372e3536312e3432312e3334375d0a2020776562736974652b276c76505c5b7d222b266c607748326249365c25270a20206c6963656e73652b272f7a316f663d4e37270a2020696d6167652b272b7f7ced259e77c41f9bc636d294088f1f8f69c0c6dd7cbb752d0d30a0676850e6b499df270a2020736974652b2f439905d52f230a3d3d
122+fuzz-1-105 | - | 71706a0044a490c4d7369d9fefafd9c72bf91d2c6a161451f6067cdc6734c30db535d1c217654025ed1476291b75cf3ff651287323230de205fcb934ba728007840b864b1b10c400033465422a60be25009f6cde8846e066fa80ea4c7e40c8885650da8ef278b462fcb0faa25a5e50e24644c68c5280deb4d6e25c9605145d9a03c2c93de0cf3cd886d5f3938806e0a1c78bcb6f272a8deb6904ec09880c47680d1ae604 | 3a7e0a20207469746c652b2754270a2020696e666f2b271492f8daa6f3f3fd35fb7825bf8345cd8222cade808ffb8c66b8a1b6265af8a20ca8a49dc22e65a3eef9c73e0a656e64a4270a2020636f6c6f722b3078650a202076657273696f6e2b5b3231302e3130362036332e32373120352e3834312e3837302e3733322e3037312e3230382e3835375d0a2020776562736974652b273d5e5c7e3b51695c4f23604f406438426b270a20206c6963656e73652b273027270a2020696d6167652b27366f442370337d4075263f2064442b286d47793c5a317e587d512d2f28712322634629406f5a6b712e4b270a2020736974652b2f205c305c5a2f62802f65422a603e0a3d3d
123+fuzz-1-106 | - | 7100dac45ea0ae667c9c72b28ed064d8cee6e6deb0a4b6dce4c00068ba72c5d4773722a5562706d3e35696624456b7d2269216d51647c60425e746e695b71263a78462b734163344765317a6466327c7d767017f2e8dae6c09d051380903810111c7ac040bb281110e08995946f280be03b45c0a0ac3a92027a03fe912ea5ab3d2c301 | 3a7e0a20207469746c652b27622f5057333e4e39594768326c6773736f58525b6e7220270a2020696e666f2b272b574c7d7723526a7562303d6e65294664752b6d2269516d71644c50726e645e792b31764a28764b633143643775616a3476727c3d270a2020636f6c6f722b3078300a202076657273696f6e2b5b313120343530203234352e3436382e3030332e3234392e3335365d0a2020776562736974652b275c0a0ac3a92027270a20206c6963656e73652b27275d425d6b567a38270a2020696d6167652b270a2761203020e2989520270a2020736974652b2f0a3d3d
124+fuzz-1-107 | - | 71005ef2a48cc85cacecb44680fadc9c7aa64260fccc01a0b5301005101071cccae15490e1d4b03071cc4a2e71ccca931371cc4a180505101885e154051071cc4a71cccae1d413102d180590131871cc4a71cc4a71cc4a2eae93132d71cc4a71cc4a1071cc4a2e2d2e71ccca302e2eae93e154302008a116f0e7d2e8ca8187d15a0248e2e2d2020151d00a0be30a535010c7ac84e11a4ed5021f670d98a726f141cbfa0151a4da1c10e4253a2c02 | 3a7e0a20207469746c652b27795246642e56765a23407d6e4e3d5321307e66270a2020696e666f2b2761200a2020e29895c3a920c3a96161e298955ce298952727e29895300a0a20300ac3a90a20e29895e29895c3a927205a300a202730e29895e29895e298955c5c27275ae29895e2989520e298955c5a5ce29895615c5c5c27c3a920270a2020636f6c6f722b3078613834320a202076657273696f6e2b5b32322e3132392033322e3538312e3238372031362e3035305d0a2020776562736974652b2722d5e6270a20206c6963656e73652b2779890e8b270a2020696d6167652b275c5c5a20200a5a61615c610a0ae29895305cc3a90a270a2020736974652b2f305a0a3d3d
125+fuzz-1-108 | - | 7180e72b85ec6b19009e2020a56ea1f505d271447b26da14351259e39587c1e808c602fcb70a36ea0107da38613f72bb72e9f20c0c6fffdff8af347daa17b42b8fad48ee8c4c6edda72b6f239cb573066c2c1c3e5bc03f637b1307d706e0078f8a9031631e798e5a79061b2ba7becd144ee81bf0d1e8e8e000f45d4f9a1c9fd2ce4c44a3cae34946d06f32e6ece35d92a23ef705aab754ac2edf7645ea6d90c429cd0b3e98c3047afd2abed7e229179be7194b000fe298955c30e298952030e298955c300ae102ebe50392c89802e26d9275a40c28d800fc3e4a668274d8dc9bb4f653d92e9d078118125110f96005021065785881e887180c | 3a7e0a20207469746c652b275f29645f4b270a2020696e666f2b2704a4d42db4be403a8e68cf449ba246226bbcf230181dc15880ff56c1463de0401b27ec476e572e5d9e81e1edff1bff95a64ff58276e5b115c99d91c9adfb74e56d84b3764e270a2020636f6c6f722b3078313637632e333835380a202076657273696f6e2b5b372e373734203638302e3733382034392e3632372e3636352e3432382e3834392e3838335d0a2020776562736974652b278f9299201d36f726adfd54b64be74120864414443e588144191e56270a20206c6963656e73652b277e88c1270a2020696d6167652b27e298955c30e298952030e298955c300a61270a2020626173652b275a270a2020676c6f622d687474702b5b27eb4993e353da99896854793cc908fa4dc69c7dbc4b52d4e7be40f5968ad5e5dbae48bd0d9238a579c107739840af5fc5f75a3ce562f33c232720307662742e313731396a2e64756b736c2e686d316a702e62613737692e376a3336362e38386c336f5d0a3d3d
126+fuzz-1-109 | - | 71003fb0ad19b39da02421ba3c13640068200c466b38f5e4441cb3324c1cb3324c818b2b6c38354c411cb312862b4c414b1cb3928b4b1cb3520be484eb04e4442b8c0b44ebe444411cb312442b4c0b4681eb0c6021d578759560edd5c20bf8676c6fe2003e46306b3b737e606550fa0024676fb8ff23ffe9138380de28e89a2e5c968af4c493d7d61e1eddf11367c047a3a38303a005ad302dae93e15471cc4a1098e1d4e154051090e15471cc4a71cccae154903071cc4aaee15418100571cc4a71cc4aade154ad1371cc4a058593e15471cc4a101898b0b3047861bb8579a49055e142cba5cb800b0fdb5c80c0449888e44fac8a72006f1871cccae1d41398e15471cc4a052d3c0c | 3a7e0a20207469746c652b27605b33663b41494274792648270a2020696e666f2b2761305ac3a92727e2989561e29895610a5c5c61c3a9610ae29895305c610a5ae298955c5ce298955a20275c2720275a615c205a27270ae29895205a615a300a5c27270a2020636f6c6f722b307831632e326435652e643630392e353735372e386435320a202076657273696f6e2b5b302039372e3336362e3235362031342e3536372e3635302e3833312e3237362e3037332e3233355d0a2020776562736974652b2730e29895c3a92730c3a9e298950a5a270a20206c6963656e73652b2761270a2020696d6167652b27d86e611e296455b8d07269270a2020626173652b2746306b3b737e6065507a270a2020676c6f622d687474702b5b275a615a5c27c3a9e298952030c3a9c3a90a2020c3a9e29895e29895c3a92061e298955cc3a930200ae29895e298955ac3a95a27e298950a0a27c3a9e29895203030612727203076342e733976336e2e38753372622e64663475342e75693539632e6e31656a622e6b32686e6b2e30676339752e6a767033762e75733675705d0a3d3d
127+fuzz-1-110 | - | 7100c4e1e23a090b5308d31a4ed512c7ace482194e15c7ac14c7ac1c4e85194e1d4e15c7ac8409d31a4e0d80d4613061205ae29895e298955ac3a95ce2989520202730c3a9615ae298955c6130c3a9305c205c20205ce298955c0a0a2720c3a95a615a5cc3a9301ae038b1fc1b6501ff8ced4d1ca0c393794a619303f11df0d1e8e8e0003ef9d0e080d066a2ec6e9e926260c8f6bae4b8fcd8a4a6fa6c86b459605779a04555405c87521c60fbaa1d80e7b9a113921d2b219f3b2c2625aa22313b10a19b2110ba1499391a3f2e35406348ea2de965652dce05 | 3a7e0a20207469746c652b275c5c2761610a615ac3a95ae298955c30c3a9e29895e29895c3a930c3a9c3a9e2989530615ac3a9270a2020696e666f2b27613061205ae29895e298955ac3a95ce2989520202730c3a9615ae298955c6130c3a9305c205c20205ce298955c0a0a2720c3a95a615a5cc3a9300a270a2020636f6c6f722b3078342e613337662e393632370a202076657273696f6e2b5b372e3333392031302e3931342036312e3535342e3734312e3137312e3532392e3834355d0a2020776562736974652b27734327243b56423e77584c4a54456276204237432074293273347e5c6a270a20206c6963656e73652b2743526f492f2b6b712e270a2020626173652b2778324f296c32270a2020676c6f622d687474702b5b277c68704068335176374f493130647b5d725c7e6c52537d36435a2720307636765d0a3d3d
128+fuzz-1-111 | - | 71009ac15c7c6272ba82a49ae2c65896f6d04074f67082c46c56eeba5e44f2e400785989e8817367d930da500f25a6759d943e2e34bb6391c1944b9ca23aaabb82a148949f5898d2f44a5417aefdf227180fdca7f00f70667ccea929a4b5803f9746570e6e0ef048b4c9c91e2dc183000ba45207758118551b1d80562eb7b7983d1cbcba1316b8b23632272796aea2253d112c911b901320f281f86795715de1690a | 3a7e0a20207469746c652b27602e3e31395d41524d71632c4b7b68203a7b384162362b775d2f227932270a2020696e666f2b2795881e3877960da30df550625ad749e9e342b33b16194cb9c429aaa3ba2b188a44f98985294daf4475e1da2f7f82f1c07d0a7f270a2020636f6c6f722b3078352e363930612e366137332e396631390a202076657273696f6e2b5b332e3430322034362031332e3734332e3434315d0a2020776562736974652b275c6e6f317b387875272c70656d644e4e2c5d454b7a225822372027270a20206c6963656e73652b271f887f5619d7159ea6270a2020696d6167652b272040270a2020736974652b2f662f126d72b2470a3d3d
129+fuzz-1-112 | - | 7100ef3071cc4a052d85e15490b093e1546ec00f8381d106c66801ff8ced4d1ca01d4e850983510003911cf0c3dacae680b0cc0da600de360b48d60d1ca080645bc1136023450db0a4b950cfb35e03347a7269303ee0e12a7281ba02 | 3a7e0a20207469746c652b2761e298950a5a0ac3a9206127c3a95c270a2020696e666f2b276130305a270a2020636f6c6f722b30783134360a202076657273696f6e2b5b3738392e3937342031302e3630362e3034362e333532203130332e3530392e3037332e3432372e3335332e3137352e3131382e3933305d0a2020776562736974652b277a7269303e270a20206c6963656e73652b275c452e5057270a2020626173652b27c3a93061300a20270a2020676c6f622d616d65732b5b7e6269746c79722d646f636865702d736f7673757220307636395d0a3d3d
130+fuzz-1-113 | - | 71001ab54e8653c5312b616040c2c4312b4fc286534fc2b44060b8ce8097f6b40cf09738884e6ea20f6b0bf8736974e58018a70a6c16c8ff0ea60121b1e6b3eedd7180cec5dd7b276d04ba1a9e8d09 | 3a7e0a20207469746c652b275a27c3a9e2989530302061e298952761c3a927615a20305c27270a2020696e666f2b27694f4b270a2020636f6c6f722b3078356163332e653839622e393361322e306532350a202076657273696f6e2b5b332e353833203236203231392e3331372e3034392e3230332e3339365d0a2020776562736974652b272eeede3b6923270a20206c6963656e73652b276a783626270a2020736974652b2f71aac00a3d3d
131+fuzz-1-114 | - | 71003ef57488d0daeeaceed8826ca690409ea45e48fa7e6e6c868cc6fc0108e85d424f9f8daf1bdc02e55740776ef20e60915767338aa1dd62b50bf8736974e5e0624b0b28d40b06bc67252970e0c21a54067f4306 | 3a7e0a20207469746c652b277a3a44686d7756776c41365348204f522f247d3f37364346637e270a2020696e666f2b27bd4be8e9b3f175835ba0fc0ae8ce4dde270a2020636f6c6f722b307831622e353632642e646131382e613333362e373537390a202076657273696f6e2b5b3133342e31303020352e3339332e313033203335322e3632345d0a2020776562736974652b2732270a20206c6963656e73652b273764270a2020696d6167652b27270a2020736974652b2f620a3d3d
132+fuzz-1-115 | - | 71009fbb93b0143a3aa7b5226e00bc1b4e15c7ac3cd1e2e20a83194e15c7ac04e112c7ac3c09e3525000d15210c7ac5438d10ae3e2e20a3b51d03a8139e10604e6c702fe19db9b38e0c33d2181030821ba1e50329cfceaf50807fcb0b6b239c080089ab0e1602c99750920aa60bfa70929e2517380c0cd19c7563b880d45861cbc90311007afba03a2386625203e94e7d4c6447006 | 3a7e0a20207469746c652b277727612974744e6b455c270a2020696e666f2b27c3a9e29895275a5c5c6130c3a9e29895205ce2989527615c0a0a205a0ae298950a275a615c5c5c61270a5a2730275c270a2020636f6c6f722b3078386663630a202076657273696f6e2b5b3220312031342e3530352e3835395d0a2020776562736974652b27e29895270a20206c6963656e73652b2787f29cda9808ce270a2020696d6167652b2705fb3d4d48118f9a03046ece38b6da416c2832e4e0270a2020626173652b27704f4860270a2020676c6f622d616d65732b5b7e646f7a6e65632d2d6261747275702d7769636c65742d73616274796e2d6d6f707375622030766f2e38756e6c662e70373169612e3066626b385d0a3d3d
133+fuzz-1-116 | - | 7100bfada81e241f3fbc2b1a961a74006170ad20055431024c90360f2ed0c1d005fcb934ba72c01b9dcda659a045d6802418950093018d29fc22410666b90104c5312b41c24e8653b9608653016f2dd9f5991c961e | 3a7e0a20207469746c652b275b513d483e7e7857342c3568270a2020696e666f2b27e05a410aa8620498206d1e5ca083270a2020636f6c6f722b3078330a202076657273696f6e2b5b31312e303432203631302e36393020362e3833392e3939382e3537372e3339332e3032322e3437342e3735355d0a2020776562736974652b27e29895206127c3a95c30c3a9270a20206c6963656e73652b27b564d76772587a270a2020736974652b2fa3b3592f0a3d3d
134+fuzz-1-117 | - | 71009b30b2da0362c84478a519009ee10229cb7109b31bd292b9e1e2795183898b3b53ca39baa2aa92ab29cbdafba11b891ae9d3a9b183c98a5b13dbe1ebf32bca32bb11cb19b2a9091b4b11118b0a1ab9291a1b7aab424b693a19a307c804b042d91cbd807fc6f6260e046f80dacd288902e82aee808f46470707401b2720272730e2989527615cc3a9613020306161c3a95ce2989530c3a90a275c0a305c610a0ae298955a305ce29895615cc3a95a270a0a5cc3a927e2989520c3a961e29895c3a96709c02326392a3c143ab912a538343d1a229e3d98199030352cbfb7be913a1d2b9a2f96361620a89d3bad946a814376c0d82b0fa89dfbe241c92b170e40880fac053e5eef9a0e30310d337418d74688b6 | 3a7e0a20207469746c652b276164b507c49089f04a13270a2020696e666f2b275c2065392e6176435a32375c3c2f6a307171674a3947575455723565597b3f742351237d3a35763059716b623b7c7d7e4559663762394336356163292262514123374563436f5568294d276374270a2020636f6c6f722b30786538652e366361312e353830320a202076657273696f6e2b5b312e373336203132342e323833203231392e3433332e3831362e3034322e3836342e3037385d0a2020776562736974652b273eb016f878bd6b3ac0c434ccd0615c1b21da270a20206c6963656e73652b27270a2020696d6167652b274c725478287472254a71687a34443c7b303320616a587e6f7d23753a56345f2c6d2c40503b775a2955270a2020626173652b27bc270a2020676c6f622d687474702b5b272720272730e2989527615cc3a9613020306161c3a95ce2989530c3a90a275c0a305c610a0ae298955a305ce29895615cc3a95a270a0a5cc3a927e2989520c3a961e29895c3a9272720307636732e616e3830612e3469686a655d0a3d3d
135+fuzz-1-118 | - | 71007ee0c4f0626666a2deb4f2b8b6d09e824cb8a0a2e201107e387538558b2b4c6b38954b1cb3324c2bec442b0c460b448b4b0b06441cb3120444812b0c268c4b0b86eb44210c4401461cb31206461cb3521cb312643835d82ee0cfa5d1952d01047faf1c08cca5e59cfed65e96fc3a6988bc1fceaa6dfd31b9830b8c0e17d8701bc401a7c2adbf7fb5ae03f8b7e1e4a1bdcd4891509dc0ecad51750d01dfc9944a5aca19 | 3a7e0a20207469746c652b27706278313333516f5a795c5b684f41265c505171270a2020696e666f2b27c3a9c3a95a5c615ac3a95ce29895615a61275a61305a205a5c5a3020e2989520200a5c6130615c5a305c270a61200a30e298953030e29895e2989520c3a9270a2020636f6c6f722b307831620a202076657273696f6e2b5b352e38353720313420312e3839342e3237382e3132392e3137302e3138365d0a2020776562736974652b276d3879686f3352245427307b6b545d43270a20206c6963656e73652b2727532a692967270a2020696d6167652b27df2b0702736939a7bfb59725bf4e1a22ef87b36a5b7f4ceee002a3c3270a2020736974652b2f0a3d3d
136+fuzz-1-119 | - | 71a015ce00105b98386665188802880288020c973866a596021717c8499802d770aa3866250ccc09080c9716c8702a4858d8098842983866a55618d7702a97020817088c1688c2c909ccc94998c270aa3866e5094c18973866a5160cd770aa386665401ecdfcc802fe19db9b3810b001c8219671c0a7dc301d4930d1ef25079ec57888ede1808f464707077c413d203055c42c648023468383e98015ecff85d6e13e06687b70f85644786601 | 3a7e0a20207469746c652b270a27270a2020696e666f2b2761e2989561200a200a200a305ce298955a0a5c5c2027610a5cc3a9e2989530302720305c5a20c3a920616127200a61e298955a615cc3a95c0a205c20305a200a2727302727610ac3a9e298952730615ce298955a305cc3a9e29895270a2020636f6c6f722b30783931662e393961330a202076657273696f6e2b5b322033312e3334312e3638312e32303120352e3035362e3731372e3132382e3732382e3431365d0a2020776562736974652b273d387c2b223c33270a20206c6963656e73652b27270a2020626173652b27b0270a2020676c6f622d687474702b5b27413d203055442720307631672e757232336f2e6f6d6630652e39666671342e6f346937392e6772696a732e3073636d345d0a3d3d
jammer/tests/vectors/marks.tsvnew file+233
@@ -0,0 +1,233 @@
1+# Generated with `urbit eval` (vere 4.6) by running each mark's real grow:mime arm on the sample.
2+# mark sample_hoon_expr sample_jam_le_hex mount_file_hex mount_file_escaped
3+atom 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
4+atom '' 02 ""
5+atom 'a\0a' 20c314 610a "a\n"
6+atom `@t`0x100 600002 0001 "\x00\x01"
7+atom 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
8+atom `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
9+css 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
10+css '' 02 ""
11+css 'a\0a' 20c314 610a "a\n"
12+css `@t`0x100 600002 0001 "\x00\x01"
13+css 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
14+css `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
15+hoon 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
16+hoon '' 02 ""
17+hoon 'a\0a' 20c314 610a "a\n"
18+hoon `@t`0x100 600002 0001 "\x00\x01"
19+hoon 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
20+hoon `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
21+html 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
22+html '' 02 ""
23+html 'a\0a' 20c314 610a "a\n"
24+html `@t`0x100 600002 0001 "\x00\x01"
25+html 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
26+html `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
27+js 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
28+js '' 02 ""
29+js 'a\0a' 20c314 610a "a\n"
30+js `@t`0x100 600002 0001 "\x00\x01"
31+js 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
32+js `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
33+png 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
34+png '' 02 ""
35+png 'a\0a' 20c314 610a "a\n"
36+png `@t`0x100 600002 0001 "\x00\x01"
37+png 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
38+png `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
39+svg 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
40+svg '' 02 ""
41+svg 'a\0a' 20c314 610a "a\n"
42+svg `@t`0x100 600002 0001 "\x00\x01"
43+svg 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
44+svg `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
45+udon 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
46+udon '' 02 ""
47+udon 'a\0a' 20c314 610a "a\n"
48+udon `@t`0x100 600002 0001 "\x00\x01"
49+udon 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
50+udon `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
51+umd 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
52+umd '' 02 ""
53+umd 'a\0a' 20c314 610a "a\n"
54+umd `@t`0x100 600002 0001 "\x00\x01"
55+umd 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
56+umd `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
57+xml 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
58+xml '' 02 ""
59+xml 'a\0a' 20c314 610a "a\n"
60+xml `@t`0x100 600002 0001 "\x00\x01"
61+xml 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
62+xml `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
63+ico 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
64+ico '' 02 ""
65+ico 'a\0a' 20c314 610a "a\n"
66+ico `@t`0x100 600002 0001 "\x00\x01"
67+ico 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
68+ico `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
69+jam 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
70+jam '' 02 ""
71+jam 'a\0a' 20c314 610a "a\n"
72+jam `@t`0x100 600002 0001 "\x00\x01"
73+jam 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
74+jam `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
75+map 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
76+map '' 02 ""
77+map 'a\0a' 20c314 610a "a\n"
78+map `@t`0x100 600002 0001 "\x00\x01"
79+map 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
80+map `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
81+helm-hi 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
82+helm-hi '' 02 ""
83+helm-hi 'a\0a' 20c314 610a "a\n"
84+helm-hi `@t`0x100 600002 0001 "\x00\x01"
85+helm-hi 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
86+helm-hi `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
87+htm 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
88+htm '' 02 ""
89+htm 'a\0a' 20c314 610a "a\n"
90+htm `@t`0x100 600002 0001 "\x00\x01"
91+htm 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
92+htm `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
93+xhtml 'hello' 8007ad8c8ded0d 68656c6c6f "hello"
94+xhtml '' 02 ""
95+xhtml 'a\0a' 20c314 610a "a\n"
96+xhtml `@t`0x100 600002 0001 "\x00\x01"
97+xhtml 'ünï ✓' 0091615eb7e1571071ce49 c3bc6ec3af20e29c93 "\xc3\xbcn\xc3\xaf \xe2\x9c\x93"
98+xhtml `@t`(bex 200) 00260100000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000001 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
99+wasm [5 'abc'] e1022f4c6c0c 6162630000 "abc\x00\x00"
100+wasm [1 'abc'] 31f0c2c4c6 61 "a"
101+wasm [0 0] 29 ""
102+wasm [3 'abc'] a18117263606 616263 "abc"
103+wasm [2 0x100] 21c10004 0001 "\x00\x01"
104+wasm [0 'abc'] 09bc30b131 ""
105+woff2 [5 'abc'] e1022f4c6c0c 6162630000 "abc\x00\x00"
106+woff2 [1 'abc'] 31f0c2c4c6 61 "a"
107+woff2 [0 0] 29 ""
108+woff2 [3 'abc'] a18117263606 616263 "abc"
109+woff2 [2 0x100] 21c10004 0001 "\x00\x01"
110+txt ~ 02 ""
111+txt ~[''] 29 ""
112+txt ~['a'] c1c302 61 "a"
113+txt ~['a' ''] c1c329 610a "a\n"
114+txt ~['a' '' ''] c1c39902 610a0a "a\n\n"
115+txt ~['' ''] 9902 0a "\n"
116+txt ~['a' 'b'] c1c3c1c502 610a62 "a\nb"
117+txt ~['a\0d' 'b'] 810cebe06201 610d0a62 "a\x0d\nb"
118+txt ~['a\0ab'] 012f4c412c 610a62 "a\nb"
119+txt ~[`@t`0x100 'x'] 8101181c2f 00010a78 "\x00\x01\nx"
120+txt ~['é' '✓' ''] 01613835208a734ea6 c3a90ae29c930a "\xc3\xa9\n\xe2\x9c\x93\n"
121+md ~ 02 ""
122+md ~['a' ''] c1c329 610a "a\n"
123+md ~['# T' '' 'body'] 016f04849a015fec8d2c2f 2320540a0a626f6479 "# T\n\nbody"
124+pem ~ 02 ""
125+pem ~['-----BEGIN-----' 'AAAA' '-----END-----' ''] 01b45b5a5a5a5a848a8e929c5a5a5a5ada801f1414140ca0d9d2d2d2d252e444d4d2d2d2d2a6 2d2d2d2d2d424547494e2d2d2d2d2d0a414141410a2d2d2d2d2d454e442d2d2d2d2d0a "-----BEGIN-----\nAAAA\n-----END-----\n"
126+noun 0 02 02 "\x02"
127+noun 1 0c 0c "\x0c"
128+noun ~ 02 02 "\x02"
129+noun [1 2] 3112 3112 "1\x12"
130+noun 'hello' 8007ad8c8ded0d 8007ad8c8ded0d "\x80\x07\xad\x8c\x8d\xed\x0d"
131+noun [%a [%b ~]] c1c3c1c502 c1c3c1c502 "\xc1\xc3\xc1\xc5\x02"
132+noun [[1 2] 3] c54834 c54834 "\xc5H4"
133+noun [[1 2] [1 2]] c5c849 c5c849 "\xc5\xc8I"
134+noun [0x1.0000.0000.0000.0000 0x1.0000.0000.0000.0000] 010c00000000000000004e02 010c00000000000000004e02 "\x01\x0c\x00\x00\x00\x00\x00\x00\x00\x00N\x02"
135+noun [(bex 70) (bex 70) (bex 70)] 013c0000000000000000804d4e02 013c0000000000000000804d4e02 "\x01<\x00\x00\x00\x00\x00\x00\x00\x00\x80MN\x02"
136+noun [[1 1] 1 [1 1] 1 [1 1]] c51c37194f02 c51c37194f02 "\xc5\x1c7\x19O\x02"
137+noun ~[%a %b %a %b %a] c1c3c1c54d36269b14 c1c3c1c54d36269b14 "\xc1\xc3\xc1\xc5M6&\x9b\x14"
138+noun-autodiff [[1 2] [1 2]] c5c849 c5c849 "\xc5\xc8I"
139+noun-autodiff [%x %x %x] c1f14d4e02 c1f14d4e02 "\xc1\xf1MN\x02"
140+json ~ 02 6e756c6c "null"
141+json [%b &] c1c502 74727565 "true"
142+json [%b |] c1c50c 66616c7365 "false"
143+json [%n '42'] c1dda06964 3432 "42"
144+json [%n '-1.5e10'] c1dd80b625c6a5a62c0606 2d312e35653130 "-1.5e10"
145+json [%s ''] c1e702 2222 "\"\""
146+json [%s 'quote" back\\ slash/'] c1e7005ae2eadee8ca4440c4c2c6d6b840e6d8c2e6d05e 2271756f74655c22206261636b5c5c20736c6173682f22 "\"quote\\\" back\\\\ slash/\""
147+json [%s 'nl\0a cr\0d tab\09 nul\00 us\1f del\7f sp '] c1e700baddd81440c6e41a40e8c2c41240dcead80040eae63e40c8cad8fe40e6e040 226e6c5c6e2063725c7530303064207461625c7530303039206e756c5c75303030302075735c75303031662064656c5c75303037662073702022 "\"nl\\n cr\\u000d tab\\u0009 nul\\u0000 us\\u001f del\\u007f sp \""
148+json [%s '<a href="x">&amp;</a>'] c1e7009a78c240d0e4cacc7a44f0447c4cc2dae076785ec27c 223c6120687265663d5c22785c223e26616d703b3c2f613e22 "\"<a href=\\\"x\\\">&amp;</a>\""
149+json [%s 'héllo ✓ 𝄞'] c1e70071b4e15436b6371071ce4910f84e424f 2268c3a96c6c6f20e29c9320f09d849e22 "\"h\xc3\xa9llo \xe2\x9c\x93 \xf0\x9d\x84\x9e\""
150+json [%s `@t`0xff.fe80] c1e74004f4ff07 2280feff22 "\"\x80\xfe\xff\""
151+json [%a ~] c1c302 5b5d "[]"
152+json [%a ~[~ [%b &] [%n '1'] [%s 'x'] [%a ~] [%o ~]]] c1c35970b1057743e382cfe1f89ab4e06f05 5b6e756c6c2c747275652c312c2278222c5b5d2c7b7d5d "[null,true,1,\"x\",[],{}]"
153+json [%o ~] c1df02 7b7d "{}"
154+json (pairs:enjs:format ~[['a' [%n '1']] ['b' [%n '2']] ['c' [%n '3']]]) c1df0517077743e52cf8b851123a57f070a324344e05 7b2263223a332c2261223a312c2262223a327d "{\"c\":3,\"a\":1,\"b\":2}"
155+json (pairs:enjs:format ~[['title' [%s 'Groups']] ['info' [%s 'chat']] ['color' [%s '0xee.5432']] ['version' [%s '1.2.3']] ['website' [%s 'https://tlon.io']] ['license' [%s 'MIT']]]) c1df0578e8d2e8d8cac1e780ef48eead0e6e5e01debb32b1b934ba72f039c01b8d8e0e6e4ee7e5858eedcdcd25ed5d019eb137b63779f039400306afaccca5866646ae809fe666f60e3e07fe185a183db5002f5bda5899db5c39f81c784d49d40af0da95c9cda5bdb983cf014d8c8b8ccb3c549f0a 7b227469746c65223a2247726f757073222c226c6963656e7365223a224d4954222c2276657273696f6e223a22312e322e33222c2277656273697465223a2268747470733a2f2f746c6f6e2e696f222c22636f6c6f72223a22307865652e35343332222c22696e666f223a2263686174227d "{\"title\":\"Groups\",\"license\":\"MIT\",\"version\":\"1.2.3\",\"website\":\"https://tlon.io\",\"color\":\"0xee.5432\",\"info\":\"chat\"}"
156+json [%o (my ~[['key\0a"' [%s 'v']]])] c1df0568d6caf214c4e073b83d05 7b226b65795c6e5c22223a2276227d "{\"key\\n\\\"\":\"v\"}"
157+json (pairs:enjs:format (turn (gulf 1 40) |=(i=@ [(crip ((d-co:co 1) i)) [%n (crip ((d-co:co 1) (mul i i)))]]))) c1df05a583bb41c7d815681ab8d111e831363070055ac66e74049acd8d5d41e74647685e819ec11b1d811e434343a7053a066e74045a0c0c5c819e891b1d8196631357a067e04647a0e5c0c015b46e74042d530f23b740cfd08d8e408f89a9b12bd03270a323d06860e061e46a41ed4647d03376053a666e74045a8c4d5e8196891b1d814643136741e34647683c8c5c6ba8c31b1d811e2393630f23b740c7d08d8e408bc9b1b340cfdc8d8e408f99b1c92bd03279a323d07068e20ab40cdee808b41b1cba82e28d8ea06de861e46a819e991b1d811e038393678196991b1d815623930f23b740cfd48d8e408f9191a92bd03379a323d0636a64e20ab4ccdde808b41b993c6ba8411b1d816623530f23b740c7dc8d8e4093c1c92bd03179a323d0666ce261e416f46e74044d930f23b740c7c88d8e408ba1a12bd03175a323d06464ea61e45aa065e44647a0d1e0d059a067e44647a0c7c0c8d059d0bcd111344e5c8196a11b1d815673635750b9d1718c0e23d7021d83373a026d4686ae46e34647a0c9d4d815e898b8d111683132f1307215 7b223138223a3332342c223136223a3235362c223131223a3132312c2239223a38312c223234223a3537362c2232223a342c223332223a313032342c223232223a3438342c2234223a31362c223132223a3134342c223135223a3232352c223335223a313232352c2237223a34392c223137223a3238392c223139223a3336312c223339223a313532312c223235223a3632352c223237223a3732392c223430223a313630302c223233223a3532392c223333223a313038392c223337223a313336392c223239223a3834312c223238223a3738342c2238223a36342c223134223a3139362c223236223a3637362c223336223a313239362c2236223a33362c223133223a3136392c2231223a312c223231223a3434312c223130223a3130302c223331223a3936312c223334223a313135362c223230223a3430302c223330223a3930302c2235223a32352c2233223a392c223338223a313434347d "{\"18\":324,\"16\":256,\"11\":121,\"9\":81,\"24\":576,\"2\":4,\"32\":1024,\"22\":484,\"4\":16,\"12\":144,\"15\":225,\"35\":1225,\"7\":49,\"17\":289,\"19\":361,\"39\":1521,\"25\":625,\"27\":729,\"40\":1600,\"23\":529,\"33\":1089,\"37\":1369,\"29\":841,\"28\":784,\"8\":64,\"14\":196,\"26\":676,\"36\":1296,\"6\":36,\"13\":169,\"1\":1,\"21\":441,\"10\":100,\"31\":961,\"34\":1156,\"20\":400,\"30\":900,\"5\":25,\"3\":9,\"38\":1444}"
158+json [%a (turn (gulf 1 5) |=(i=@ (pairs:enjs:format ~[['k' [%n (crip ((d-co:co 1) i))]] ['j' [%b &]]])))] c1c3057f177c1ddc0d8d2bb83ab8d8d41aa3356a366a43e5f1688dd11a351bb5a1f378b4c6688d9a8dda507a3c5a63b446cd466d683d1e15 5b7b226b223a312c226a223a747275657d2c7b226b223a322c226a223a747275657d2c7b226b223a332c226a223a747275657d2c7b226b223a342c226a223a747275657d2c7b226b223a352c226a223a747275657d5d "[{\"k\":1,\"j\":true},{\"k\":2,\"j\":true},{\"k\":3,\"j\":true},{\"k\":4,\"j\":true},{\"k\":5,\"j\":true}]"
159+kelvin [%zuse 408] 015faf6eae0c0633 5b257a757365203430385d0a "[%zuse 408]\n"
160+kelvin [%zuse 1.000] 015faf6eae0c0a7d 5b257a75736520312e3030305d0a "[%zuse 1.000]\n"
161+kelvin [%zuse 0] 015faf6eae2c 5b257a75736520305d0a "[%zuse 0]\n"
162+kelvin [%$ 7] 890f 5b2520375d0a "[% 7]\n"
163+kelvin [[%1 ~] (sy ~[[%zuse 408] [%zuse 409]])] c516f0f5eae6ca6030678d3898cc29 5b257a757365203430395d0a5b257a757365203430385d0a "[%zuse 409]\n[%zuse 408]\n"
164+kelvin [[%1 ~] (sy ~[[%zuse 408] [%zuse 409] [%lull 330] [%hoon 139] [%arvo 236]])] c516f0f5eae6ca603057c00f93b37b83604f6bc4c164ce023edadb9b1bc46216f0d9ead8d860940eb108 5b256172766f203233365d0a5b25686f6f6e203133395d0a5b256c756c6c203333305d0a5b257a757365203430395d0a5b257a757365203430385d0a "[%arvo 236]\n[%hoon 139]\n[%lull 330]\n[%zuse 409]\n[%zuse 408]\n"
165+kelvin [[%1 ~] (sy ~[[%zuse 412] [%zuse 411] [%zuse 410] [%zuse 409]])] c516f0f5eae6ca603257230e867335e26034a7d68883d93c8424 5b257a757365203431325d0a5b257a757365203431315d0a5b257a757365203431305d0a5b257a757365203430395d0a "[%zuse 412]\n[%zuse 411]\n[%zuse 410]\n[%zuse 409]\n"
166+kelvin [[%1 ~] (sy ~[[%zuse 408]])] c516f0f5eae6ca6030a7 5b257a757365203430385d0a "[%zuse 408]\n"
167+kelvin [[%1 ~] (sy ~[[%a 1] [%b 1] [%aa 1] [%ba 1] [%ab 1] [%z 5]])] c5165ccc1578b130a7053e4ccc155c8f5b810f0bf3b8d08287795c08 5b25616220315d0a5b25626120315d0a5b25616120315d0a5b257a20355d0a5b256220315d0a5b256120315d0a "[%ab 1]\n[%ba 1]\n[%aa 1]\n[%z 5]\n[%b 1]\n[%a 1]\n"
168+kelvin [[%1 ~] ~] c50a ""
169+kelvin [[%1 ~] (sy ~[[%zuse 1.234.567] [%zuse 99]])] c516f0f5eae6cac03ab49659230e1fa7 5b257a75736520312e3233342e3536375d0a5b257a7573652039395d0a "[%zuse 1.234.567]\n[%zuse 99]\n"
170+bill ~ 02 7e0a "~\n"
171+bill ~[%hood] 011feded8d2c 3a7e202025686f6f640a3d3d0a ":~ %hood\n==\n"
172+bill ~[%acme %azimuth %dbug] 013f6cacad1ce00dd34b6baba343077c32b1bab3 3a7e20202561636d650a2020202025617a696d7574680a2020202025646275670a3d3d0a ":~ %acme\n %azimuth\n %dbug\n==\n"
173+bill ~[%acme %azimuth %dbug %dojo %eth-watcher %hood %herm %lens %ping %spider] 013f6cacad1ce00dd34b6baba343077c32b1ba73c0277b537b07f02aa3436bb90ba31b432b93077cb4b73772c0472b936b077cb632b779c0874b733b07f8e6e0d2c8cae402 3a7e20202561636d650a2020202025617a696d7574680a2020202025646275670a2020202025646f6a6f0a20202020256574682d776174636865720a2020202025686f6f640a20202020256865726d0a20202020256c656e730a202020202570696e670a20202020257370696465720a3d3d0a ":~ %acme\n %azimuth\n %dbug\n %dojo\n %eth-watcher\n %hood\n %herm\n %lens\n %ping\n %spider\n==\n"
174+bill ~[%$] 29 3a7e2020250a3d3d0a ":~ %\n==\n"
175+bill (reap 30 %x) c1f14d36d964934d36d964934d36d964934d36d964934d36d964934d36d964934d36d964934d0a 3a7e202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a2020202025780a3d3d0a ":~ %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n %x\n==\n"
176+ship ~zod 02 7e7a6f64 "~zod"
177+ship ~fes 20fe01 7e666573 "~fes"
178+ship ~marzod 600002 7e6d61727a6f64 "~marzod"
179+ship ~fipfes 40f8ff07 7e666970666573 "~fipfes"
180+ship ~sampel-palnet c0ff89d70603 7e73616d70656c2d70616c6e6574 "~sampel-palnet"
181+ship `@p`0x1.0000 c0000008 7e6461706e65702d726f6e6d796c "~dapnep-ronmyl"
182+ship `@p`0xffff.ffff 80e0ffffff1f 7e646f737465632d72697366656e "~dostec-risfen"
183+ship `@p`0x1.0000.0000 800100000020 7e646f7a6e65632d646f7a7a6f642d646f7a7a6f64 "~doznec-dozzod-dozzod"
184+ship `@p`0x1234.5678.9abc.def0 801dde9b5713cf8a4602 7e646f707275742d706f7366656c2d746f727465722d6e6f63747578 "~doprut-posfel-torter-noctux"
185+ship `@p`0xffff.ffff.ffff.ffff 0081ffffffffffffff7f 7e6669706665732d6669706665732d646f737465632d72697366656e "~fipfes-fipfes-dostec-risfen"
186+ship `@p`(bex 64) 00030000000000000080 7e646f7a6e65632d2d646f7a7a6f642d646f7a7a6f642d646f7a7a6f642d646f7a7a6f64 "~doznec--dozzod-dozzod-dozzod-dozzod"
187+ship `@p`(dec (bex 128)) 0002feffffffffffffffffffffffffffffff01 7e6669706665732d6669706665732d6669706665732d6669706665732d2d6669706665732d6669706665732d6669706665732d666970666573 "~fipfes-fipfes-fipfes-fipfes--fipfes-fipfes-fipfes-fipfes"
188+ship `@p`(bex 100) 004b00000000000000000000000008 7e646f7a7279702d646f7a7a6f642d646f7a7a6f642d2d646f7a7a6f642d646f7a7a6f642d646f7a7a6f642d646f7a7a6f64 "~dozryp-dozzod-dozzod--dozzod-dozzod-dozzod-dozzod"
189+ship `@p`0xdead.beef 80e0ddb7d51b 7e62696c7479632d6d697466796c "~biltyc-mitfyl"
190+ship `@p`0x1.0001 c0080008 7e6d696c7279732d736f676c6563 "~milrys-soglec"
191+ship `@p`0xffff.0000 800000e0ff1f 7e6c6f646e79742d72616e727564 "~lodnyt-ranrud"
192+ship `@p`0xfffe.ffff 80e0ffdfff1f 7e6c697666796c2d626f72646566 "~livfyl-bordef"
193+story ~ 02 ""
194+story (~(put ju *story) `@uv`0 ['Title' 'Body']) 65011eaa343ab632f085dec8f2e9381a 636f6d6d69743a203076300a5469746c650a0a426f6479 "commit: 0v0\nTitle\n\nBody"
195+story (~(put ju *story) `@uv`0x1234 ['T' '']) 059646169c6a3aea02 636f6d6d69743a20307634686b0a540a "commit: 0v4hk\nT\n"
196+story (~(put ju *story) `@uv`0x1234 ['T' 'line1\0aline2\0a']) 059646169c0a90634b732b8b51604b732b93d174884b 636f6d6d69743a20307634686b0a540a0a6c696e65310a6c696e6532 "commit: 0v4hk\nT\n\nline1\nline2"
197+story (~(gas ju *story) ~[[`@uv`1 ['A' 'a']] [`@uv`1 ['B' 'b']]]) c5050fc2c3155c0817a78e2b01 636f6d6d69743a203076310a410a0a610a7c7c7c0a420a0a62 "commit: 0v1\nA\n\na\n|||\nB\n\nb"
198+story (~(gas ju *story) ~[[`@uv`1 ['A' 'a']] [`@uv`2 ['B' 'b']] [`@uv`3 ['C' 'c']]]) 852cb8102e4e2b2e78101e1e5d47d742177c081f8faea32b 636f6d6d69743a203076330a430a0a630a2d2d2d0a636f6d6d69743a203076320a420a0a620a2d2d2d0a636f6d6d69743a203076310a410a0a61 "commit: 0v3\nC\n\nc\n---\ncommit: 0v2\nB\n\nb\n---\ncommit: 0v1\nA\n\na"
199+story (~(put by *story) `@uv`5 ~) 856b0a ""
200+story (~(gas ju *story) ~[[(shax 'x') ['Fix bug' 'Details here.']]]) 05408096380ba15b1358a20031bed47d56997ae4a98758481ee26d8192c30b490da4c002bc46697820627567802651195d581adb1c085a995c999b0e431d 636f6d6d69743a20307631302e61383361392e31663170352e30626473382e6634676d342e376c3769372e6c36616d662e6e616273632e38306b39632e31366d74312e31637339640a466978206275670a0a44657461696c7320686572652e "commit: 0v10.a83a9.1f1p5.0bds8.f4gm4.7l7i7.l6amf.nabsc.80k9c.16mt1.1cs9d\nFix bug\n\nDetails here."
201+story (~(gas ju *story) (turn (gulf 1 12) |=(i=@ [`@uv`(shax i) [(crip ((d-co:co 1) i)) 'b\0a\0ac']]))) 054080f33d4dcdf4f185865edb7a0851b2f7cef340a88ebd3549d7c4f5024eaddba1ed82d6c017535018a7150001b683699301fec91bafb6ba4a8d7108024aecbb60fd7d489296ecd431c9aeb20d0b2a0f837218a9150001ce9f8d40f1fca9f5fb17bc74fe29900470a7f05d8b349f18285aa76396f8864d0b744c3c0cca61a456000458aa9778a129a22adef176d96594be1d8f0653b08e192e2cbd6616e7b92b2cd22c683c0cca61a45600045861a279a19a592f8f0cd5be039f88be25a9b36a41ba6a545e24d4352ee3e0c92d683e0cca61a46601f87f67eb0d095357cf1b72369ccc156c1a89cefbd9e778502eb005984edc244abe17e818791894c348ad000880f0d48e908bf7dad497a1466784ba8605e039b616d6112b1661aba5d9fa9f525cd0791894c3481d46aa05e05fb9e610cb5eda8f8d5e4a6e32efee14a91f31a7c336cf59bbeff8f703c00b3d5fd07b1894c348ad00fc97b7704231428d1cd134311eb4462e5b1b28f2d7fd8b49c28b81728e06227bc60b4a0f837218a9150001ceb0dc30f5a5fb40732d17817342df6992737271cb47507df13b8f0c118ad9ed0b6a0f837218a9c348550bc07f80ee5106f2c25b7a046ce429701489ad93bbb3a5530230d6fb236681b632d5ba40c7c0c3a01c466a1600017cd5aff3289faf6682d82f6a177ffd669b4e22922783b3ed625166311a8305350b8a0f837218a9c34815 636f6d6d69743a203076316d2e71336d74642e396f3166622e68366e39342e717272336c2e38383370732e74747469612e3434376c6d2e71756771322e76337436642e396b7576370a350a0a620a0a630a2d2d2d0a636f6d6d69743a20307631362e6b326f36362e68686370382e6d3572646a2e67636a70342e3869656a642e6a6671766f2e6e64386e742e68306a366c2e75666968732e7466716c750a380a0a620a0a630a2d2d2d0a636f6d6d69743a203076712e716b7062642e30623634662e74746363302e32616569722e3765736a6c2e6d346838732e313973686d2e30387569722e6f627030632e6b666567310a31300a0a620a0a630a2d2d2d0a636f6d6d69743a20307631312e6d706174692e3968716a6d2e39643469382e666e756d312e65766339382e31306773632e64396174622e64626f72702e37763033342e72396765720a320a0a620a0a630a2d2d2d0a636f6d6d69743a20307631392e6d336668352e6a336b74642e32673634762e3669356c722e73353765302e32393061662e75656975312e6675766c6c2e37756632672e34646a76370a31310a0a620a0a630a2d2d2d0a636f6d6d69743a203076752e6638626f302e31766675372e666e6463732e75646d336b2e736f6876612e386b74726e2e6a347269612e6271366f762e6d697570632e38656465610a370a0a620a0a630a2d2d2d0a636f6d6d69743a203076732e63757032302e71373735302e63626f39342e6f6e76656e2e75386b316d2e6d706538712e71317363392e6b713465382e71676868382e396f6266350a340a0a620a0a630a2d2d2d0a636f6d6d69743a20307631742e6e636f6b342e38636873742e76327661672e3876356e322e736a6a69392e6b7475676a2e6a673462692e71737130762e65697661632e36736d33370a360a0a620a0a630a2d2d2d0a636f6d6d69743a20307631362e6935676c722e6a706f6d632e7175696f622e677068716f2e3536316b662e336d7639382e70657065722e6f7473616c2e323536676e2e68357461620a310a0a620a0a630a2d2d2d0a636f6d6d69743a203076742e75393869642e6f6a6b6f302e6d6f32736b2e336f7376632e766e6a6b392e33396d31622e6a347336702e70316e6a712e6e616334672e72717237660a31320a0a620a0a630a2d2d2d0a636f6d6d69743a20307631682e393976756d2e706b6d6c6d2e32356862322e37623164642e68707330322e6f64656b342e63743361332e35756b72622e726f6e34342e65716a6f380a330a0a620a0a630a2d2d2d0a636f6d6d69743a20307631652e39733368692e7364656b342e68663538712e6c7138356c2e62376139352e6e713439752e307475716b2e36387562712e706a61676e2e6a386a31620a390a0a620a0a63 "commit: 0v1m.q3mtd.9o1fb.h6n94.qrr3l.883ps.tttia.447lm.qugq2.v3t6d.9kuv7\n5\n\nb\n\nc\n---\ncommit: 0v16.k2o66.hhcp8.m5rdj.gcjp4.8iejd.jfqvo.nd8nt.h0j6l.ufihs.tfqlu\n8\n\nb\n\nc\n---\ncommit: 0vq.qkpbd.0b64f.ttcc0.2aeir.7esjl.m4h8s.19shm.08uir.obp0c.kfeg1\n10\n\nb\n\nc\n---\ncommit: 0v11.mpati.9hqjm.9d4i8.fnum1.evc98.10gsc.d9atb.dborp.7v034.r9ger\n2\n\nb\n\nc\n---\ncommit: 0v19.m3fh5.j3ktd.2g64v.6i5lr.s57e0.290af.ueiu1.fuvll.7uf2g.4djv7\n11\n\nb\n\nc\n---\ncommit: 0vu.f8bo0.1vfu7.fndcs.udm3k.sohva.8ktrn.j4ria.bq6ov.miupc.8edea\n7\n\nb\n\nc\n---\ncommit: 0vs.cup20.q7750.cbo94.onven.u8k1m.mpe8q.q1sc9.kq4e8.qghh8.9obf5\n4\n\nb\n\nc\n---\ncommit: 0v1t.ncok4.8chst.v2vag.8v5n2.sjji9.ktugj.jg4bi.qsq0v.eivac.6sm37\n6\n\nb\n\nc\n---\ncommit: 0v16.i5glr.jpomc.quiob.gphqo.561kf.3mv98.peper.otsal.256gn.h5tab\n1\n\nb\n\nc\n---\ncommit: 0vt.u98id.ojko0.mo2sk.3osvc.vnjk9.39m1b.j4s6p.p1njq.nac4g.rqr7f\n12\n\nb\n\nc\n---\ncommit: 0v1h.99vum.pkmlm.25hb2.7b1dd.hps02.odek4.ct3a3.5ukrb.ron44.eqjo8\n3\n\nb\n\nc\n---\ncommit: 0v1e.9s3hi.sdek4.hf58q.lq85l.b7a95.nq49u.0tuqk.68ubq.pjagn.j8j1b\n9\n\nb\n\nc"
202+eth-txs ~ 02 6e6f6e63652c6761732d70726963652c6761732c746f2c76616c75652c646174612c636861696e2d69640a "nonce,gas-price,gas,to,value,data,chain-id\n"
203+eth-txs ~[[nonce=0 gas-price=1 gas=21.000 to=0x0 value=0 data=0x0 chain-id=0x1]] 651c78046966b2 6e6f6e63652c6761732d70726963652c6761732c746f2c76616c75652c646174612c636861696e2d69640a3069302c3069312c306932313030302c3078303030303030303030303030303030303030303030303030303030303030303030303030303030302c3069302c307830302c3078310a "nonce,gas-price,gas,to,value,data,chain-id\n0i0,0i1,0i21000,0x0000000000000000000000000000000000000000,0i0,0x00,0x1\n"
204+eth-txs ~[[nonce=5 gas-price=20.000.000.000 gas=100.000 to=0xdead.beef value=1.000.000.000.000.000.000 data=0x1234.5678 chain-id=0x539]] 851be00040be4065c000351c2078f76df50e900300909dceda8277c0c6b3a291e0722a 6e6f6e63652c6761732d70726963652c6761732c746f2c76616c75652c646174612c636861696e2d69640a3069352c306932303030303030303030302c30693130303030302c3078303030303030303030303030303030303030303030303030303030303030303064656164626565662c3069313030303030303030303030303030303030302c307831323334353637382c30783533390a "nonce,gas-price,gas,to,value,data,chain-id\n0i5,0i20000000000,0i100000,0x00000000000000000000000000000000deadbeef,0i1000000000000000000,0x12345678,0x539\n"
205+eth-txs ~[[1 2 3 0x1 4 0xab 0x0] [10 20 30 0xffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff.ffff 1.234 0x0 0x2.a1b2.c3d4]] c52143c7610662d58268301acc0720e8ffffffffffffffffffffffffffffffffffffff3f70d24c40413d2c1baa 6e6f6e63652c6761732d70726963652c6761732c746f2c76616c75652c646174612c636861696e2d69640a3069312c3069322c3069332c3078303030303030303030303030303030303030303030303030303030303030303030303030303030312c3069342c307861622c3078300a306931302c306932302c306933302c3078666666666666666666666666666666666666666666666666666666666666666666666666666666662c3069313233342c307830302c3078322e613162322e633364340a "nonce,gas-price,gas,to,value,data,chain-id\n0i1,0i2,0i3,0x0000000000000000000000000000000000000001,0i4,0xab,0x0\n0i10,0i20,0i30,0xffffffffffffffffffffffffffffffffffffffff,0i1234,0x00,0x2.a1b2.c3d4\n"
206+hymn ;div; 053cb234bb02 3c6469763e3c2f6469763e "<div></div>"
207+hymn ;br; 055e4cae 3c6272202f3e "<br />"
208+hymn ;html:(head:title:"T" body:(h1:"Hi" img(src "i.png");)) 057c34ba36b615f0d1cac2c85680872e8d8ead6c65199caa6a057cb137b2bc1568b4d8ca323832785ab5025e5adbb9803727370e9e0eea06870777073fab0a 3c68746d6c3e3c686561643e3c7469746c653e543c2f7469746c653e3c2f686561643e3c626f64793e3c68313e48693c2f68313e3c696d67207372633d22692e706e6722202f3e3c2f626f64793e3c2f68746d6c3e "<html><head><title>T</title></head><body><h1>Hi</h1><img src=\"i.png\" /></body></html>"
209+hymn [[%p ~] ~[[[%$ [%$ "Hello & <world> \"q\" 'a'"] ~] ~]]] 05875b590647062f07670767077f0705839a41c1a07cf07bf077707970767072503f2818540c1e0f2a0605839ec1c3414f55 3c703e48656c6c6f2026616d703b20266c743b776f726c642667743b202671756f743b712671756f743b20262333393b61262333393b3c2f703e "<p>Hello &amp; &lt;world&gt; &quot;q&quot; &#39;a&#39;</p>"
210+hymn [[%a ~[[%href "x?a=1&b='2'"] [%title "<t>"]]] ~[[[%$ [%$ "link"] ~] ~]]] 050f17f0d1e4caccc1f141ff6606ed83c641cde0e2a07dd033a81cf4b4000f5d1a1d5b39281f9c1ed4d7ca32383b783ab83bf85a15 3c6120687265663d22783f613d3126616d703b623d262333393b32262333393b22207469746c653d22266c743b742667743b223e6c696e6b3c2f613e "<a href=\"x?a=1&amp;b=&#39;2&#39;\" title=\"&lt;t&gt;\">link</a>"
211+hymn [[%script ~] ~[[[%$ [%$ "if (a < b && c > \"d\") \{}"] ~] ~]]] 05f8e6c6e4d2e0e85696c1d3c1cd41c1a068f0705030281f140c2e0e0a0635839a41c1e0e3a060503f2818540c4e0e2a064d8382c1f7c1fbaa 3c7363726970743e6966202861203c20622026262063203e2022642229207b7d3c2f7363726970743e "<script>if (a < b && c > \"d\") {}</script>"
212+hymn [[%style ~] ~[[[%$ [%$ "a > b \{ content: \"&\"; }"] ~] ~]]] 0578e6e8f2d8ca5696c1c341c1a07e5030b83828187c1f140c3e0efe0eee0e4e0f5e0eee0e4e0faa0705838a41cda062d03d2818bcaf0a 3c7374796c653e61203e2062207b20636f6e74656e743a202226223b207d3c2f7374796c653e "<style>a > b { content: \"&\"; }</style>"
213+hymn [[%script [%src "a&b"] ~] ~[[[%p ~] ~[[[%$ [%$ "<x>"] ~] ~]]]]] 05f8e6c6e4d2e0e805bc39b971f0705033b8582b38dcca32281f1c1fd45715 3c736372697074207372633d226126616d703b62223e3c703e3c783e3c2f703e3c2f7363726970743e "<script src=\"a&amp;b\"><p><x></p></script>"
214+hymn [[%div ~] ~[[[%script ~] ~] [[%$ [%$ "<after>"] ~] ~]]] 053cb234bb15e09b1b934b83a36b6519940f1e0e6e0e4e0f5e0e2e0feaab02 3c6469763e3c7363726970743e3c2f7363726970743e266c743b61667465722667743b3c2f6469763e "<div><script></script>&lt;after&gt;</div>"
215+hymn [[%input ~[[%type "text"] [%value ""]]] ~] 0578d2dce0eae8057cba3cb872707af072707c70ba0578ecc2d8eaca2a 3c696e70757420747970653d2274657874222076616c75653d2222202f3e "<input type=\"text\" value=\"\" />"
216+hymn [[%$ [%$ "text"] [%x "y"] ~] ~[[[%b ~] ~]]] 65199c1ebc1c1cdf885ba36ef0bc5670b10a 3c203d22746578742220783d2279223e3c623e3c2f623e3c2f3e "< =\"text\" x=\"y\"><b></b></>"
217+hymn [[%$ ~] ~[[[%b ~] ~]]] a5155cac02 3c3e3c623e3c2f623e3c2f3e "<><b></b></>"
218+hymn [[[%xlink %href] ~] ~] 15e0c1634b735b031f4daeccac 3c786c696e6b3a687265663e3c2f786c696e6b3a687265663e "<xlink:href></xlink:href>"
219+hymn [[%p ~] ~[[[%$ [%$ "é✓"] ~] ~]]] 05875b5906623810a981100f046720265501 3c703ec3a9e29c933c2f703e "<p>\xc3\xa9\xe2\x9c\x93</p>"
220+hymn [[%div ~] ~[[[%br ~] ~[[[%$ [%$ "x"] ~] ~]]] [[%hr [%class "a"] ~] ~]]] 053cb234bb157831b9956570bc6a051e4d5e80678c2d6c6e1e3cac0a 3c6469763e3c62723e783c2f62723e3c687220636c6173733d226122202f3e3c2f6469763e "<div><br>x</br><hr class=\"a\" /></div>"
221+hymn [[[%svg %img] ~] ~] 15f0e6eccec04b6b3b2b 3c7376673a696d673e3c2f7376673a696d673e "<svg:img></svg:img>"
222+hymn [[%img [[%xml %lang] "en"] ~] ~] 05bcb4b673053cbc3636f0d9c2dccec1cbc1dd2a 3c696d6720786d6c3a6c616e673d22656e22202f3e "<img xml:lang=\"en\" />"
223+hymn [[%$ [%$ "a"] ~] ~[[[%b ~] ~]]] 65193cac155cac02 61 "a"
224+urb ;div; 053cb234bb02 3c68746d6c3e3c686561643e3c2f686561643e3c626f64793e3c6469763e3c2f6469763e3c2f626f64793e3c2f68746d6c3e "<html><head></head><body><div></div></body></html>"
225+urb ;br; 055e4cae 3c68746d6c3e3c686561643e3c2f686561643e3c626f64793e3c6272202f3e3c2f626f64793e3c2f68746d6c3e "<html><head></head><body><br /></body></html>"
226+urb [[%p ~] ~[[[%$ [%$ "Hello & <world>"] ~] ~]]] 05875b590647062f07670767077f0705839a41c1a07cf07bf077707970767072505f15 3c68746d6c3e3c686561643e3c2f686561643e3c626f64793e3c703e48656c6c6f2026616d703b20266c743b776f726c642667743b3c2f703e3c2f626f64793e3c2f68746d6c3e "<html><head></head><body><p>Hello &amp; &lt;world&gt;</p></body></html>"
227+snip [~ ~] 29 3c68746d6c3e3c686561643e3c7469746c653e736e69703c2f7469746c653e3c2f686561643e3c626f64793e3c6469763e3c68313e3c2f68313e3c6469763e3c2f6469763e3c2f6469763e3c2f626f64793e3c2f68746d6c3e "<html><head><title>snip</title></head><body><div><h1></h1><div></div></div></body></html>"
228+snip [~[;b:"x"] ~[;i:"y" ;br;]] 5570b1956570bcaa153c6d65193caf5a811793ab 3c68746d6c3e3c686561643e3c7469746c653e736e69703c2f7469746c653e3c2f686561643e3c626f64793e3c6469763e3c68313e3c623e783c2f623e3c2f68313e3c6469763e3c693e793c2f693e3c6272202f3e3c2f6469763e3c2f6469763e3c2f626f64793e3c2f68746d6c3e "<html><head><title>snip</title></head><body><div><h1><b>x</b></h1><div><i>y</i><br /></div></div></body></html>"
229+snip [~[[[%$ [%$ "<h>"] ~] ~]] ~] 559641f9e0e8a0beaa 3c68746d6c3e3c686561643e3c7469746c653e736e69703c2f7469746c653e3c2f686561643e3c626f64793e3c6469763e3c68313e266c743b682667743b3c2f68313e3c6469763e3c2f6469763e3c2f6469763e3c2f626f64793e3c2f68746d6c3e "<html><head><title>snip</title></head><body><div><h1>&lt;h&gt;</h1><div></div></div></body></html>"
230+azimuth-snapshot [1 2] 3112 3112 "1\x12"
231+azimuth-snapshot [[1 2] [1 2]] c5c849 c5c849 "\xc5\xc8I"
232+eth-logs ~[1 2] 714801 714801 "qH\x01"
233+pill [%pill %foo ~ ~ ~] 011f2e8d8d1df0ccdede9902 011f2e8d8d1df0ccdede9902 "\x01\x1f.\x8d\x8d\x1d\xf0\xcc\xde\xde\x99\x02"