rledemo.gno
2.59 Kb · 99 lines
1// Package rledemo is a small gnoweb demo of the run-length codec provided by
2// the [p/moul/x/daily/rle](/p/moul/x/daily/rle/v0) library: it encodes a few
3// sample strings, round-trips them, and reports the size ratio — including the
4// case where the "compression" makes things bigger.
5//
6// It contains no codec logic of its own. Stateless, so Render is deterministic.
7package rledemo
8
9import (
10 "strconv"
11 "strings"
12
13 "gno.land/p/moul/x/daily/rle/v0"
14)
15
16// samples are picked to show both outcomes: runny data compresses, data
17// without runs expands.
18var samples = []string{
19 "aaaaaaaaaabbbbbbbbbb",
20 "aaabbc",
21 "abcdef",
22}
23
24// Render renders the demo for gnoweb.
25//
26// Render("") / Render("/") -> the samples table
27// Render("/<text>") -> encode that text
28func Render(path string) string {
29 var b strings.Builder
30 b.WriteString("# Run-Length Encoding\n\n")
31 b.WriteString("`<count><char>` pairs, demoing the ")
32 b.WriteString("[`p/moul/x/daily/rle`](/p/moul/x/daily/rle/v0) library.\n\n")
33
34 if in := parseArg(path); in != "" {
35 return b.String() + one(in)
36 }
37
38 b.WriteString("| input | encoded | size | round-trips |\n|---|---|---|---|\n")
39 for _, s := range samples {
40 enc, err := rle.Encode(s)
41 if err != nil {
42 continue
43 }
44 dec, _ := rle.Decode(enc)
45 b.WriteString("| `")
46 b.WriteString(s)
47 b.WriteString("` | `")
48 b.WriteString(enc)
49 b.WriteString("` | ")
50 b.WriteString(strconv.Itoa(rle.Ratio(s, enc)))
51 b.WriteString("% | ")
52 if dec == s {
53 b.WriteString("✅")
54 } else {
55 b.WriteString("❌")
56 }
57 b.WriteString(" |\n")
58 }
59 b.WriteString("\n> Over 100% means the encoding made the data **bigger**. ")
60 b.WriteString("RLE only wins on runny input, and `abcdef` is the honest counter-example.\n\n")
61 b.WriteString("> Append text to the path to encode it — digits are rejected, ")
62 b.WriteString("since they would be ambiguous with a run count.\n")
63 return b.String()
64}
65
66func one(in string) string {
67 var b strings.Builder
68 b.WriteString("## `")
69 b.WriteString(in)
70 b.WriteString("`\n\n")
71 enc, err := rle.Encode(in)
72 if err != nil {
73 b.WriteString("_")
74 b.WriteString(err.Error())
75 b.WriteString("_\n")
76 return b.String()
77 }
78 dec, _ := rle.Decode(enc)
79 b.WriteString("- encoded: `")
80 b.WriteString(enc)
81 b.WriteString("`\n- size: **")
82 b.WriteString(strconv.Itoa(rle.Ratio(in, enc)))
83 b.WriteString("%**\n- round-trips: ")
84 if dec == in {
85 b.WriteString("✅\n")
86 } else {
87 b.WriteString("❌\n")
88 }
89 return b.String()
90}
91
92func parseArg(path string) string {
93 s := strings.TrimSpace(path)
94 s = strings.TrimPrefix(s, "/")
95 if i := strings.IndexByte(s, '/'); i >= 0 {
96 s = s[:i]
97 }
98 return s
99}