{strength = 4}
!be 67 <lbl> 44 $178 [(end - lbl) * 8 + strength : 16] $99 <end>
!le [-1993 : 32]
-[-3.141593 : 64]
+[-3.141593 : 64be]
----
+
Output:
+
----
-67 44 b2 00 2c 63 37 f8 ff ff 7f bd c2 82 fb 21
-09 c0
+67 44 b2 00 2c 63 37 f8 ff ff c0 09 21 fb 82 c2
+bd 7f
----
+
The encoded number is the evaluation of a valid {py3} expression which
|[[cur-bo]] Current byte order
|
-The current byte order has an effect on the encoding of
+The current byte order can have an effect on the encoding of
<<fixed-length-number,fixed-length numbers>>.
A <<current-byte-order-setting,current byte order setting>> may change
little endian).
* A <<fixed-length-number,fixed-length number>> (integer or
- floating point) using the <<cur-bo,current byte order>> and of which
- the value is the result of a {py3} expression.
+ floating point), possibly using the <<cur-bo,current byte order>>, and
+ of which the value is the result of a {py3} expression.
* An <<leb128-integer,LEB128 integer>> of which the value is the result
of a {py3} expression.
+
The available length are 32 (_binary32_) and 64 (_binary64_).
-The value is the result of evaluating a {py3} expression using the
-<<cur-bo,current byte order>>.
+The value is the result of evaluating a {py3} expression.
+
+The byte order to use to encode the value is either directly specified
+or is the <<cur-bo,current byte order>>.
A fixed-length number is:
`32` and `64`.
--
+. **Optional**: a suffix of the previous encoding length, without
+ any whitespace, amongst:
++
+--
+[horizontal]
+`be`:: Encode in big endian.
+`le`:: Encode in little endian.
+--
++
+Without this suffix, the encoding byte order is the <<cur-bo,current
+byte order>> which must be defined if the encoding length is greater
+than eight.
+
. The `]` suffix.
====
Input:
----
-!le [345:16]
-!be [-0xabcd:32]
+[345:16le]
+[-0xabcd:32be]
----
Output:
Input:
----
-!le
-[2 * 0.0529 : 32]
+[2 * 0.0529 : 32le]
----
Output:
# Fixed-length number, possibly needing more than one byte.
class _FlNum(_ScalarItem, _RepableItem, _ExprMixin):
def __init__(
- self, expr_str: str, expr: ast.Expression, len: int, text_loc: TextLocation
+ self,
+ expr_str: str,
+ expr: ast.Expression,
+ len: int,
+ bo: Optional[ByteOrder],
+ text_loc: TextLocation,
):
super().__init__(text_loc)
_ExprMixin.__init__(self, expr_str, expr)
self._len = len
+ self._bo = bo
# Length (bits).
@property
def len(self):
return self._len
+ # Byte order override.
+ @property
+ def bo(self):
+ return self._bo
+
@property
def size(self):
return self._len // 8
def __repr__(self):
- return "_FlNum({}, {}, {}, {})".format(
+ return "_FlNum({}, {}, {}, {}, {})".format(
repr(self._expr_str),
repr(self._expr),
repr(self._len),
+ repr(self._bo),
repr(self._text_loc),
)
return expr_str, expr
+ # Returns a `ByteOrder` value from the _valid_ byte order string
+ # `bo_str`.
+ @staticmethod
+ def _bo_from_str(bo_str: str):
+ return {
+ "be": ByteOrder.BE,
+ "le": ByteOrder.LE,
+ }[bo_str]
+
# Patterns for _try_parse_val()
_val_prefix_pat = re.compile(r"\[")
_val_expr_pat = re.compile(r"([^\]:]+):")
- _fl_num_len_fmt_pat = re.compile(r"8|16|24|32|40|48|56|64")
+ _fl_num_len_fmt_pat = re.compile(r"(?P<len>8|16|24|32|40|48|56|64)(?P<bo>[bl]e)?")
_leb128_int_fmt_pat = re.compile(r"(u|s)leb128")
_val_suffix_pat = re.compile(r"]")
# Tries to parse a value (number or string) and format (fixed length
- # in bits, `uleb128`, `sleb128`, or `s:` followed with an encoding
- # name), returning an item on success.
+ # in bits and optional byte order override, `uleb128`, `sleb128`, or
+ # `s:` followed with an encoding name), returning an item on
+ # success.
def _try_parse_val(self):
# Match prefix
if self._try_parse_pat(self._val_prefix_pat) is None:
m_fmt = self._try_parse_pat(self._fl_num_len_fmt_pat)
if m_fmt is not None:
+ # Byte order override
+ if m_fmt.group("bo") is None:
+ bo = None
+ else:
+ bo = self._bo_from_str(m_fmt.group("bo"))
+
# Create fixed-length number item
item = _FlNum(
expr_str,
expr,
- int(m_fmt.group(0)),
+ int(m_fmt.group("len")),
+ bo,
expr_text_loc,
)
else:
else:
# At this point it's invalid
self._raise_error(
- "Expecting a fixed length (multiple of eight bits), `uleb128`, `sleb128`, or `s:` followed with a valid encoding (`u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`)"
+ "Expecting a fixed length (multiple of eight bits and optional `be` or `le`), `uleb128`, `sleb128`, or `s:` followed with a valid encoding (`u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`)"
)
# Expect `]`
item, state, accept_float=True, accept_str=True
)
+ # Returns the effective byte order to use to encode the fixed-length
+ # number `item` considering the current state `state`.
+ @staticmethod
+ def _fl_num_item_effective_bo(item: _FlNum, state: _GenState):
+ return state.bo if item.bo is None else item.bo
+
# Handles the fixed-length number item `item`.
def _handle_fl_num_item(self, item: _FlNum, state: _GenState):
+ # Effective byte order
+ bo = self._fl_num_item_effective_bo(item, state)
+
# Validate current byte order
- if state.bo is None and item.len > 8:
+ if bo is None and item.len > 8:
_raise_error_for_item(
"Current byte order isn't defined at first fixed-length number (`{}`) to encode on more than 8 bits".format(
item.expr_str
return self._item_handlers[type(item)](item, state)
# Generates the data for a fixed-length integer item instance having
- # the value `val` and returns it.
- def _gen_fl_int_item_inst_data(self, val: int, item: _FlNum, state: _GenState):
+ # the value `val` and the effective byte order `bo` and returns it.
+ def _gen_fl_int_item_inst_data(
+ self, val: int, bo: Optional[ByteOrder], item: _FlNum
+ ):
# Validate range
if val < -(2 ** (item.len - 1)) or val > 2**item.len - 1:
_raise_error_for_item(
# value of `item.len`).
data = struct.pack(
"{}{}".format(
- ">" if state.bo in (None, ByteOrder.BE) else "<",
+ ">" if bo in (None, ByteOrder.BE) else "<",
"Q" if val >= 0 else "q",
),
val,
# Keep only the requested length
len_bytes = item.len // 8
- if state.bo in (None, ByteOrder.BE):
+ if bo in (None, ByteOrder.BE):
# Big endian: keep last bytes
data = data[-len_bytes:]
else:
# Little endian: keep first bytes
- assert state.bo == ByteOrder.LE
+ assert bo == ByteOrder.LE
data = data[:len_bytes]
# Return data
return data
# Generates the data for a fixed-length floating point number item
- # instance having the value `val` and returns it.
- def _gen_fl_float_item_inst_data(self, val: float, item: _FlNum, state: _GenState):
+ # instance having the value `val` and the effective byte order `bo`
+ # and returns it.
+ def _gen_fl_float_item_inst_data(
+ self, val: float, bo: Optional[ByteOrder], item: _FlNum
+ ):
# Validate length
if item.len not in (32, 64):
_raise_error_for_item(
# Encode and return result
return struct.pack(
"{}{}".format(
- ">" if state.bo in (None, ByteOrder.BE) else "<",
+ ">" if bo in (None, ByteOrder.BE) else "<",
"f" if item.len == 32 else "d",
),
val,
# Generates the data for a fixed-length number item instance and
# returns it.
def _gen_fl_num_item_inst_data(self, item: _FlNum, state: _GenState):
+ # Effective byte order
+ bo = self._fl_num_item_effective_bo(item, state)
+
# Compute value
val = self._eval_item_expr(item, state, True)
# Handle depending on type
if type(val) is int:
- return self._gen_fl_int_item_inst_data(val, item, state)
+ return self._gen_fl_int_item_inst_data(val, bo, item)
else:
assert type(val) is float
- return self._gen_fl_float_item_inst_data(val, item, state)
+ return self._gen_fl_float_item_inst_data(val, bo, item)
# Generates the data for all the fixed-length number item instances
# and writes it at the correct offset within `self._data`.