# This script exists to auto-generate Http2HuffmanIncoming.h from the table # contained in the HPACK spec. It's pretty simple to run: # python make_incoming_tables.py < http2_huffman_table.txt > Http2HuffmanIncoming.h # where huff_incoming.txt is copy/pasted text from the latest version of the # HPACK spec, with all non-relevant lines removed (the most recent version # of huff_incoming.txt also lives in this directory as an example). import sys def char_cmp(x, y): rv = cmp(x["nbits"], y["nbits"]) if not rv: rv = cmp(x["bpat"], y["bpat"]) if not rv: rv = cmp(x["ascii"], y["ascii"]) return rv characters = [] for line in sys.stdin: line = line.rstrip() obracket = line.rfind("[") nbits = int(line[obracket + 1 : -1]) oparen = line.find(" (") ascii = int(line[oparen + 2 : oparen + 5].strip()) bar = line.find("|", oparen) space = line.find(" ", bar) bpat = line[bar + 1 : space].strip().rstrip("|") characters.append({"ascii": ascii, "nbits": nbits, "bpat": bpat}) characters.sort(cmp=char_cmp) raw_entries = [] for c in characters: raw_entries.append((c["ascii"], c["bpat"])) class DefaultList(list): def __init__(self, default=None): self.__default = default def __ensure_size(self, sz): while sz > len(self): self.append(self.__default) def __getitem__(self, idx): self.__ensure_size(idx + 1) rv = super(DefaultList, self).__getitem__(idx) return rv def __setitem__(self, idx, val): self.__ensure_size(idx + 1) super(DefaultList, self).__setitem__(idx, val) def expand_to_8bit(bstr): while len(bstr) < 8: bstr += "0" return int(bstr, 2) table = DefaultList() for r in raw_entries: ascii, bpat = r ascii = int(ascii) bstrs = bpat.split("|") curr_table = table while len(bstrs) > 1: idx = expand_to_8bit(bstrs[0]) if curr_table[idx] is None: curr_table[idx] = DefaultList() curr_table = curr_table[idx] bstrs.pop(0) idx = expand_to_8bit(bstrs[0]) curr_table[idx] = { "prefix_len": len(bstrs[0]), "mask": int(bstrs[0], 2), "value": ascii, } def output_table(table, name_suffix=""): max_prefix_len = 0 for i, t in enumerate(table): if isinstance(t, dict): if t["prefix_len"] > max_prefix_len: max_prefix_len = t["prefix_len"] elif t is not None: output_table(t, "%s_%s" % (name_suffix, i)) tablename = "HuffmanIncoming%s" % (name_suffix if name_suffix else "Root",) entriestable = tablename.replace("HuffmanIncoming", "HuffmanIncomingEntries") nexttable = tablename.replace("HuffmanIncoming", "HuffmanIncomingNextTables") sys.stdout.write("static const HuffmanIncomingEntry %s[] = {\n" % (entriestable,)) prefix_len = 0 value = 0 i = 0 while i < 256: t = table[i] if isinstance(t, dict): value = t["value"] prefix_len = t["prefix_len"] elif t is not None: break sys.stdout.write(" { %s, %s }" % (value, prefix_len)) sys.stdout.write(",\n") i += 1 indexOfFirstNextTable = i if i < 256: sys.stdout.write("};\n") sys.stdout.write("\n") sys.stdout.write("static const HuffmanIncomingTable* %s[] = {\n" % (nexttable,)) while i < 256: subtable = "%s_%s" % (name_suffix, i) ptr = "&HuffmanIncoming%s" % (subtable,) sys.stdout.write(" %s" % (ptr)) sys.stdout.write(",\n") i += 1 else: nexttable = "nullptr" sys.stdout.write("};\n") sys.stdout.write("\n") sys.stdout.write("static const HuffmanIncomingTable %s = {\n" % (tablename,)) sys.stdout.write(" %s,\n" % (entriestable,)) sys.stdout.write(" %s,\n" % (nexttable,)) sys.stdout.write(" %s,\n" % (indexOfFirstNextTable,)) sys.stdout.write(" %s\n" % (max_prefix_len,)) sys.stdout.write("};\n") sys.stdout.write("\n") sys.stdout.write( """/* * THIS FILE IS AUTO-GENERATED. DO NOT EDIT! */ #ifndef mozilla__net__Http2HuffmanIncoming_h #define mozilla__net__Http2HuffmanIncoming_h namespace mozilla { namespace net { struct HuffmanIncomingTable; struct HuffmanIncomingEntry { uint16_t mValue:9; // 9 bits so it can hold 0..256 uint16_t mPrefixLen:7; // only holds 1..8 }; // The data members are public only so they can be statically constructed. All // accesses should be done through the functions. struct HuffmanIncomingTable { // The normal entries, for indices in the range 0..(mNumEntries-1). const HuffmanIncomingEntry* const mEntries; // The next tables, for indices in the range mNumEntries..255. Must be // |nullptr| if mIndexOfFirstNextTable is 256. const HuffmanIncomingTable** const mNextTables; // The index of the first next table (equal to the number of entries in // mEntries). This cannot be a uint8_t because it can have the value 256, // in which case there are no next tables and mNextTables must be |nullptr|. const uint16_t mIndexOfFirstNextTable; const uint8_t mPrefixLen; bool IndexHasANextTable(uint8_t aIndex) const { return aIndex >= mIndexOfFirstNextTable; } const HuffmanIncomingEntry* Entry(uint8_t aIndex) const { MOZ_ASSERT(aIndex < mIndexOfFirstNextTable); return &mEntries[aIndex]; } const HuffmanIncomingTable* NextTable(uint8_t aIndex) const { MOZ_ASSERT(aIndex >= mIndexOfFirstNextTable); return mNextTables[aIndex - mIndexOfFirstNextTable]; } }; """ ) output_table(table) sys.stdout.write( """} // namespace net } // namespace mozilla #endif // mozilla__net__Http2HuffmanIncoming_h """ )