diff --git a/python/calcratio.py b/python/calcratio.py new file mode 100755 index 00000000..0c52a16f --- /dev/null +++ b/python/calcratio.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# +# This script takes directories which contain the hpack-test-case json +# files, and calculates the compression ratio in each file and outputs +# the result in table formatted in rst. +# +# The each directory contains the result of various HPACK compressor. +# +# The table is laid out so that we can see that how input header set +# in one json file is compressed in each compressor. +# +# For hpack-test-case, see https://github.com/Jxck/hpack-test-case +# +import sys, json, os, re + +class Stat: + + def __init__(self, complen, srclen): + self.complen = complen + self.srclen = srclen + +def compute_stat(jsdata): + complen = 0 + srclen = 0 + for item in jsdata['cases']: + complen += len(item['wire']) // 2 + srclen += \ + sum([len(list(x.keys())[0]) + len(list(x.values())[0]) \ + for x in item['headers']]) + return Stat(complen, srclen) + +def format_result(r): + return '{:.02f} ({}/{}) '.format(r.complen/r.srclen, r.complen, r.srclen) + +if __name__ == '__main__': + entries = [(os.path.basename(re.sub(r'/+$', '', p)), p) \ + for p in sys.argv[1:]] + maxnamelen = 0 + maxstorynamelen = 0 + res = {} + + stories = set() + for name, ent in entries: + files = [p for p in os.listdir(ent) if p.endswith('.json')] + res[name] = {} + maxnamelen = max(maxnamelen, len(name)) + for fn in files: + stories.add(fn) + maxstorynamelen = max(maxstorynamelen, len(fn)) + with open(os.path.join(ent, fn)) as f: + input = f.read() + rv = compute_stat(json.loads(input)) + res[name][fn] = rv + maxnamelen = max(maxnamelen, len(format_result(rv))) + stories = list(stories) + stories.sort() + + storynameformat = '{{:{}}} '.format(maxstorynamelen) + nameformat = '{{:{}}} '.format(maxnamelen) + + + sys.stdout.write('''\ +hpack-test-case compression ratio +================================= + +The each cell has ``X (Y/Z)`` format: + +X + Y / Z +Y + number of bytes after compression +Z + number of bytes before compression + +''') + + def write_border(): + sys.stdout.write('='*maxstorynamelen) + sys.stdout.write(' ') + for _ in entries: + sys.stdout.write('='*maxnamelen) + sys.stdout.write(' ') + sys.stdout.write('\n') + + write_border() + + sys.stdout.write(storynameformat.format('story')) + for name, _ in entries: + sys.stdout.write(nameformat.format(name)) + sys.stdout.write('\n') + + write_border() + + for story in stories: + sys.stdout.write(storynameformat.format(story)) + srclen = -1 + for name, _ in entries: + stats = res[name] + if story not in stats: + sys.stdout.write(nameformat.format('N/A')) + continue + if srclen == -1: + srclen = stats[story].srclen + elif srclen != stats[story].srclen: + raise Exception('Bad srclen') + sys.stdout.write(nameformat.format(format_result(stats[story]))) + sys.stdout.write('\n') + + write_border()