summaryrefslogtreecommitdiff
path: root/Com/Simatime/roun
diff options
context:
space:
mode:
Diffstat (limited to 'Com/Simatime/roun')
-rwxr-xr-xCom/Simatime/roun227
1 files changed, 227 insertions, 0 deletions
diff --git a/Com/Simatime/roun b/Com/Simatime/roun
new file mode 100755
index 0000000..addbaf3
--- /dev/null
+++ b/Com/Simatime/roun
@@ -0,0 +1,227 @@
+#!/usr/bin/env python3
+
+import argparse
+
+DESC = """roun is a program that takes numbers and turns them into human-readable
+names, and vice versa.
+
+A 'roun' is the human-readable name. For example:
+
+ roun = hidor-kahih
+ ip = 68.107.97.20
+ hex = 0x446b6114
+ int = 1147887892
+
+The names are inspired by proquints <https://arxiv.org/html/0901.4016>.
+
+Currently the algorithm is the same, but I would like to modify the algorithm at
+some point to be less odd than what proquints currently is. When I get time.
+"""
+
+
+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__":
+ parser = argparse.ArgumentParser(
+ description=DESC, formatter_class=argparse.RawDescriptionHelpFormatter
+ )
+ 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))