# 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
"""
)
