base85.py
90 lines
| 2.1 KiB
| text/x-python
|
PythonLexer
Brendan Cully
|
r7701 | # base85.py: pure python base85 codec | ||
# | ||||
# Copyright (C) 2009 Brendan Cully <brendan@kublai.com> | ||||
# | ||||
Martin Geisler
|
r8225 | # This software may be used and distributed according to the terms of the | ||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Brendan Cully
|
r7701 | |||
Matt Harbison
|
r52756 | from __future__ import annotations | ||
Gregory Szorc
|
r27334 | |||
Brendan Cully
|
r7701 | import struct | ||
Pulkit Goyal
|
r35962 | from .. import pycompat | ||
Augie Fackler
|
r43346 | _b85chars = pycompat.bytestr( | ||
Augie Fackler
|
r43347 | b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef" | ||
b"ghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~" | ||||
Augie Fackler
|
r43346 | ) | ||
Mads Kiilerich
|
r7835 | _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars] | ||
Brendan Cully
|
r7701 | _b85dec = {} | ||
Augie Fackler
|
r43346 | |||
Brendan Cully
|
r7701 | def _mkb85dec(): | ||
Martin Geisler
|
r8632 | for i, c in enumerate(_b85chars): | ||
_b85dec[c] = i | ||||
Brendan Cully
|
r7701 | |||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r52616 | def b85encode(text: bytes, pad: bool = False) -> bytes: | ||
Brendan Cully
|
r7701 | """encode text in base85 format""" | ||
l = len(text) | ||||
r = l % 4 | ||||
if r: | ||||
Augie Fackler
|
r43347 | text += b'\0' * (4 - r) | ||
Brendan Cully
|
r7701 | longs = len(text) >> 2 | ||
Augie Fackler
|
r43347 | words = struct.unpack(b'>%dL' % longs, text) | ||
Brendan Cully
|
r7701 | |||
Augie Fackler
|
r43347 | out = b''.join( | ||
Augie Fackler
|
r43346 | _b85chars[(word // 52200625) % 85] | ||
+ _b85chars2[(word // 7225) % 7225] | ||||
+ _b85chars2[word % 7225] | ||||
for word in words | ||||
) | ||||
Brendan Cully
|
r7701 | |||
if pad: | ||||
return out | ||||
# Trim padding | ||||
olen = l % 4 | ||||
if olen: | ||||
olen += 1 | ||||
Alejandro Santos
|
r9029 | olen += l // 4 * 5 | ||
Brendan Cully
|
r7701 | return out[:olen] | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r52616 | def b85decode(text: bytes) -> bytes: | ||
Brendan Cully
|
r7701 | """decode base85-encoded text""" | ||
if not _b85dec: | ||||
_mkb85dec() | ||||
l = len(text) | ||||
out = [] | ||||
Matt Harbison
|
r52830 | acc = 0 | ||
Brendan Cully
|
r7701 | for i in range(0, len(text), 5): | ||
Augie Fackler
|
r43346 | chunk = text[i : i + 5] | ||
Pulkit Goyal
|
r36209 | chunk = pycompat.bytestr(chunk) | ||
Brendan Cully
|
r7701 | acc = 0 | ||
Martin Geisler
|
r8632 | for j, c in enumerate(chunk): | ||
Brendan Cully
|
r7701 | try: | ||
Martin Geisler
|
r8632 | acc = acc * 85 + _b85dec[c] | ||
Brendan Cully
|
r7701 | except KeyError: | ||
Augie Fackler
|
r43346 | raise ValueError( | ||
r44081 | 'bad base85 character at position %d' % (i + j) | |||
Augie Fackler
|
r43346 | ) | ||
Brendan Cully
|
r7701 | if acc > 4294967295: | ||
r44082 | raise ValueError('Base85 overflow in hunk starting at byte %d' % i) | |||
Brendan Cully
|
r7701 | out.append(acc) | ||
# Pad final chunk if necessary | ||||
cl = l % 5 | ||||
if cl: | ||||
acc *= 85 ** (5 - cl) | ||||
if cl > 1: | ||||
Augie Fackler
|
r43346 | acc += 0xFFFFFF >> (cl - 2) * 8 | ||
Brendan Cully
|
r7701 | out[-1] = acc | ||
Augie Fackler
|
r43347 | out = struct.pack(b'>%dL' % (len(out)), *out) | ||
Brendan Cully
|
r7701 | if cl: | ||
Augie Fackler
|
r43346 | out = out[: -(5 - cl)] | ||
Brendan Cully
|
r7701 | |||
return out | ||||