1#!/usr/bin/env python 2# 3# This script takes directories which contain the hpack-test-case json 4# files, and calculates the compression ratio in each file and outputs 5# the result in table formatted in rst. 6# 7# The each directory contains the result of various HPACK compressor. 8# 9# The table is laid out so that we can see that how input header set 10# in one json file is compressed in each compressor. 11# 12# For hpack-test-case, see https://github.com/Jxck/hpack-test-case 13# 14import sys, json, os, re 15 16class Stat: 17 18 def __init__(self, complen, srclen): 19 self.complen = complen 20 self.srclen = srclen 21 22def compute_stat(jsdata): 23 complen = 0 24 srclen = 0 25 for item in jsdata['cases']: 26 complen += len(item['wire']) // 2 27 srclen += \ 28 sum([len(list(x.keys())[0]) + len(list(x.values())[0]) \ 29 for x in item['headers']]) 30 return Stat(complen, srclen) 31 32def format_result(r): 33 return '{:.02f} ({}/{}) '.format(r.complen/r.srclen, r.complen, r.srclen) 34 35if __name__ == '__main__': 36 entries = [(os.path.basename(re.sub(r'/+$', '', p)), p) \ 37 for p in sys.argv[1:]] 38 maxnamelen = 0 39 maxstorynamelen = 0 40 res = {} 41 42 stories = set() 43 for name, ent in entries: 44 files = [p for p in os.listdir(ent) if p.endswith('.json')] 45 res[name] = {} 46 maxnamelen = max(maxnamelen, len(name)) 47 for fn in files: 48 stories.add(fn) 49 maxstorynamelen = max(maxstorynamelen, len(fn)) 50 with open(os.path.join(ent, fn)) as f: 51 input = f.read() 52 rv = compute_stat(json.loads(input)) 53 res[name][fn] = rv 54 maxnamelen = max(maxnamelen, len(format_result(rv))) 55 stories = list(stories) 56 stories.sort() 57 58 storynameformat = '{{:{}}} '.format(maxstorynamelen) 59 nameformat = '{{:{}}} '.format(maxnamelen) 60 61 62 sys.stdout.write('''\ 63hpack-test-case compression ratio 64================================= 65 66The each cell has ``X (Y/Z)`` format: 67 68X 69 Y / Z 70Y 71 number of bytes after compression 72Z 73 number of bytes before compression 74 75''') 76 77 def write_border(): 78 sys.stdout.write('='*maxstorynamelen) 79 sys.stdout.write(' ') 80 for _ in entries: 81 sys.stdout.write('='*maxnamelen) 82 sys.stdout.write(' ') 83 sys.stdout.write('\n') 84 85 write_border() 86 87 sys.stdout.write(storynameformat.format('story')) 88 for name, _ in entries: 89 sys.stdout.write(nameformat.format(name)) 90 sys.stdout.write('\n') 91 92 write_border() 93 94 for story in stories: 95 sys.stdout.write(storynameformat.format(story)) 96 srclen = -1 97 for name, _ in entries: 98 stats = res[name] 99 if story not in stats: 100 sys.stdout.write(nameformat.format('N/A')) 101 continue 102 if srclen == -1: 103 srclen = stats[story].srclen 104 elif srclen != stats[story].srclen: 105 raise Exception('Bad srclen') 106 sys.stdout.write(nameformat.format(format_result(stats[story]))) 107 sys.stdout.write('\n') 108 109 write_border() 110