11from __future__ import annotations
22
33import functools
4- from bitstring .exceptions import InterpretError
4+ from bitstring .exceptions import InterpretError , CreationError
55from bitstring .bits import Bits
66from typing import Optional , Dict , List , Any
77from bitstring .utils import parse_name_length_token
88
99
1010class Dtype :
1111
12- __slots__ = ('name' , 'length' , 'read_fn' , 'set_fn' , 'get_fn' , 'is_integer' , 'is_signed' , 'is_float' , 'is_fixed_length' , 'is_unknown_length' )
12+ __slots__ = ('name' , 'length' , 'bitlength' , ' read_fn' , 'set_fn' , 'get_fn' , 'is_integer' , 'is_signed' , 'is_float' , 'is_fixed_length' , 'is_unknown_length' )
1313
1414 def __new__ (cls , token : Optional [str ] = None ) -> Dtype :
1515 if token is not None :
@@ -20,17 +20,23 @@ def __new__(cls, token: Optional[str] = None) -> Dtype:
2020 else :
2121 return super (Dtype , cls ).__new__ (cls )
2222
23+ def __hash__ (self ) -> int :
24+ return 0 # TODO: Optimise :)
25+
2326 @classmethod
24- def create (cls , name : str , length : Optional [int ], set_fn , read_fn , get_fn , is_integer , is_float , is_signed ,
25- is_unknown_length , is_fixed_length ) -> Dtype :
27+ def create (cls , name : str , length : Optional [int ], set_fn , read_fn , get_fn , is_integer : bool , is_float : bool , is_signed : bool ,
28+ is_unknown_length : bool , is_fixed_length : bool , length_multiplier : Optional [ int ] ) -> Dtype :
2629 x = cls .__new__ (cls )
2730 x .name = name
2831 x .length = length
29- x .read_fn = functools .partial (read_fn , length = length )
32+ x .bitlength = length
33+ if length_multiplier is not None :
34+ x .bitlength *= length_multiplier
35+ x .read_fn = functools .partial (read_fn , length = x .bitlength )
3036 if set_fn is None :
3137 x .set_fn = None
3238 else :
33- x .set_fn = functools .partial (set_fn , length = length )
39+ x .set_fn = functools .partial (set_fn , length = x . bitlength )
3440 x .get_fn = get_fn
3541 x .is_integer = is_integer
3642 x .is_signed = is_signed
@@ -57,7 +63,7 @@ class MetaDtype:
5763 # Represents a class of dtypes, such as uint or float, rather than a concrete dtype such as uint8.
5864
5965 def __init__ (self , name : str , description : str , set_fn , read_fn , get_fn , is_integer : bool , is_float : bool , is_signed : bool ,
60- is_unknown_length : bool , length : Optional [int ] = None ):
66+ is_unknown_length : bool , length : Optional [int ] = None , length_multiplier : Optional [ int ] = None ):
6167 # Consistency checks
6268 if is_unknown_length and length is not None :
6369 raise ValueError ("Can't set is_unknown_length and give a value for length." )
@@ -72,6 +78,7 @@ def __init__(self, name: str, description: str, set_fn, read_fn, get_fn, is_inte
7278 self .is_fixed_length = length is not None
7379 self .is_unknown_length = is_unknown_length
7480 self .length = length
81+ self .length_multiplier = length_multiplier
7582
7683 self .set_fn = set_fn
7784 self .read_fn = read_fn # With a start and usually a length
@@ -82,7 +89,7 @@ def getDtype(self, length: Optional[int] = None) -> Dtype:
8289 if not self .is_fixed_length and not self .is_unknown_length :
8390 raise ValueError (f"No length given for dtype '{ self .name } ', and meta type is not fixed length." )
8491 d = Dtype .create (self .name , None , self .set_fn , self .read_fn , self .get_fn , self .is_integer , self .is_float , self .is_signed ,
85- self .is_unknown_length , self .is_fixed_length )
92+ self .is_unknown_length , self .is_fixed_length , self . length_multiplier )
8693 return d
8794 if self .is_unknown_length :
8895 raise ValueError ("Length shouldn't be supplied for dtypes that are variable length." )
@@ -91,7 +98,7 @@ def getDtype(self, length: Optional[int] = None) -> Dtype:
9198 raise ValueError # TODO
9299 length = self .length
93100 d = Dtype .create (self .name , length , self .set_fn , self .read_fn , self .get_fn , self .is_integer , self .is_float , self .is_signed ,
94- self .is_unknown_length , self .is_fixed_length )
101+ self .is_unknown_length , self .is_fixed_length , self . length_multiplier )
95102 return d
96103
97104
@@ -100,6 +107,7 @@ class Register:
100107 _instance : Optional [Register ] = None
101108
102109 def __new__ (cls ) -> Register :
110+ # Singleton. Only one Register instance can ever exist.
103111 if cls ._instance is None :
104112 cls ._instance = super (Register , cls ).__new__ (cls )
105113 cls .name_to_meta_dtype : Dict [str , MetaDtype ] = {}
@@ -122,11 +130,16 @@ def get_dtype(cls, name: str, length: Optional[int]) -> Dtype:
122130 d = meta_type .getDtype (length )
123131 # Test if the length makes sense by trying out the getter. # TODO: Optimise!
124132 if length != 0 and not d .is_unknown_length :
125- temp = Bits (length )
133+ if meta_type .length_multiplier is not None :
134+ length *= meta_type .length_multiplier
135+ try :
136+ temp = Bits (length )
137+ except CreationError as e :
138+ raise ValueError (f"Invalid Dtype: { e } " )
126139 try :
127140 _ = d .read_fn (temp , 0 )
128141 except InterpretError as e :
129- raise ValueError (f"Invalid Dtype: { e . msg } " )
142+ raise ValueError (f"Invalid Dtype: { e } " )
130143 return d
131144
132145 # TODO: This should be only calculated if the register has been altered since the last time it was called.
0 commit comments