Skip to content

Commit

Permalink
Merge branch 'release/0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
suminb committed Apr 11, 2017
2 parents c346cf3 + 7c30852 commit 79d607f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 4 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
dist: trusty
sudo: false
language: python

python:
Expand All @@ -7,7 +9,7 @@ python:
- "3.4"
- "3.5"
- "3.6"
- "pypy"
# - "pypy" # disable pypy builds until supported by trusty containers

install:
- pip install --requirement requirements.txt
Expand Down
61 changes: 58 additions & 3 deletions base62.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,31 @@

__author__ = 'Sumin Byeon'
__email__ = 'suminb@gmail.com'
__version__ = '0.1.3'
__version__ = '0.2.0'

CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
BASE = 62
DEFAULT_ENCODING = 'utf-8'


def bytes_to_int(s, byteorder='big', signed=False):
"""Converts a byte array to an integer value. Python 3 comes with a
built-in function to do this, but we would like to keep our code Python 2
compatible.
"""
try:
return int.from_bytes(s, byteorder, signed=signed)
except AttributeError:
# For Python 2.x
if byteorder != 'big' or signed:
raise NotImplementedError()

# NOTE: This won't work if a generator is given
n = len(s)
ds = (x << (8 * (n - 1 - i)) for i, x in enumerate(bytearray(s)))

return sum(ds)


def encode(n):
"""Encodes a given integer ``n``."""
Expand All @@ -27,6 +48,16 @@ def encode(n):

return ''.join(s)


def encodebytes(s):
"""Encode a bytestring into a base62 string.
:param s: A byte array
"""
_check_bytes_type(s)
return encode(bytes_to_int(s))


def decode(b):
"""Encodes a base62 encoded value ``b``."""

Expand All @@ -35,11 +66,28 @@ def decode(b):

l, i, v = len(b), 0, 0
for x in b:
v += __value__(x) * (BASE**(l-(i+1)))
v += __value__(x) * (BASE ** (l - (i + 1)))
i += 1

return v


def decodebytes(s):
"""Decodes a string of base62 data into a bytes object.
:param s: A string to be decoded in base62
:rtype: bytes
"""
decoded = decode(s)
buf = bytearray()
while decoded > 0:
buf.append(decoded & 0xff)
decoded //= 256
buf.reverse()

return bytes(buf)


def __value__(ch):
"""Decodes an individual digit of a base62 encoded string."""

Expand All @@ -50,4 +98,11 @@ def __value__(ch):
elif ch in 'abcdefghijklmnopqrstuvwxyz':
return ord(ch) - ord('a') + 36
else:
raise RuntimeError('base62: Invalid character (%s)' % ch)
raise ValueError('base62: Invalid character (%s)' % ch)


def _check_bytes_type(s):
"""Checks if the input is in an appropriate type."""
if not isinstance(s, bytes):
msg = 'expected bytes-like object, not %s' % s.__class__.__name__
raise TypeError(msg)
39 changes: 39 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,47 @@
import pytest

import base62


bytes_int_pairs = [
(b'\x00', 0),
(b'\x01', 1),
(b'\x01\x01', 0x0101),
(b'\xff\xff', 0xffff),
(b'\x01\x01\x01', 0x010101),
(b'\x01\x02\x03\x04\x05\x06\x07\x08', 0x0102030405060708),
]


def test_basic():
assert base62.encode(0) == '0'
assert base62.decode('0') == 0

assert base62.encode(34441886726) == 'base62'
assert base62.decode('base62') == 34441886726


@pytest.mark.parametrize('b, i', bytes_int_pairs)
def test_bytes_to_int(b, i):
assert base62.bytes_to_int(b) == i


@pytest.mark.parametrize('b, i', bytes_int_pairs)
def test_encodebytes(b, i):
assert base62.encodebytes(b) == base62.encode(i)


@pytest.mark.parametrize('s', ['0', '1', 'a', 'z', 'ykzvd7ga'])
def test_decodebytes(s):
assert base62.bytes_to_int(base62.decodebytes(s)) == base62.decode(s)


@pytest.mark.parametrize('input_bytes', [
b'', b'0', b'bytes to encode', b'\x01\x00\x80'])
def test_roundtrip(input_bytes):
"""Ensures type consistency. Suggested by @dhimmel"""
base62_encoded = base62.encodebytes(input_bytes)
assert isinstance(base62_encoded, str)
output_bytes = base62.decodebytes(base62_encoded)
assert isinstance(output_bytes, bytes)
assert input_bytes == output_bytes

0 comments on commit 79d607f

Please sign in to comment.