diff options
author | Matthias Baumgartner <dev@igsor.net> | 2022-12-08 16:35:20 +0100 |
---|---|---|
committer | Matthias Baumgartner <dev@igsor.net> | 2022-12-08 16:35:20 +0100 |
commit | 547aa08b1f05ec0cdf725c34a7b1d1512b694063 (patch) | |
tree | 0bc59fb50af55b681da77540728cb557ee544496 /bsfs | |
parent | 7eb61d117a995b076d36c55d2c7c268665360813 (diff) | |
download | bsfs-547aa08b1f05ec0cdf725c34a7b1d1512b694063.tar.gz bsfs-547aa08b1f05ec0cdf725c34a7b1d1512b694063.tar.bz2 bsfs-547aa08b1f05ec0cdf725c34a7b1d1512b694063.zip |
remaining essentials: uuid, errors
Diffstat (limited to 'bsfs')
-rw-r--r-- | bsfs/utils/__init__.py | 5 | ||||
-rw-r--r-- | bsfs/utils/errors.py | 38 | ||||
-rw-r--r-- | bsfs/utils/uuid.py | 108 |
3 files changed, 151 insertions, 0 deletions
diff --git a/bsfs/utils/__init__.py b/bsfs/utils/__init__.py index 56a9323..94680ee 100644 --- a/bsfs/utils/__init__.py +++ b/bsfs/utils/__init__.py @@ -8,12 +8,17 @@ Author: Matthias Baumgartner, 2022 import typing # inner-module imports +from . import errors from .commons import typename from .uri import URI +from .uuid import UUID, UCID # exports __all__ : typing.Sequence[str] = ( + 'UCID', 'URI', + 'UUID', + 'errors', 'typename', ) diff --git a/bsfs/utils/errors.py b/bsfs/utils/errors.py new file mode 100644 index 0000000..04561a2 --- /dev/null +++ b/bsfs/utils/errors.py @@ -0,0 +1,38 @@ +""" + +Part of the BlackStar filesystem (bsfs) module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import typing + +# exports +__all__: typing.Sequence[str] = ( + ) + + +## code ## + +class _BSFSError(Exception): + """Generic bsfs error.""" + +class SchemaError(_BSFSError): + """Generic schema errios.""" + +class ConsistencyError(SchemaError): + """A requested operation is inconsistent with the schema.""" + +class InstanceError(SchemaError): + """An instance affected by some operation is inconsistent with the schema.""" + +class PermissionDeniedError(_BSFSError): + """An operation was aborted due to access control restrictions.""" + +class ProgrammingError(_BSFSError): + """An assertion-like error that indicates a code-base issue.""" + +class UnreachableError(ProgrammingError): + """Bravo, you've reached a point in code that should logically not be reachable.""" + +## EOF ## diff --git a/bsfs/utils/uuid.py b/bsfs/utils/uuid.py new file mode 100644 index 0000000..7c39128 --- /dev/null +++ b/bsfs/utils/uuid.py @@ -0,0 +1,108 @@ +""" + +Part of the BlackStar filesystem (bsfs) module. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +from collections import abc +import hashlib +import os +import platform +import random +import threading +import time +import typing +import uuid + +# constants +HASH = hashlib.sha256 + +# exports +__all__: typing.Sequence[str] = [ + 'UCID', + 'UUID', + ] + + +## code ## + +class UUID(abc.Iterator, abc.Callable): + """Generate 256-bit universally unique IDs. + + This is a 'best-effort' kind of implementation that tries to ensure global + uniqueness, even tough actual uniqueness cannot be guaranteed. + The approach is different from python's uuid module (which implements + RFC 4122) in that it generates longer UUIDs and in that it cannot be + reconstructed whether two UUIDs were generated on the same system. + + The ID is a cryptographic hash over several components: + * host + * system + * process + * thread + * random + * time + * cpu cycles + * content (if available) + + """ + + # host identifier + host: str + + # system identifier + system: str + + # process identifier + process: str + + # thread identifier + thread: str + + def __init__(self, seed: typing.Optional[int] = None): + # initialize static components + self.host = str(uuid.getnode()) + self.system = '-'.join(platform.uname()) + self.process = str(os.getpid()) + self.thread = str(threading.get_ident()) + # initialize random component + random.seed(seed) + + def __call__(self, content: typing.Optional[str] = None) -> str: + """Return a globally unique ID.""" + # content component + content = str(content) if content is not None else '' + # time component + now = str(time.time()) + # clock component + clk = str(time.perf_counter()) + # random component + rnd = str(random.random()) + # build the token from all available components + token = self.host + self.system + self.process + self.thread + rnd + now + clk + content + # return the token's hash + return HASH(token.encode('ascii', 'ignore')).hexdigest() + + def __iter__(self) -> typing.Iterator[str]: + """Iterate indefinitely over universally unique IDs.""" + return self + + def __next__(self) -> str: + """Generate universally unique IDs.""" + return self() + + +class UCID(abc.Callable): + """Generate 256-bit content IDs. + + Effectively computes a cryptographic hash over the content. + + """ + @staticmethod + def from_path(path: str) -> str: + """Read the content from a file.""" + with open(path, 'rb') as ifile: + return HASH(ifile.read()).hexdigest() + +## EOF ## |