-
Notifications
You must be signed in to change notification settings - Fork 0
/
shorten.py
68 lines (51 loc) · 1.92 KB
/
shorten.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
DEFAULT_ALPHABET = 'mn6j2c4rv8bpygw95z7hsdaetxuk3fq'
DEFAULT_BLOCK_SIZE = 24
MIN_LENGTH = 5
class UrlEncoder(object):
def __init__(self, alphabet=DEFAULT_ALPHABET, block_size=DEFAULT_BLOCK_SIZE):
if len(set(alphabet)) < 2:
raise AttributeError('Alphabet has to contain at least 2 characters.')
self.alphabet = alphabet
self.block_size = block_size
self.mask = (1 << block_size) - 1
self.mapping = range(block_size)
def encode_url(self, n, min_length=MIN_LENGTH):
return self.enbase(self.encode(n), min_length)
def decode_url(self, n):
return self.decode(self.debase(n))
def encode(self, n):
return (n & ~self.mask) | self._encode(n & self.mask)
def _encode(self, n):
result = 0
for i, b in enumerate(reversed(self.mapping)):
if n & (1 << i):
result |= (1 << b)
return result
def decode(self, n):
return (n & ~self.mask) | self._decode(n & self.mask)
def _decode(self, n):
result = 0
for i, b in enumerate(reversed(self.mapping)):
if n & (1 << b):
result |= (1 << i)
return result
def enbase(self, x, min_length=MIN_LENGTH):
result = self._enbase(x)
padding = self.alphabet[0] * (min_length - len(result))
return '%s%s' % (padding, result)
def _enbase(self, x):
n = len(self.alphabet)
if x < n:
return self.alphabet[x]
return self._enbase(int(x // n)) + self.alphabet[int(x % n)]
def debase(self, x):
n = len(self.alphabet)
result = 0
for i, c in enumerate(reversed(x)):
result += self.alphabet.index(c) * (n ** i)
return result
DEFAULT_ENCODER = UrlEncoder()
def encode_url(n, min_length=MIN_LENGTH):
return DEFAULT_ENCODER.encode_url(n, min_length)
def decode_url(n):
return DEFAULT_ENCODER.decode_url(n)