86 lines
1.7 KiB
Python
86 lines
1.7 KiB
Python
import struct
|
|
from decimal import Decimal
|
|
|
|
def pwp_number(fmt, value_type):
|
|
size = struct.calcsize(fmt)
|
|
|
|
def decorator(cls):
|
|
def __init__(self, value):
|
|
self.value = value_type(value)
|
|
|
|
def to_com(self):
|
|
return struct.pack(fmt, self.value)
|
|
|
|
@classmethod
|
|
def from_com(cls, data, offset=0):
|
|
value = struct.unpack_from(fmt, data, offset)[0]
|
|
return cls(value), offset + size
|
|
|
|
cls.__init__ = __init__
|
|
cls.to_com = to_com
|
|
cls.from_com = from_com
|
|
|
|
return cls
|
|
|
|
return decorator
|
|
|
|
# Unsigned integers
|
|
@pwp_number("<B", int)
|
|
class u8: pass
|
|
|
|
@pwp_number("<H", int)
|
|
class u16: pass
|
|
|
|
@pwp_number("<I", int)
|
|
class u32: pass
|
|
|
|
@pwp_number("<Q", int)
|
|
class u64: pass
|
|
|
|
# Signed integers
|
|
@pwp_number("<b", int)
|
|
class i8: pass
|
|
|
|
@pwp_number("<h", int)
|
|
class i16: pass
|
|
|
|
@pwp_number("<i", int)
|
|
class i32: pass
|
|
|
|
@pwp_number("<q", int)
|
|
class i64: pass
|
|
|
|
# Floating point
|
|
@pwp_number("<f", float)
|
|
class f32: pass
|
|
|
|
@pwp_number("<d", float)
|
|
class f64: pass
|
|
|
|
class decimal:
|
|
size = struct.calcsize("<qb")
|
|
|
|
def __init__(self, value):
|
|
self.value = Decimal(value)
|
|
|
|
def to_com(self):
|
|
sign, digits, exponent = self.value.as_tuple()
|
|
|
|
if not isinstance(exponent, int):
|
|
raise ValueError("Cannot serialize NaN or Infinity")
|
|
|
|
scale = -exponent
|
|
integer = int("".join(map(str, digits)))
|
|
|
|
if sign:
|
|
integer = -integer
|
|
|
|
return struct.pack("<qb", integer, scale)
|
|
|
|
@classmethod
|
|
def from_com(cls, data, offset=0):
|
|
integer, scale = struct.unpack_from("<qb", data, offset)
|
|
|
|
value = Decimal(integer) / (Decimal(10) ** scale)
|
|
|
|
return cls(value), offset + cls.size |