ErrorsManager/Python/ErrorsManager.py

397 lines
11 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from typing import Any, Optional
from re import Pattern as REPattern
from re import compile as RECompile
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
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)
i:int
test:bool = bits == 35
code = self.to_array(code)
if reverse:
code = code[start:]
if rest:
if code:
r:int = 6 - rest
block:int = ~-(1 << rest)
hexa:int
code = [(hexa >> rest) | ((code[i + 1] & block) << r) for i, hexa in enumerate(code[:-1])] + [code[-1] >> rest]
else:
code = [0]
else:
test and print(["start", start, rest])
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]
test and print(code)
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))