Browse Source

huffman decode

Brad Fitzpatrick 11 years ago
parent
commit
16a8b2f321
2 changed files with 128 additions and 0 deletions
  1. 35 0
      hpack/hpack_test.go
  2. 93 0
      hpack/huffman.go

+ 35 - 0
hpack/hpack_test.go

@@ -7,6 +7,8 @@ package hpack
 
 import (
 	"bufio"
+	"bytes"
+	"encoding/hex"
 	"regexp"
 	"strconv"
 	"strings"
@@ -127,3 +129,36 @@ func TestHeaderTableAt(t *testing.T) {
 		t.Errorf("at(62) = %q; want %q", got, want)
 	}
 }
+
+func TestHuffmanDecode(t *testing.T) {
+	tests := []struct {
+		inHex, want string
+	}{
+		{"f1e3 c2e5 f23a 6ba0 ab90 f4ff", "www.example.com"},
+		{"a8eb 1064 9cbf", "no-cache"},
+		{"25a8 49e9 5ba9 7d7f", "custom-key"},
+		{"25a8 49e9 5bb8 e8b4 bf", "custom-value"},
+		{"6402", "302"},
+		{"aec3 771a 4b", "private"},
+		{"d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff", "Mon, 21 Oct 2013 20:13:21 GMT"},
+		{"9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3", "https://www.example.com"},
+		{"9bd9 ab", "gzip"},
+		{"94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07",
+			"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
+	}
+	for i, tt := range tests {
+		var buf bytes.Buffer
+		in, err := hex.DecodeString(strings.Replace(tt.inHex, " ", "", -1))
+		if err != nil {
+			t.Errorf("%d. hex input error: %v", i, err)
+			continue
+		}
+		if _, err := HuffmanDecode(&buf, in); err != nil {
+			t.Errorf("%d. decode error: %v", i, err)
+			continue
+		}
+		if got := buf.String(); tt.want != got {
+			t.Errorf("%d. decode = %q; want %q", i, got, tt.want)
+		}
+	}
+}

+ 93 - 0
hpack/huffman.go

@@ -0,0 +1,93 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package hpack
+
+import (
+	"bytes"
+	"io"
+	"sync"
+)
+
+var bufPool = sync.Pool{
+	New: func() interface{} { return new(bytes.Buffer) },
+}
+
+// HuffmanDecode decodes the string in v and writes the expanded
+// result to w, returning the number of bytes written to w and the
+// Write call's return value. At most one Write call is made.
+func HuffmanDecode(w io.Writer, v []byte) (int, error) {
+	buf := bufPool.Get().(*bytes.Buffer)
+	buf.Reset()
+	defer bufPool.Put(buf)
+
+	n := rootHuffmanNode
+	cur, nbits := uint(0), uint8(0)
+	for _, b := range v {
+		cur = cur<<8 | uint(b)
+		nbits += 8
+		for nbits >= 8 {
+			n = n.children[byte(cur>>(nbits-8))]
+			if n.children == nil {
+				buf.WriteByte(n.sym)
+				nbits -= n.codeLen
+				n = rootHuffmanNode
+			} else {
+				nbits -= 8
+			}
+		}
+	}
+	for nbits > 0 {
+		n = n.children[byte(cur<<(8-nbits))]
+		if n.children != nil || n.codeLen > nbits {
+			break
+		}
+		buf.WriteByte(n.sym)
+		nbits -= n.codeLen
+		n = rootHuffmanNode
+	}
+	return w.Write(buf.Bytes())
+}
+
+type node struct {
+	// children is non-nil for internal nodes
+	children []*node
+
+	// The following are only valid if children is nil:
+	codeLen uint8 // number of bits that led to the output of sym
+	sym     byte  // output symbol
+}
+
+func newInternalNode() *node {
+	return &node{children: make([]*node, 256)}
+}
+
+var rootHuffmanNode = newInternalNode()
+
+func init() {
+	for i, code := range huffmanCodes {
+		if i > 255 {
+			panic("too many huffman codes")
+		}
+		addDecoderNode(byte(i), code, huffmanCodeLen[i])
+	}
+}
+
+func addDecoderNode(sym byte, code uint32, codeLen uint8) {
+	cur := rootHuffmanNode
+	for codeLen > 8 {
+		codeLen -= 8
+		i := uint8(code >> codeLen)
+		if cur.children[i] == nil {
+			cur.children[i] = newInternalNode()
+		}
+		cur = cur.children[i]
+	}
+	shift := 8 - codeLen
+	start, end := int(uint8(code<<shift)), int(1<<shift)
+	for i := start; i < start+end; i++ {
+		cur.children[i] = &node{sym: sym, codeLen: codeLen}
+	}
+}