Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

huffman.gno

12.85 Kb · 439 lines
  1// Package huffman compresses a string with canonical Huffman codes: the
  2// classic frequency-based prefix code, in the one form that is safe to put on
  3// a chain.
  4//
  5// Two properties matter here and neither is about compression.
  6//
  7// The codes are CANONICAL. A plain Huffman tree is not unique: two equally
  8// optimal trees give the same total bit count and different codes, and which
  9// one you get depends on how ties are broken while merging. Canonical codes
 10// throw the tree away and keep only the code LENGTHS, then re-derive the bits
 11// by sorting on (length, symbol). The same input therefore always produces the
 12// same table, which is what a deterministic Render and a reproducible payload
 13// both need, and the table a decoder has to carry shrinks to one length per
 14// symbol.
 15//
 16// Decoding VALIDATES. [Encoded] is a plain struct with exported fields, so a
 17// caller can build one by hand or read one out of storage, and a decoder that
 18// trusts what it is handed will happily return a string nobody ever encoded.
 19// [Encoded.Decode] rebuilds the canonical table from the lengths it was given,
 20// refuses a payload whose own Bits disagree with it, and checks the Kraft
 21// equality, the bit budget and the decoded size before returning.
 22//
 23// A live demo of this package is at
 24// [r/moul/x/daily/huffmandemo](/r/moul/x/daily/huffmandemo/v0).
 25package huffman
 26
 27import (
 28	"errors"
 29	"strings"
 30)
 31
 32// MaxLen bounds input so gas stays predictable.
 33const MaxLen = 4096
 34
 35// maxCodeLen is the longest code this package will emit or accept.
 36//
 37// It is not arbitrary. The worst case for Huffman is a Fibonacci frequency
 38// distribution, where n symbols need a code of n-1 bits and the input has to
 39// be at least F(n+2) bytes long. F(34) is 5,702,887, so reaching 32 bits needs
 40// megabytes of input and [MaxLen] stops at 4096, which bounds a code here to
 41// 18 bits. The ceiling exists so the overflow is a returned error rather than
 42// a shift nobody checked.
 43const maxCodeLen = 32
 44
 45var (
 46	// ErrEmpty is returned for an input with no bytes in it: there is no such
 47	// thing as a code for an empty alphabet.
 48	ErrEmpty = errors.New("huffman: empty input")
 49	// ErrTooLong is returned when input exceeds MaxLen.
 50	ErrTooLong = errors.New("huffman: input too long")
 51	// ErrTooDeep is returned when the optimal code would be longer than 32
 52	// bits. Unreachable at MaxLen, and returned rather than panicked because a
 53	// realm's Render can never be fixed once the path is published.
 54	ErrTooDeep = errors.New("huffman: code longer than 32 bits")
 55	// ErrCorrupt is returned by Decode when the payload is not one this
 56	// package could have produced.
 57	ErrCorrupt = errors.New("huffman: corrupt payload")
 58)
 59
 60// Code is one symbol's canonical prefix code.
 61type Code struct {
 62	Symbol byte
 63	Len    int    // length in bits, 1 to 32
 64	Bits   uint32 // the code, right aligned, most significant bit first
 65}
 66
 67// String renders the code as its bit pattern, "110" and not "6".
 68func (c Code) String() string {
 69	var b strings.Builder
 70	for i := c.Len - 1; i >= 0; i-- {
 71		if (c.Bits>>uint(i))&1 == 1 {
 72			b.WriteByte('1')
 73		} else {
 74			b.WriteByte('0')
 75		}
 76	}
 77	return b.String()
 78}
 79
 80// Encoded is a compressed payload: the canonical table, which is the whole
 81// header a decoder needs, and the packed bit stream.
 82type Encoded struct {
 83	Codes []Code // sorted by (Len, Symbol)
 84	Data  []byte // the bit stream, most significant bit of each byte first
 85	NBits int    // significant bits in Data; the rest of the last byte is padding
 86	Size  int    // length of the original input, in bytes
 87}
 88
 89// Saved reports how many bits the encoding removed, which is negative when the
 90// alphabet is flat enough that the codes cost more than the bytes did.
 91func (e *Encoded) Saved() int { return e.Size*8 - e.NBits }
 92
 93// BitString renders the stream as '0' and '1', padding excluded. It is meant
 94// for demos and error messages: the result is NBits characters long.
 95func (e *Encoded) BitString() string {
 96	var b strings.Builder
 97	for i := 0; i < e.NBits && i/8 < len(e.Data); i++ {
 98		if (e.Data[i/8]>>uint(7-i%8))&1 == 1 {
 99			b.WriteByte('1')
100		} else {
101			b.WriteByte('0')
102		}
103	}
104	return b.String()
105}
106
107// Table returns the canonical code table for s, sorted by (Len, Symbol),
108// without encoding anything.
109func Table(s string) ([]Code, error) {
110	if len(s) > MaxLen {
111		return nil, ErrTooLong
112	}
113	if len(s) == 0 {
114		return nil, ErrEmpty
115	}
116
117	var freq [256]int
118	for i := 0; i < len(s); i++ {
119		freq[s[i]]++
120	}
121
122	lengths, err := codeLengths(freq)
123	if err != nil {
124		return nil, err
125	}
126
127	codes := make([]Code, 0, len(lengths))
128	for _, l := range lengths {
129		codes = append(codes, Code{Symbol: l.Symbol, Len: l.Len})
130	}
131	sortCodes(codes)
132	assignCanonical(codes)
133	return codes, nil
134}
135
136// Encode builds the canonical table for s and packs s with it.
137func Encode(s string) (*Encoded, error) {
138	codes, err := Table(s)
139	if err != nil {
140		return nil, err
141	}
142
143	var (
144		byLen    [256]int
145		byBits   [256]uint32
146		declared [256]bool
147	)
148	for _, c := range codes {
149		byLen[c.Symbol] = c.Len
150		byBits[c.Symbol] = c.Bits
151		declared[c.Symbol] = true
152	}
153
154	e := &Encoded{Codes: codes, Size: len(s)}
155	for i := 0; i < len(s); i++ {
156		sym := s[i]
157		if !declared[sym] {
158			// Unreachable: the table is built from this very string.
159			return nil, ErrCorrupt
160		}
161		e.writeBits(byBits[sym], byLen[sym])
162	}
163	return e, nil
164}
165
166// writeBits appends the low n bits of v to the stream, most significant first.
167func (e *Encoded) writeBits(v uint32, n int) {
168	for i := n - 1; i >= 0; i-- {
169		if e.NBits%8 == 0 {
170			e.Data = append(e.Data, 0)
171		}
172		if (v>>uint(i))&1 == 1 {
173			e.Data[e.NBits/8] |= 1 << uint(7-e.NBits%8)
174		}
175		e.NBits++
176	}
177}
178
179// Decode validates the payload and returns the original string.
180//
181// Everything it checks is something a hand-built or storage-read Encoded can
182// get wrong: an empty or over-long table, a zero length, a table out of
183// canonical order, a repeated symbol, Bits that disagree with the lengths, a
184// code set that is not a complete prefix code, a zero-bit or zero-size
185// payload, a Data slice that is not exactly the bytes NBits needs, nonzero
186// padding in the last byte, a stream ending mid-code, and a decoded length
187// that is not Size.
188func (e *Encoded) Decode() (string, error) {
189	if len(e.Codes) == 0 || len(e.Codes) > 256 || e.NBits < 1 || e.Size < 1 {
190		// Encode refuses an empty input, so a zero-bit or zero-size payload
191		// is not one this package produced. Accepting it would make "" a
192		// valid decoding of a table that was never used.
193		return "", ErrCorrupt
194	}
195	if e.Size > MaxLen {
196		return "", ErrCorrupt
197	}
198	// Data holds exactly the bytes NBits needs, and the padding in the last
199	// one is zero. Neither is cosmetic: spare bytes and dirty padding are two
200	// ways to carry a second payload that decodes to the same string.
201	if len(e.Data) != (e.NBits+7)/8 {
202		return "", ErrCorrupt
203	}
204	if pad := len(e.Data)*8 - e.NBits; pad > 0 {
205		if e.Data[len(e.Data)-1]&byte((1<<uint(pad))-1) != 0 {
206			return "", ErrCorrupt
207		}
208	}
209
210	var (
211		count [maxCodeLen + 1]int
212		seen  [256]bool
213	)
214	for i, c := range e.Codes {
215		if c.Len < 1 || c.Len > maxCodeLen {
216			return "", ErrCorrupt
217		}
218		if i > 0 {
219			prev := e.Codes[i-1]
220			if c.Len < prev.Len || (c.Len == prev.Len && c.Symbol <= prev.Symbol) {
221				return "", ErrCorrupt
222			}
223		}
224		// The order check only compares neighbours, so it catches a repeat at
225		// the same length and misses one at two different lengths. Encode
226		// never emits two codes for one byte, and a decoder that accepted it
227		// would map two streams to the same string.
228		if seen[c.Symbol] {
229			return "", ErrCorrupt
230		}
231		seen[c.Symbol] = true
232		count[c.Len]++
233	}
234
235	// Kraft equality: a complete prefix code fills the unit interval exactly.
236	// The single-symbol alphabet is the one exception this package emits, and
237	// it is incomplete by construction: its leaf has no sibling.
238	if !(len(e.Codes) == 1 && e.Codes[0].Len == 1) {
239		kraft := uint64(0)
240		for _, c := range e.Codes {
241			kraft += uint64(1) << uint(maxCodeLen-c.Len)
242		}
243		if kraft != uint64(1)<<uint(maxCodeLen) {
244			return "", ErrCorrupt
245		}
246	}
247
248	// The lengths are the header. Re-derive the bits from them and refuse a
249	// payload that carries different ones.
250	want := make([]Code, len(e.Codes))
251	copy(want, e.Codes)
252	assignCanonical(want)
253	for i, c := range e.Codes {
254		if c.Bits != want[i].Bits {
255			return "", ErrCorrupt
256		}
257	}
258
259	var (
260		first    [maxCodeLen + 2]uint32
261		firstIdx [maxCodeLen + 2]int
262		code     uint32
263		idx      int
264	)
265	for l := 1; l <= maxCodeLen; l++ {
266		first[l] = code
267		firstIdx[l] = idx
268		code = (code + uint32(count[l])) << 1
269		idx += count[l]
270	}
271
272	var (
273		out    strings.Builder
274		cur    uint32
275		curLen int
276	)
277	for i := 0; i < e.NBits; i++ {
278		cur = cur<<1 | uint32((e.Data[i/8]>>uint(7-i%8))&1)
279		curLen++
280		if curLen > maxCodeLen {
281			return "", ErrCorrupt
282		}
283		if n := count[curLen]; n > 0 && cur >= first[curLen] && cur-first[curLen] < uint32(n) {
284			out.WriteByte(e.Codes[firstIdx[curLen]+int(cur-first[curLen])].Symbol)
285			cur, curLen = 0, 0
286		}
287	}
288	if curLen != 0 {
289		return "", ErrCorrupt // the stream ends in the middle of a code
290	}
291	got := out.String()
292	if len(got) != e.Size {
293		return "", ErrCorrupt
294	}
295	return got, nil
296}
297
298// node is one entry in the merge forest. A leaf carries a symbol, an internal
299// node carries the two indices it was merged from.
300type node struct {
301	weight      int
302	symbol      int // -1 for an internal node
303	left, right int
304}
305
306// codeLengths returns the optimal length per present symbol, deterministically.
307//
308// It is the two-queue variant of Huffman's algorithm: leaves pre-sorted by
309// (frequency, symbol) in one queue, merged nodes appended to a second in the
310// order they are created, which is already non-decreasing. Both queues are
311// then read from the front, so no priority queue and no map is involved and
312// nothing depends on heap tie-breaking. Ties prefer the leaf queue, which
313// keeps the shallower codes on the more frequent symbols.
314func codeLengths(freq [256]int) ([]Code, error) {
315	nodes := make([]node, 0, 511)
316	leaves := make([]int, 0, 256)
317	for sym := 0; sym < 256; sym++ {
318		if freq[sym] == 0 {
319			continue
320		}
321		nodes = append(nodes, node{weight: freq[sym], symbol: sym, left: -1, right: -1})
322		leaves = append(leaves, len(nodes)-1)
323	}
324	sortLeaves(nodes, leaves)
325
326	// One distinct symbol: the leaf has nothing to pair with, so the tree walk
327	// below would give it a zero-bit code and no stream at all.
328	if len(leaves) == 1 {
329		return []Code{{Symbol: byte(nodes[leaves[0]].symbol), Len: 1}}, nil
330	}
331
332	merged := make([]int, 0, 256)
333	headLeaf, headMerged := 0, 0
334	pop := func() int {
335		takeLeaf := headLeaf < len(leaves)
336		if takeLeaf && headMerged < len(merged) {
337			takeLeaf = nodes[leaves[headLeaf]].weight <= nodes[merged[headMerged]].weight
338		}
339		if takeLeaf {
340			i := leaves[headLeaf]
341			headLeaf++
342			return i
343		}
344		i := merged[headMerged]
345		headMerged++
346		return i
347	}
348	for (len(leaves)-headLeaf)+(len(merged)-headMerged) > 1 {
349		a := pop()
350		b := pop()
351		nodes = append(nodes, node{
352			weight: nodes[a].weight + nodes[b].weight,
353			symbol: -1,
354			left:   a,
355			right:  b,
356		})
357		merged = append(merged, len(nodes)-1)
358	}
359	root := merged[len(merged)-1]
360
361	// Depth-first walk, explicit stack: a chain's call depth is not a place to
362	// be clever, and the tree can be 18 levels deep at MaxLen.
363	out := make([]Code, 0, len(leaves))
364	type frame struct{ idx, depth int }
365	stack := []frame{{root, 0}}
366	for len(stack) > 0 {
367		f := stack[len(stack)-1]
368		stack = stack[:len(stack)-1]
369		n := nodes[f.idx]
370		if n.symbol >= 0 {
371			if f.depth > maxCodeLen {
372				return nil, ErrTooDeep
373			}
374			out = append(out, Code{Symbol: byte(n.symbol), Len: f.depth})
375			continue
376		}
377		stack = append(stack, frame{n.left, f.depth + 1}, frame{n.right, f.depth + 1})
378	}
379	return out, nil
380}
381
382// sortLeaves orders leaf indices by (weight, symbol), ascending. Insertion
383// sort: at most 256 entries, and gno has no sort.Slice.
384func sortLeaves(nodes []node, leaves []int) {
385	for i := 1; i < len(leaves); i++ {
386		v := leaves[i]
387		j := i - 1
388		for j >= 0 && leafLess(nodes[v], nodes[leaves[j]]) {
389			leaves[j+1] = leaves[j]
390			j--
391		}
392		leaves[j+1] = v
393	}
394}
395
396func leafLess(a, b node) bool {
397	if a.weight != b.weight {
398		return a.weight < b.weight
399	}
400	return a.symbol < b.symbol
401}
402
403// sortCodes orders a table by (Len, Symbol), ascending, which is the canonical
404// order every other function here assumes.
405func sortCodes(codes []Code) {
406	for i := 1; i < len(codes); i++ {
407		v := codes[i]
408		j := i - 1
409		for j >= 0 && codeLess(v, codes[j]) {
410			codes[j+1] = codes[j]
411			j--
412		}
413		codes[j+1] = v
414	}
415}
416
417func codeLess(a, b Code) bool {
418	if a.Len != b.Len {
419		return a.Len < b.Len
420	}
421	return a.Symbol < b.Symbol
422}
423
424// assignCanonical fills in Bits from Len alone: start at zero, add one per
425// code, shift left on every increase in length. The table must already be
426// sorted by (Len, Symbol).
427func assignCanonical(codes []Code) {
428	if len(codes) == 0 {
429		return
430	}
431	code := uint32(0)
432	prev := codes[0].Len
433	for i := range codes {
434		code <<= uint(codes[i].Len - prev)
435		prev = codes[i].Len
436		codes[i].Bits = code
437		code++
438	}
439}