summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Sima <ben@bsima.me>2019-02-23 19:24:10 -0800
committerBen Sima <ben@bsima.me>2019-02-23 19:54:41 -0800
commit81313e15c70bf1c2fe2cd4720f19ae7a2e952fc7 (patch)
tree3af43ab376c3c4a84420fedf6d800d3c4629b7fa
parentd72dae5772bb16d802bdfb03ede44de51b28debc (diff)
Add roun script for generating identifiers
-rwxr-xr-xchip/roun183
1 files changed, 183 insertions, 0 deletions
diff --git a/chip/roun b/chip/roun
new file mode 100755
index 0000000..19a7438
--- /dev/null
+++ b/chip/roun
@@ -0,0 +1,183 @@
+#!/usr/bin/env python3
+#
+# adapted from proquints <https://arxiv.org/html/0901.4016>
+
+import argparse
+
+def _char_list_to_dict(char_list):
+ return {c: k for (c, k) in zip(char_list, range(len(char_list)))}
+
+UINT_TO_CONSONANT = 'bdfghjklmnprstvz'
+UINT_TO_VOWEL = 'aiou'
+
+CONSONANT_TO_UINT = _char_list_to_dict(UINT_TO_CONSONANT)
+VOWEL_TO_UINT = _char_list_to_dict(UINT_TO_VOWEL)
+
+MASK_LAST4 = 0xF
+MASK_LAST2 = 0x3
+
+CHARS_PER_CHUNK = 5
+
+def _uint16_to_roun(uint16_val):
+ val = uint16_val
+ res = ['?']*CHARS_PER_CHUNK
+ for i in range(CHARS_PER_CHUNK):
+ if i&1:
+ res[-i - 1] = UINT_TO_VOWEL[val & MASK_LAST2]
+ val >>= 2
+ else:
+ res[-i - 1] = UINT_TO_CONSONANT[val & MASK_LAST4]
+ val >>= 4
+ return ''.join(res)
+
+
+def uint_to_roun(uint_val, separator='-'):
+ """Convert 32-bit integer value into corresponding roun string identifier.
+
+ >>> uint_to_roun(0x7F000001, '-')
+ lusab-babad
+
+ :param uint_val: 32-bit integer value to encode
+ :param separator: string to separate character rounets
+ :return: roun string identifier
+ """
+ if uint_val < 0 or uint_val > 0xFFFFFFFF:
+ raise ValueError('uint_val should be in range 0-0xFFFFFFFF')
+ return _uint16_to_roun(uint_val >> 16) + \
+ separator + \
+ _uint16_to_roun(uint_val)
+
+
+def roun_to_uint(roun):
+ """Convert roun string identifier into corresponding 32-bit integer value.
+
+ >>> hex(roun_to_uint('lusab-babad'))
+ '0x7F000001'
+
+ :param roun: roun string identifier to decode
+ :return: 32-bit integer representation of the roun encoded value
+ """
+ nchar = len(roun)
+ if nchar < 10 or nchar > 11:
+ raise ValueError('roun should be in form of two rounets + optional separator')
+
+ res = 0
+ for i, c in enumerate(roun):
+ mag = CONSONANT_TO_UINT.get(c)
+ if mag is not None:
+ res <<= 4
+ res += mag
+ else:
+ mag = VOWEL_TO_UINT.get(c)
+ if mag is not None:
+ res <<= 2
+ res += mag
+ elif i != 5:
+ raise ValueError('bad roun format')
+ return res
+
+def ip2uint_str(ipv4_str):
+ """Convert IPv4 string to 32-bit integer value"""
+ parts = ipv4_str.split('.')
+ if len(parts) != 4:
+ raise ValueError('Expected IPv4 address in form A.B.C.D, got {}'.
+ format(ipv4_str))
+ ip = [0]*4
+ for i, part in enumerate(parts):
+ try:
+ int_part = int(part)
+ except ValueError:
+ raise ValueError('Part {} of IPv4 address is not an integer'.
+ format(i))
+ if int_part < 0 or int_part > 255:
+ raise ValueError('Part {} of IPv4 address is not in range 0-255'.
+ format(i))
+ ip[i] = int_part
+ return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3]
+
+def uint_to_ip_str(uint_val):
+ "Covert 32-bit integer value to IPv4 string"
+ return '{}.{}.{}.{}'.format(
+ (uint_val >> 24)&0xFF,
+ (uint_val >> 16)&0xFF,
+ (uint_val >> 8)&0xFF,
+ uint_val&0xFF)
+
+def uint_to_roun_str(uint_str, separator='-'):
+ return uint_to_roun(int(uint_str), separator)
+
+def roun_to_uint_str(roun):
+ return str(roun_to_uint(roun))
+
+def hex2roun_str(hex_str, separator='-'):
+ return uint_to_roun(int(hex_str, 16), separator)
+
+def roun2hex_str(roun):
+ return hex(roun_to_uint(roun))
+
+def convert(str_val, target=None):
+ """Convert between roun, integer, hex or IPv4 string representations.
+ Tries to guess the representation from input.
+ :param str_val: input representation (string)
+ :return: output representation (string)
+ """
+ if target is not None and target not in {'uint', 'hex', 'ip'}:
+ raise ValueError('Convert target should be one of: uint, hex, ip')
+
+ if target == 'uint':
+ return roun_to_uint_str(str_val)
+
+ if target == 'hex':
+ return roun2hex_str(str_val)
+
+ if target == 'ip':
+ return uint_to_ip_str(roun_to_uint(str_val))
+
+ # try to guess the representation
+ try:
+ return roun_to_uint_str(str_val)
+ except ValueError:
+ pass
+
+ try:
+ return uint_to_roun_str(str_val)
+ except ValueError:
+ pass
+
+ try:
+ return hex2roun_str(str_val)
+ except ValueError:
+ pass
+
+ try:
+ return uint_to_roun_str(ip2uint_str(str_val))
+ except ValueError:
+ pass
+
+ raise ValueError('Unrecognized input format: {}'.format(str_val))
+
+if __name__ == '__main__':
+ desc = 'Convert between [integer, hexadecimal, IPv4 address] <-> roun representations. '
+ parser = argparse.ArgumentParser(description=desc)
+ parser.add_argument('-n', '--uint', action='store_true',
+ help='convert from roun to 32-bit integer', required=False)
+ parser.add_argument('-x', '--hex', action='store_true',
+ help='convert from roun to hexadecimal', required=False)
+ parser.add_argument('-i', '--ip', action='store_true',
+ help='convert from roun to IPv4', required=False)
+ parser.add_argument('val', nargs='?', type=str, default=None,
+ help='value to convert (if not specified, ' \
+ 'IP address of the current host is printed)')
+
+ args = parser.parse_args()
+
+ target = None
+ if args.uint:
+ target = 'uint'
+ elif args.hex:
+ target = 'hex'
+ elif args.ip:
+ target = 'ip'
+
+ res = convert(args.val, target)
+ print('{}'.format(res))