1*f4ee7fbaSAndroid Build Coastguard Worker#! /usr/bin/env python 2*f4ee7fbaSAndroid Build Coastguard Worker"""Compression/decompression utility using the Brotli algorithm.""" 3*f4ee7fbaSAndroid Build Coastguard Worker 4*f4ee7fbaSAndroid Build Coastguard Workerfrom __future__ import print_function 5*f4ee7fbaSAndroid Build Coastguard Workerimport argparse 6*f4ee7fbaSAndroid Build Coastguard Workerimport sys 7*f4ee7fbaSAndroid Build Coastguard Workerimport os 8*f4ee7fbaSAndroid Build Coastguard Workerimport platform 9*f4ee7fbaSAndroid Build Coastguard Worker 10*f4ee7fbaSAndroid Build Coastguard Workerimport brotli 11*f4ee7fbaSAndroid Build Coastguard Worker 12*f4ee7fbaSAndroid Build Coastguard Worker# default values of encoder parameters 13*f4ee7fbaSAndroid Build Coastguard WorkerDEFAULT_PARAMS = { 14*f4ee7fbaSAndroid Build Coastguard Worker 'mode': brotli.MODE_GENERIC, 15*f4ee7fbaSAndroid Build Coastguard Worker 'quality': 11, 16*f4ee7fbaSAndroid Build Coastguard Worker 'lgwin': 22, 17*f4ee7fbaSAndroid Build Coastguard Worker 'lgblock': 0, 18*f4ee7fbaSAndroid Build Coastguard Worker} 19*f4ee7fbaSAndroid Build Coastguard Worker 20*f4ee7fbaSAndroid Build Coastguard Worker 21*f4ee7fbaSAndroid Build Coastguard Workerdef get_binary_stdio(stream): 22*f4ee7fbaSAndroid Build Coastguard Worker """ Return the specified standard input, output or errors stream as a 23*f4ee7fbaSAndroid Build Coastguard Worker 'raw' buffer object suitable for reading/writing binary data from/to it. 24*f4ee7fbaSAndroid Build Coastguard Worker """ 25*f4ee7fbaSAndroid Build Coastguard Worker assert stream in ['stdin', 'stdout', 'stderr'], 'invalid stream name' 26*f4ee7fbaSAndroid Build Coastguard Worker stdio = getattr(sys, stream) 27*f4ee7fbaSAndroid Build Coastguard Worker if sys.version_info[0] < 3: 28*f4ee7fbaSAndroid Build Coastguard Worker if sys.platform == 'win32': 29*f4ee7fbaSAndroid Build Coastguard Worker # set I/O stream binary flag on python2.x (Windows) 30*f4ee7fbaSAndroid Build Coastguard Worker runtime = platform.python_implementation() 31*f4ee7fbaSAndroid Build Coastguard Worker if runtime == 'PyPy': 32*f4ee7fbaSAndroid Build Coastguard Worker # the msvcrt trick doesn't work in pypy, so I use fdopen 33*f4ee7fbaSAndroid Build Coastguard Worker mode = 'rb' if stream == 'stdin' else 'wb' 34*f4ee7fbaSAndroid Build Coastguard Worker stdio = os.fdopen(stdio.fileno(), mode, 0) 35*f4ee7fbaSAndroid Build Coastguard Worker else: 36*f4ee7fbaSAndroid Build Coastguard Worker # this works with CPython -- untested on other implementations 37*f4ee7fbaSAndroid Build Coastguard Worker import msvcrt 38*f4ee7fbaSAndroid Build Coastguard Worker msvcrt.setmode(stdio.fileno(), os.O_BINARY) 39*f4ee7fbaSAndroid Build Coastguard Worker return stdio 40*f4ee7fbaSAndroid Build Coastguard Worker else: 41*f4ee7fbaSAndroid Build Coastguard Worker # get 'buffer' attribute to read/write binary data on python3.x 42*f4ee7fbaSAndroid Build Coastguard Worker if hasattr(stdio, 'buffer'): 43*f4ee7fbaSAndroid Build Coastguard Worker return stdio.buffer 44*f4ee7fbaSAndroid Build Coastguard Worker else: 45*f4ee7fbaSAndroid Build Coastguard Worker orig_stdio = getattr(sys, '__%s__' % stream) 46*f4ee7fbaSAndroid Build Coastguard Worker return orig_stdio.buffer 47*f4ee7fbaSAndroid Build Coastguard Worker 48*f4ee7fbaSAndroid Build Coastguard Worker 49*f4ee7fbaSAndroid Build Coastguard Workerdef main(args=None): 50*f4ee7fbaSAndroid Build Coastguard Worker 51*f4ee7fbaSAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 52*f4ee7fbaSAndroid Build Coastguard Worker prog=os.path.basename(__file__), description=__doc__) 53*f4ee7fbaSAndroid Build Coastguard Worker parser.add_argument( 54*f4ee7fbaSAndroid Build Coastguard Worker '--version', action='version', version=brotli.__version__) 55*f4ee7fbaSAndroid Build Coastguard Worker parser.add_argument( 56*f4ee7fbaSAndroid Build Coastguard Worker '-i', 57*f4ee7fbaSAndroid Build Coastguard Worker '--input', 58*f4ee7fbaSAndroid Build Coastguard Worker metavar='FILE', 59*f4ee7fbaSAndroid Build Coastguard Worker type=str, 60*f4ee7fbaSAndroid Build Coastguard Worker dest='infile', 61*f4ee7fbaSAndroid Build Coastguard Worker help='Input file', 62*f4ee7fbaSAndroid Build Coastguard Worker default=None) 63*f4ee7fbaSAndroid Build Coastguard Worker parser.add_argument( 64*f4ee7fbaSAndroid Build Coastguard Worker '-o', 65*f4ee7fbaSAndroid Build Coastguard Worker '--output', 66*f4ee7fbaSAndroid Build Coastguard Worker metavar='FILE', 67*f4ee7fbaSAndroid Build Coastguard Worker type=str, 68*f4ee7fbaSAndroid Build Coastguard Worker dest='outfile', 69*f4ee7fbaSAndroid Build Coastguard Worker help='Output file', 70*f4ee7fbaSAndroid Build Coastguard Worker default=None) 71*f4ee7fbaSAndroid Build Coastguard Worker parser.add_argument( 72*f4ee7fbaSAndroid Build Coastguard Worker '-f', 73*f4ee7fbaSAndroid Build Coastguard Worker '--force', 74*f4ee7fbaSAndroid Build Coastguard Worker action='store_true', 75*f4ee7fbaSAndroid Build Coastguard Worker help='Overwrite existing output file', 76*f4ee7fbaSAndroid Build Coastguard Worker default=False) 77*f4ee7fbaSAndroid Build Coastguard Worker parser.add_argument( 78*f4ee7fbaSAndroid Build Coastguard Worker '-d', 79*f4ee7fbaSAndroid Build Coastguard Worker '--decompress', 80*f4ee7fbaSAndroid Build Coastguard Worker action='store_true', 81*f4ee7fbaSAndroid Build Coastguard Worker help='Decompress input file', 82*f4ee7fbaSAndroid Build Coastguard Worker default=False) 83*f4ee7fbaSAndroid Build Coastguard Worker params = parser.add_argument_group('optional encoder parameters') 84*f4ee7fbaSAndroid Build Coastguard Worker params.add_argument( 85*f4ee7fbaSAndroid Build Coastguard Worker '-m', 86*f4ee7fbaSAndroid Build Coastguard Worker '--mode', 87*f4ee7fbaSAndroid Build Coastguard Worker metavar='MODE', 88*f4ee7fbaSAndroid Build Coastguard Worker type=int, 89*f4ee7fbaSAndroid Build Coastguard Worker choices=[0, 1, 2], 90*f4ee7fbaSAndroid Build Coastguard Worker help='The compression mode can be 0 for generic input, ' 91*f4ee7fbaSAndroid Build Coastguard Worker '1 for UTF-8 encoded text, or 2 for WOFF 2.0 font data. ' 92*f4ee7fbaSAndroid Build Coastguard Worker 'Defaults to 0.') 93*f4ee7fbaSAndroid Build Coastguard Worker params.add_argument( 94*f4ee7fbaSAndroid Build Coastguard Worker '-q', 95*f4ee7fbaSAndroid Build Coastguard Worker '--quality', 96*f4ee7fbaSAndroid Build Coastguard Worker metavar='QUALITY', 97*f4ee7fbaSAndroid Build Coastguard Worker type=int, 98*f4ee7fbaSAndroid Build Coastguard Worker choices=list(range(0, 12)), 99*f4ee7fbaSAndroid Build Coastguard Worker help='Controls the compression-speed vs compression-density ' 100*f4ee7fbaSAndroid Build Coastguard Worker 'tradeoff. The higher the quality, the slower the ' 101*f4ee7fbaSAndroid Build Coastguard Worker 'compression. Range is 0 to 11. Defaults to 11.') 102*f4ee7fbaSAndroid Build Coastguard Worker params.add_argument( 103*f4ee7fbaSAndroid Build Coastguard Worker '--lgwin', 104*f4ee7fbaSAndroid Build Coastguard Worker metavar='LGWIN', 105*f4ee7fbaSAndroid Build Coastguard Worker type=int, 106*f4ee7fbaSAndroid Build Coastguard Worker choices=list(range(10, 25)), 107*f4ee7fbaSAndroid Build Coastguard Worker help='Base 2 logarithm of the sliding window size. Range is ' 108*f4ee7fbaSAndroid Build Coastguard Worker '10 to 24. Defaults to 22.') 109*f4ee7fbaSAndroid Build Coastguard Worker params.add_argument( 110*f4ee7fbaSAndroid Build Coastguard Worker '--lgblock', 111*f4ee7fbaSAndroid Build Coastguard Worker metavar='LGBLOCK', 112*f4ee7fbaSAndroid Build Coastguard Worker type=int, 113*f4ee7fbaSAndroid Build Coastguard Worker choices=[0] + list(range(16, 25)), 114*f4ee7fbaSAndroid Build Coastguard Worker help='Base 2 logarithm of the maximum input block size. ' 115*f4ee7fbaSAndroid Build Coastguard Worker 'Range is 16 to 24. If set to 0, the value will be set based ' 116*f4ee7fbaSAndroid Build Coastguard Worker 'on the quality. Defaults to 0.') 117*f4ee7fbaSAndroid Build Coastguard Worker # set default values using global DEFAULT_PARAMS dictionary 118*f4ee7fbaSAndroid Build Coastguard Worker parser.set_defaults(**DEFAULT_PARAMS) 119*f4ee7fbaSAndroid Build Coastguard Worker 120*f4ee7fbaSAndroid Build Coastguard Worker options = parser.parse_args(args=args) 121*f4ee7fbaSAndroid Build Coastguard Worker 122*f4ee7fbaSAndroid Build Coastguard Worker if options.infile: 123*f4ee7fbaSAndroid Build Coastguard Worker if not os.path.isfile(options.infile): 124*f4ee7fbaSAndroid Build Coastguard Worker parser.error('file "%s" not found' % options.infile) 125*f4ee7fbaSAndroid Build Coastguard Worker with open(options.infile, 'rb') as infile: 126*f4ee7fbaSAndroid Build Coastguard Worker data = infile.read() 127*f4ee7fbaSAndroid Build Coastguard Worker else: 128*f4ee7fbaSAndroid Build Coastguard Worker if sys.stdin.isatty(): 129*f4ee7fbaSAndroid Build Coastguard Worker # interactive console, just quit 130*f4ee7fbaSAndroid Build Coastguard Worker parser.error('no input') 131*f4ee7fbaSAndroid Build Coastguard Worker infile = get_binary_stdio('stdin') 132*f4ee7fbaSAndroid Build Coastguard Worker data = infile.read() 133*f4ee7fbaSAndroid Build Coastguard Worker 134*f4ee7fbaSAndroid Build Coastguard Worker if options.outfile: 135*f4ee7fbaSAndroid Build Coastguard Worker if os.path.isfile(options.outfile) and not options.force: 136*f4ee7fbaSAndroid Build Coastguard Worker parser.error('output file exists') 137*f4ee7fbaSAndroid Build Coastguard Worker outfile = open(options.outfile, 'wb') 138*f4ee7fbaSAndroid Build Coastguard Worker else: 139*f4ee7fbaSAndroid Build Coastguard Worker outfile = get_binary_stdio('stdout') 140*f4ee7fbaSAndroid Build Coastguard Worker 141*f4ee7fbaSAndroid Build Coastguard Worker try: 142*f4ee7fbaSAndroid Build Coastguard Worker if options.decompress: 143*f4ee7fbaSAndroid Build Coastguard Worker data = brotli.decompress(data) 144*f4ee7fbaSAndroid Build Coastguard Worker else: 145*f4ee7fbaSAndroid Build Coastguard Worker data = brotli.compress( 146*f4ee7fbaSAndroid Build Coastguard Worker data, 147*f4ee7fbaSAndroid Build Coastguard Worker mode=options.mode, 148*f4ee7fbaSAndroid Build Coastguard Worker quality=options.quality, 149*f4ee7fbaSAndroid Build Coastguard Worker lgwin=options.lgwin, 150*f4ee7fbaSAndroid Build Coastguard Worker lgblock=options.lgblock) 151*f4ee7fbaSAndroid Build Coastguard Worker except brotli.error as e: 152*f4ee7fbaSAndroid Build Coastguard Worker parser.exit(1, 153*f4ee7fbaSAndroid Build Coastguard Worker 'bro: error: %s: %s' % (e, options.infile or 'sys.stdin')) 154*f4ee7fbaSAndroid Build Coastguard Worker 155*f4ee7fbaSAndroid Build Coastguard Worker outfile.write(data) 156*f4ee7fbaSAndroid Build Coastguard Worker outfile.close() 157*f4ee7fbaSAndroid Build Coastguard Worker 158*f4ee7fbaSAndroid Build Coastguard Worker 159*f4ee7fbaSAndroid Build Coastguard Workerif __name__ == '__main__': 160*f4ee7fbaSAndroid Build Coastguard Worker main() 161