2024-10-11 11:42:28 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
from typing import Any, Optional
|
|
|
|
from math import log2
|
|
|
|
|
|
|
|
class ErrorsManager:
|
|
|
|
|
|
|
|
BASE64:list[str] = [*"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="]
|
|
|
|
|
|
|
|
def __init__(self, alphabet:Optional[str|list[str]|tuple[str]] = None) -> None:
|
|
|
|
|
|
|
|
self.__error:int = 0
|
|
|
|
# self.__re_hexa_error:REPattern|None = None
|
|
|
|
self.__alphabet:list[str] = []
|
|
|
|
|
|
|
|
self.set_alphabet(alphabet)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def is_string(value:Any|None) -> bool:
|
|
|
|
return isinstance(value, str)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def is_array(value:Any|None) -> bool:
|
|
|
|
return isinstance(value, (list, tuple))
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def is_integer(value:Any|None) -> bool:
|
|
|
|
return isinstance(value, int)
|
|
|
|
|
|
|
|
def set_alphabet(self, alphabet:Optional[str] = None) -> int:
|
|
|
|
|
|
|
|
self.__error = (
|
|
|
|
0 if alphabet == None else
|
|
|
|
1 << 2 if not ErrorsManager.is_array(alphabet) and not ErrorsManager.is_string(alphabet) else
|
|
|
|
0) << 1
|
|
|
|
|
|
|
|
if not self.__error:
|
|
|
|
if alphabet:
|
|
|
|
|
|
|
|
original_length:int = len(self.__alphabet)
|
|
|
|
final_length:int = 0
|
|
|
|
character:str
|
|
|
|
i:int
|
|
|
|
|
|
|
|
alphabet = (
|
|
|
|
alphabet if isinstance(alphabet, list) else
|
|
|
|
list(alphabet) if isinstance(alphabet, tuple) else
|
|
|
|
[*alphabet])
|
|
|
|
alphabet = [character for i, character in alphabet if isinstance(character, str) and len(character) == 1 and alphabet.index(character) == i]
|
|
|
|
final_length = len(alphabet)
|
|
|
|
|
|
|
|
self.__error |= (
|
|
|
|
1 << 0 if original_length != final_length else
|
|
|
|
1 << 1 if final_length < 64 else
|
|
|
|
0) << 5
|
|
|
|
|
|
|
|
self.__alphabet = (
|
|
|
|
self.__alphabet if self.__alphabet and len(self.__alphabet) else
|
|
|
|
ErrorsManager.BASE64) if self.__error or not alphabet else alphabet
|
|
|
|
# self.__re_hexa_error = RECompile(r'[^' + alphabet[0] + ']')
|
|
|
|
|
|
|
|
return self.__error
|
|
|
|
|
|
|
|
def to_array(self,
|
|
|
|
code:str|int|list[int]|tuple[int],
|
|
|
|
length:Optional[int] = 1
|
|
|
|
) -> list[int]:
|
|
|
|
|
|
|
|
array:list[int] = []
|
|
|
|
|
|
|
|
if ErrorsManager.is_string(code):
|
|
|
|
|
|
|
|
hexa:str
|
|
|
|
|
|
|
|
for hexa in code:
|
|
|
|
array += [self.__alphabet.index(hexa)]
|
|
|
|
|
|
|
|
elif ErrorsManager.is_integer(code):
|
|
|
|
while code:
|
|
|
|
array += [code & 0x3F]
|
|
|
|
code >>= 6
|
|
|
|
elif ErrorsManager.is_array(code):
|
|
|
|
|
|
|
|
hexa:int
|
|
|
|
|
|
|
|
for hexa in code:
|
|
|
|
array += [hexa]
|
|
|
|
|
|
|
|
while len(array) < length:
|
|
|
|
array += [0]
|
|
|
|
|
|
|
|
return array
|
|
|
|
|
|
|
|
def process(self,
|
|
|
|
error:int|str|list[int]|tuple[int],
|
|
|
|
messages:list[str]|tuple[str]
|
|
|
|
) -> list[str]:
|
|
|
|
|
|
|
|
response:list[str] = []
|
|
|
|
m:int = len(messages)
|
|
|
|
i:int
|
|
|
|
hexa:int
|
|
|
|
|
|
|
|
for i, hexa in enumerate(self.to_array(error)):
|
|
|
|
|
|
|
|
j:int
|
|
|
|
|
|
|
|
for j in range(6):
|
|
|
|
if hexa & (1 << j):
|
|
|
|
|
|
|
|
k:int = j + i * 6
|
|
|
|
|
|
|
|
response += [[k, messages[k] if k < m and messages[k] else "error_message_" + str(k)]]
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
|
|
|
def get_alphabet(self) -> str:
|
|
|
|
return "".join(self.__alphabet)
|
|
|
|
|
|
|
|
def bits(self, code:str|int|list[int]|tuple[int]) -> int|None:
|
|
|
|
|
|
|
|
if ErrorsManager.is_integer(code):
|
|
|
|
return 1 if not code else int(log2(code) + 1)
|
|
|
|
|
|
|
|
if ErrorsManager.is_string(code):
|
|
|
|
code = self.to_array(code)
|
|
|
|
|
|
|
|
if ErrorsManager.is_array(code):
|
|
|
|
|
|
|
|
code = self.compact(code)
|
|
|
|
|
|
|
|
l:int = len(code)
|
|
|
|
|
|
|
|
return 1 if not l or not code[-1] else (l - 1) * 6 + int(log2(code[-1]) + 1)
|
|
|
|
return None
|
2024-11-14 10:58:46 +00:00
|
|
|
|
|
|
|
def get_error(self) -> int:
|
|
|
|
return self.__error | 0
|
2024-10-11 11:42:28 +00:00
|
|
|
|
|
|
|
def to_array_binary(self, code:str|int|list[int]|tuple[int]) -> list[str]:
|
|
|
|
|
|
|
|
hexa:int
|
|
|
|
|
|
|
|
return [("000000" + "{0:b}".format(hexa))[-6:] for hexa in self.to_array(code)]
|
|
|
|
|
|
|
|
def to_integer(self, code:str|int|list[int]|tuple[int]) -> int:
|
|
|
|
if ErrorsManager.is_integer(code):
|
|
|
|
return code
|
|
|
|
if ErrorsManager.is_array(code):
|
|
|
|
|
|
|
|
hexa:int
|
|
|
|
i:int
|
|
|
|
|
|
|
|
return sum([hexa << i * 6 for i, hexa in enumerate(code)])
|
|
|
|
if ErrorsManager.is_string(code):
|
|
|
|
|
|
|
|
hexa:str
|
|
|
|
i:int
|
|
|
|
|
|
|
|
return sum([self.__alphabet.index(hexa) << i * 6 for i, hexa in enumerate(code)])
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def to_string(self,
|
|
|
|
code:str|int|list[int]|tuple[int],
|
|
|
|
length:Optional[int] = 1
|
|
|
|
) -> str:
|
|
|
|
|
|
|
|
string:str = ""
|
|
|
|
|
|
|
|
if ErrorsManager.is_string(code):
|
|
|
|
string += code
|
|
|
|
elif ErrorsManager.is_integer(code):
|
|
|
|
while code:
|
|
|
|
string += self.__alphabet[code & 0x3F]
|
|
|
|
code >>= 6
|
|
|
|
elif ErrorsManager.is_array(code):
|
|
|
|
|
|
|
|
hexa:str
|
|
|
|
|
|
|
|
string = "".join([self.__alphabet[hexa] for hexa in code])
|
|
|
|
|
|
|
|
while len(string) < length:
|
|
|
|
string += self.__alphabet[0]
|
|
|
|
|
|
|
|
return string or self.__alphabet[0]
|
|
|
|
|
|
|
|
def has(self,
|
|
|
|
code:int|str|tuple[int]|list[int],
|
|
|
|
bits:Optional[int|list[int]|tuple[int]] = None
|
|
|
|
) -> bool:
|
|
|
|
if not ErrorsManager.is_integer(bits) and not ErrorsManager.is_array(bits):
|
|
|
|
if ErrorsManager.is_string(code):
|
|
|
|
|
|
|
|
hexa:str
|
|
|
|
|
|
|
|
for hexa in code:
|
|
|
|
if self.__alphabet.index(hexa):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
if ErrorsManager.is_integer(code):
|
|
|
|
return not not code
|
|
|
|
if ErrorsManager.is_array(code):
|
|
|
|
|
|
|
|
hexa:int
|
|
|
|
|
|
|
|
for hexa in code:
|
|
|
|
if hexa:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
error:list[int] = self.to_array(code)
|
|
|
|
bit:int
|
|
|
|
|
|
|
|
if ErrorsManager.is_integer(bits):
|
|
|
|
bits = (bits,)
|
|
|
|
|
|
|
|
for bit in bits[:len(error) - 1]:
|
|
|
|
if error[bit]:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def to_unknown(self, code:Any|None) -> Any|None:
|
|
|
|
return code
|
|
|
|
|
|
|
|
def compact(self, code:int|str|list[int]|tuple[int]) -> int|str|list[int]:
|
|
|
|
if ErrorsManager.is_string(code):
|
|
|
|
while code and code[-1] == self.__alphabet[0]:
|
|
|
|
code = code[:-1]
|
|
|
|
return code or self.__alphabet[0]
|
|
|
|
if ErrorsManager.is_array(code):
|
|
|
|
code = list(code)
|
|
|
|
while len(code) and not code[-1]:
|
|
|
|
code = code[:-1]
|
|
|
|
return code if len(code) else [0]
|
|
|
|
if ErrorsManager.is_integer(code):
|
|
|
|
return code
|
|
|
|
return 0
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def type(self, code:int|str|tuple[int]|list[int]):
|
|
|
|
return (
|
|
|
|
"string" if self.is_string(code) else
|
|
|
|
"integer" if self.is_integer(code) else
|
|
|
|
"array" if self.is_array(code) else
|
|
|
|
"unknown")
|
|
|
|
|
|
|
|
def bitwise(self,
|
|
|
|
code:int|str|tuple[int]|list[int],
|
|
|
|
bits:int
|
|
|
|
) -> str|int|list[int]:
|
|
|
|
|
|
|
|
if not bits or not self.has(code):
|
|
|
|
return code
|
|
|
|
|
|
|
|
reverse:bool = bits < 0
|
|
|
|
|
|
|
|
if reverse:
|
|
|
|
bits *= -1
|
|
|
|
|
|
|
|
start:int = int(bits / 6)
|
|
|
|
rest:int = bits % 6
|
|
|
|
type_method:str = "to_" + ErrorsManager.type(code)
|
|
|
|
|
|
|
|
code = self.to_array(code)
|
|
|
|
|
|
|
|
if reverse:
|
|
|
|
|
|
|
|
code = code[start:]
|
|
|
|
|
|
|
|
if rest:
|
|
|
|
if code:
|
|
|
|
|
|
|
|
r:int = 6 - rest
|
|
|
|
block:int = ~-(1 << rest)
|
2024-11-14 10:58:46 +00:00
|
|
|
i:int
|
2024-10-11 11:42:28 +00:00
|
|
|
hexa:int
|
|
|
|
|
|
|
|
code = [(hexa >> rest) | ((code[i + 1] & block) << r) for i, hexa in enumerate(code[:-1])] + [code[-1] >> rest]
|
|
|
|
|
|
|
|
else:
|
|
|
|
code = [0]
|
|
|
|
|
|
|
|
else:
|
2024-11-14 10:58:46 +00:00
|
|
|
|
|
|
|
i:int
|
2024-10-11 11:42:28 +00:00
|
|
|
|
|
|
|
if rest:
|
|
|
|
|
|
|
|
r:int = 6 - rest
|
|
|
|
mask:int = ~-(1 << 6)
|
|
|
|
|
|
|
|
code = [(code[0] << rest) & mask] + [((hexa << rest) & mask) | (code[i - 1] >> r) for i, hexa in enumerate(code[1:])] + [code[-1] >> r]
|
|
|
|
|
|
|
|
for i in range(start):
|
|
|
|
code = [0] + code
|
|
|
|
|
|
|
|
return getattr(self, type_method)(code)
|
|
|
|
|
|
|
|
def set(self,
|
|
|
|
code:int|str|list[int]|tuple[int],
|
|
|
|
error:int|str|list[int]|tuple[int],
|
|
|
|
bit:Optional[int] = 0,
|
|
|
|
length:Optional[int] = 0
|
|
|
|
) -> str:
|
|
|
|
|
|
|
|
code = self.to_array(code)
|
|
|
|
error = self.to_array(error)
|
|
|
|
|
|
|
|
if bit:
|
|
|
|
error = self.bitwise(error, bit)
|
|
|
|
|
|
|
|
i:int = int(bit / 6)
|
|
|
|
|
|
|
|
if length:
|
|
|
|
|
|
|
|
start:int = bit % 6
|
|
|
|
j:int
|
|
|
|
hexa:int
|
|
|
|
end:int = (start + length) % 6
|
|
|
|
|
|
|
|
for j, hexa in enumerate([~-(1 << start)] + [0 for j in range(int((length + start) / 6) - 1)] + ([~-(1 << (6 - end)) << end] if end else [])):
|
|
|
|
code[j + i] &= hexa
|
|
|
|
|
|
|
|
if self.has(error):
|
|
|
|
|
|
|
|
l:int = len(error)
|
|
|
|
|
|
|
|
while len(code) < i:
|
|
|
|
code += [0]
|
|
|
|
|
|
|
|
m:int = len(code)
|
|
|
|
|
|
|
|
while i < l:
|
|
|
|
if i >= m:
|
|
|
|
code += [0]
|
|
|
|
code[i] = (code[i] or 0) | error[i]
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
return self.compact(self.to_string(code))
|
|
|
|
|
|
|
|
def join(self,
|
|
|
|
code:int|str|list[int]|tuple[int],
|
|
|
|
error:int|str|list[int]|tuple[int],
|
|
|
|
bit:Optional[int] = 0,
|
|
|
|
length:Optional[int] = 0
|
|
|
|
) -> str:
|
|
|
|
return self.set(code, error, bit, length)
|
|
|
|
|
|
|
|
def set_blocks(self,
|
|
|
|
code:str|int|tuple[int]|list[int],
|
|
|
|
blocks:list[str|int|tuple[int]|list[int]]|tuple[str|int|tuple[int]|list[int]],
|
|
|
|
bit:Optional[int] = 0,
|
|
|
|
length:Optional[int] = 0
|
|
|
|
) -> str:
|
|
|
|
|
|
|
|
block:int
|
|
|
|
i:int
|
|
|
|
|
|
|
|
if length:
|
|
|
|
code = self.set(code, 0, bit, length)
|
|
|
|
|
|
|
|
for i, block in blocks:
|
|
|
|
if block:
|
|
|
|
code = self.set(code, block, i + bit)
|
|
|
|
|
|
|
|
return code or self.__alphabet[0]
|
|
|
|
|
|
|
|
def slice(self,
|
|
|
|
code:str|int|list[int]|tuple[int],
|
|
|
|
_from:int,
|
|
|
|
_to:Optional[int] = 0
|
|
|
|
) -> str|int|list[int]|None:
|
|
|
|
if self.has(code):
|
|
|
|
return code
|
|
|
|
|
|
|
|
bits:int = self.bits(code)
|
|
|
|
rest:int
|
|
|
|
|
|
|
|
if _from < 0:
|
|
|
|
_from = bits + _from
|
|
|
|
_to = (
|
|
|
|
bits if _to > bits else
|
|
|
|
bits - _to if _to < 0 else
|
|
|
|
_to) - _from
|
|
|
|
rest = _to %6
|
|
|
|
code = self.bitwise(code, -_from)
|
|
|
|
|
|
|
|
return (
|
|
|
|
code[:int(_to / 6)] + (self.__alphabet[self.__alphabet.index(code[-1]) & ~-(1 << rest)] if rest else "") if ErrorsManager.is_string(code) else
|
|
|
|
code[:int(_to / 6)] + ([code[-1] & ~-(1 << rest)] if rest else []) if ErrorsManager.is_array(code) else
|
|
|
|
code & ~-(1 << _to) if ErrorsManager.is_integer(code) else
|
|
|
|
None)
|
|
|
|
|
|
|
|
def has_range(self, code:int|str|list[int]|tuple[int], _from:int, _to:Optional[int] = 0) -> bool:
|
|
|
|
return self.has(self.slice(code, _from, _to))
|