286 lines
7.7 KiB
Python
286 lines
7.7 KiB
Python
import argparse
|
|
import base64
|
|
import sys
|
|
from base64 import b64decode as b64decodeValidate
|
|
from base64 import encodebytes as b64encodebytes
|
|
from timeit import default_timer as timer
|
|
|
|
import pybase64
|
|
|
|
|
|
def bench_one(duration, data, enc, dec, encbytes, altchars=None, validate=False):
|
|
duration = duration / 2.0
|
|
|
|
if not validate and altchars is None:
|
|
number = 0
|
|
time = timer()
|
|
while True:
|
|
encodedcontent = encbytes(data)
|
|
number += 1
|
|
if timer() - time > duration:
|
|
break
|
|
iter = number
|
|
time = timer()
|
|
while iter > 0:
|
|
encodedcontent = encbytes(data)
|
|
iter -= 1
|
|
time = timer() - time
|
|
print(
|
|
"{:<32s} {:9.3f} MB/s ({:,d} bytes -> {:,d} bytes)".format(
|
|
encbytes.__module__ + "." + encbytes.__name__ + ":",
|
|
((number * len(data)) / (1024.0 * 1024.0)) / time,
|
|
len(data),
|
|
len(encodedcontent),
|
|
)
|
|
)
|
|
|
|
number = 0
|
|
time = timer()
|
|
while True:
|
|
encodedcontent = enc(data, altchars=altchars)
|
|
number += 1
|
|
if timer() - time > duration:
|
|
break
|
|
iter = number
|
|
time = timer()
|
|
while iter > 0:
|
|
encodedcontent = enc(data, altchars=altchars)
|
|
iter -= 1
|
|
time = timer() - time
|
|
print(
|
|
"{:<32s} {:9.3f} MB/s ({:,d} bytes -> {:,d} bytes)".format(
|
|
enc.__module__ + "." + enc.__name__ + ":",
|
|
((number * len(data)) / (1024.0 * 1024.0)) / time,
|
|
len(data),
|
|
len(encodedcontent),
|
|
)
|
|
)
|
|
|
|
number = 0
|
|
time = timer()
|
|
while True:
|
|
decodedcontent = dec(encodedcontent, altchars=altchars, validate=validate)
|
|
number += 1
|
|
if timer() - time > duration:
|
|
break
|
|
iter = number
|
|
time = timer()
|
|
while iter > 0:
|
|
decodedcontent = dec(encodedcontent, altchars=altchars, validate=validate)
|
|
iter -= 1
|
|
time = timer() - time
|
|
print(
|
|
"{0:<32s} {1:9.3f} MB/s ({3:,d} bytes -> {2:,d} bytes)".format(
|
|
dec.__module__ + "." + dec.__name__ + ":",
|
|
((number * len(data)) / (1024.0 * 1024.0)) / time,
|
|
len(data),
|
|
len(encodedcontent),
|
|
)
|
|
)
|
|
assert decodedcontent == data
|
|
|
|
|
|
def readall(file):
|
|
if file == sys.stdin:
|
|
if hasattr(file, "buffer"):
|
|
# Python 3 does not honor the binary flag,
|
|
# read from the underlying buffer
|
|
return file.buffer.read()
|
|
else:
|
|
return file.read()
|
|
# do not close the file
|
|
else:
|
|
try:
|
|
return file.read()
|
|
finally:
|
|
file.close()
|
|
|
|
|
|
def writeall(file, data):
|
|
if file == sys.stdout:
|
|
if hasattr(file, "buffer"):
|
|
# Python 3 does not honor the binary flag,
|
|
# write to the underlying buffer
|
|
file.buffer.write(data)
|
|
else:
|
|
file.write(data)
|
|
# do not close the file
|
|
else:
|
|
try:
|
|
file.write(data)
|
|
finally:
|
|
file.close()
|
|
|
|
|
|
def benchmark(args):
|
|
print(__package__ + " " + pybase64.get_version())
|
|
data = readall(args.input)
|
|
for altchars in [None, b"-_"]:
|
|
for validate in [False, True]:
|
|
print(
|
|
"bench: altchars={:s}, validate={:s}".format(
|
|
repr(altchars), repr(validate)
|
|
)
|
|
)
|
|
bench_one(
|
|
args.duration,
|
|
data,
|
|
pybase64.b64encode,
|
|
pybase64.b64decode,
|
|
pybase64.encodebytes,
|
|
altchars,
|
|
validate,
|
|
)
|
|
bench_one(
|
|
args.duration,
|
|
data,
|
|
base64.b64encode,
|
|
b64decodeValidate,
|
|
b64encodebytes,
|
|
altchars,
|
|
validate,
|
|
)
|
|
|
|
|
|
def encode(args):
|
|
data = readall(args.input)
|
|
data = pybase64.b64encode(data, args.altchars)
|
|
writeall(args.output, data)
|
|
|
|
|
|
def decode(args):
|
|
data = readall(args.input)
|
|
data = pybase64.b64decode(data, args.altchars, args.validate)
|
|
writeall(args.output, data)
|
|
|
|
|
|
class LicenseAction(argparse.Action):
|
|
def __init__(
|
|
self,
|
|
option_strings,
|
|
license=None,
|
|
dest=argparse.SUPPRESS,
|
|
default=argparse.SUPPRESS,
|
|
help="show license information and exit",
|
|
):
|
|
super().__init__(
|
|
option_strings=option_strings,
|
|
dest=dest,
|
|
default=default,
|
|
nargs=0,
|
|
help=help,
|
|
)
|
|
self.license = license
|
|
|
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
print(self.license)
|
|
parser.exit()
|
|
|
|
|
|
def main(args=None):
|
|
# main parser
|
|
parser = argparse.ArgumentParser(
|
|
prog=__package__, description=__package__ + " command-line tool."
|
|
)
|
|
parser.add_argument(
|
|
"-V",
|
|
"--version",
|
|
action="version",
|
|
version=__package__ + " " + pybase64.get_version(),
|
|
)
|
|
parser.add_argument(
|
|
"--license", action=LicenseAction, license=pybase64.get_license_text()
|
|
)
|
|
# create sub-parsers
|
|
subparsers = parser.add_subparsers(help="tool help")
|
|
# benchmark parser
|
|
benchmark_parser = subparsers.add_parser("benchmark", help="-h for usage")
|
|
benchmark_parser.add_argument(
|
|
"-d",
|
|
"--duration",
|
|
metavar="D",
|
|
dest="duration",
|
|
type=float,
|
|
default=1.0,
|
|
help="expected duration for a single encode or decode test",
|
|
)
|
|
benchmark_parser.add_argument(
|
|
"input", type=argparse.FileType("rb"), help="input file used for the benchmark"
|
|
)
|
|
benchmark_parser.set_defaults(func=benchmark)
|
|
# encode parser
|
|
encode_parser = subparsers.add_parser("encode", help="-h for usage")
|
|
encode_parser.add_argument(
|
|
"input", type=argparse.FileType("rb"), help="input file to be encoded"
|
|
)
|
|
group = encode_parser.add_mutually_exclusive_group()
|
|
group.add_argument(
|
|
"-u",
|
|
"--url",
|
|
action="store_const",
|
|
const=b"-_",
|
|
dest="altchars",
|
|
help="use URL encoding",
|
|
)
|
|
group.add_argument(
|
|
"-a",
|
|
"--altchars",
|
|
dest="altchars",
|
|
help="use alternative characters for encoding",
|
|
)
|
|
encode_parser.add_argument(
|
|
"-o",
|
|
"--output",
|
|
dest="output",
|
|
type=argparse.FileType("wb"),
|
|
default=sys.stdout,
|
|
help="encoded output file (default to stdout)",
|
|
)
|
|
encode_parser.set_defaults(func=encode)
|
|
# decode parser
|
|
decode_parser = subparsers.add_parser("decode", help="-h for usage")
|
|
decode_parser.add_argument(
|
|
"input", type=argparse.FileType("rb"), help="input file to be decoded"
|
|
)
|
|
group = decode_parser.add_mutually_exclusive_group()
|
|
group.add_argument(
|
|
"-u",
|
|
"--url",
|
|
action="store_const",
|
|
const=b"-_",
|
|
dest="altchars",
|
|
help="use URL decoding",
|
|
)
|
|
group.add_argument(
|
|
"-a",
|
|
"--altchars",
|
|
dest="altchars",
|
|
help="use alternative characters for decoding",
|
|
)
|
|
decode_parser.add_argument(
|
|
"-o",
|
|
"--output",
|
|
dest="output",
|
|
type=argparse.FileType("wb"),
|
|
default=sys.stdout,
|
|
help="decoded output file (default to stdout)",
|
|
)
|
|
decode_parser.add_argument(
|
|
"--no-validation",
|
|
dest="validate",
|
|
action="store_false",
|
|
help="disable validation of the input data",
|
|
)
|
|
decode_parser.set_defaults(func=decode)
|
|
# ready, parse
|
|
if args is None:
|
|
args = sys.argv[1:]
|
|
if len(args) == 0:
|
|
args = ["-h"]
|
|
args = parser.parse_args(args=args)
|
|
args.func(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|