Source code for dolor.types.numeric

"""Types for numbers."""

import math

from .. import util
from .type import Type
from .misc import StructType

[docs]class Boolean(StructType): """A boolean that corresponds to a single byte.""" _default = False fmt = "?"
[docs]class Byte(StructType): """A signed 8-bit integer.""" _default = 0 fmt = "b"
[docs]class UnsignedByte(StructType): """An unsigned 8-bit integer.""" _default = 0 fmt = "B"
[docs]class Short(StructType): """A signed 16-bit integer.""" _default = 0 fmt = "h"
[docs]class UnsignedShort(StructType): """An unsigned 8-bit integer.""" _default = 0 fmt = "H"
[docs]class Int(StructType): """A signed 32-bit integer.""" _default = 0 fmt = "i"
[docs]class UnsignedInt(StructType): """An unsigned 32-bit integer.""" _default = 0 fmt = "I"
[docs]class Long(StructType): """A signed 64-bit integer.""" _default = 0 fmt = "q"
[docs]class UnsignedLong(StructType): """An unsigned 64-bit integer.""" _default = 0 fmt = "Q"
[docs]class Float(StructType): """A 32-bit floating point value.""" _default = 0.0 fmt = "f"
[docs]class Double(StructType): """A 64-bit floating point value.""" _default = 0.0 fmt = "d"
[docs]class VarNum(Type): """A signed, variable-length integer. :meta no-undoc-members: To read the value, a byte is read, where the bottom 7 bits are a portion of the value, and the top bit indicates whether to read another byte and repeat the process. If the bytes read would exceed the number of bytes needed to get the necessary bits (as specified with the :attr:`bits` attribute), then a :exc:`ValueError` will be raised. This is done to prevent a DOS attack that sends bytes that always have the top bit set, leading to an infinite amount of bytes being read. Attributes ---------- bits : :class:`int` The maximum amount of bits before a :exc:`ValueError` is raised. """ _default = 0 bits = None @classmethod def _unpack(cls, buf, *, ctx=None): ret = 0 for i in range(1 + cls.bits // 8): read = UnsignedByte.unpack(buf, ctx=ctx) value = read & 0x7f ret |= value << (7 * i) if read & 0x80 == 0: return util.to_signed(ret, bits=cls.bits) raise ValueError(f"{cls.__name__} is too big") @classmethod def _pack(cls, value, *, ctx=None): ret = b"" for i in range(1 + cls.bits // 8): tmp = value & 0x7f value = util.urshift(value, 7, bits=cls.bits) if value != 0: tmp |= 0x80 ret += UnsignedByte.pack(tmp, ctx=ctx) if value == 0: return ret raise ValueError(f"{cls.__name__} is too big")
[docs]class VarInt(VarNum): """A signed, variable-length 32-bit integer.""" bits = 32
[docs]class VarLong(VarNum): """A signed, variable-length 64-bit integer.""" bits = 64
[docs]class Angle(Type): """Represents an angle. The value is in radians. """ _default = 0 @classmethod def _unpack(cls, buf, *, ctx=None): return math.tau * UnsignedByte.unpack(buf) / 256 @classmethod def _pack(cls, value, *, ctx=None): return UnsignedByte.pack(round(256 * (value % math.tau) / math.tau))