+ (* Compute the offset of this field within the match, if it
+ * can be known at compile time.
+ *
+ * Actually, we'll compute two things: the 'natural_field_offset'
+ * is the offset assuming this field had no offset() qualifier
+ * (in other words, its position, immediately following the
+ * preceding field). 'field_offset' is the real field offset
+ * taking into account any offset() qualifier.
+ *
+ * This will be [Some i] if our current offset is known
+ * at compile time, or [None] if we can't determine it.
+ *)
+ let natural_field_offset, field_offset =
+ let has_constant_offset field =
+ match P.get_offset field with
+ | None -> false
+ | Some expr ->
+ match expr_is_constant expr with
+ | None -> false
+ | Some i -> true
+ in
+ let get_constant_offset field =
+ match P.get_offset field with
+ | None -> assert false
+ | Some expr ->
+ match expr_is_constant expr with
+ | None -> assert false
+ | Some i -> i
+ in
+
+ let has_constant_len field =
+ match expr_is_constant (P.get_length field) with
+ | None -> false
+ | Some i when i > 0 -> true
+ | Some _ -> false
+ in
+ let get_constant_len field =
+ match expr_is_constant (P.get_length field) with
+ | None -> assert false
+ | Some i when i > 0 -> i
+ | Some _ -> assert false
+ in
+
+ (* NB: We are looping over the PRECEDING fields in reverse order. *)
+ let rec loop = function
+ (* first field has constant offset 0 *)
+ | [] -> Some 0
+ (* preceding field with constant offset & length *)
+ | f :: _
+ when has_constant_offset f && has_constant_len f ->
+ Some (get_constant_offset f + get_constant_len f)
+ (* preceding field with no offset & constant length *)
+ | f :: fs
+ when P.get_offset f = None && has_constant_len f ->
+ (match loop fs with
+ | None -> None
+ | Some offset -> Some (offset + get_constant_len f))
+ (* else, can't work out the offset *)
+ | _ -> None
+ in
+
+ let natural_field_offset = loop fields in
+
+ let field_offset =
+ match P.get_offset field with
+ | None -> natural_field_offset
+ | Some expr -> (* has an offset() clause *)
+ match expr_is_constant expr with
+ | None -> None
+ | i -> i in
+
+ natural_field_offset, field_offset in
+
+ (* Also compute if the field_offset is known to be byte-aligned at
+ * compile time, which is usually both the common and best possible
+ * case for generating optimized code.
+ *
+ * This is None if not aligned / don't know.
+ * Or Some byte_offset if we can work it out.
+ *)
+ let field_offset_aligned =
+ match field_offset with
+ | None -> None (* unknown, assume no *)
+ | Some off when off land 7 = 0 -> Some (off lsr 3)
+ | Some _ -> None in (* definitely no *)
+
+ (* Now build the code which matches a single field. *)
+ let int_extract_const i endian signed =
+ build_bitstring_call _loc ExtractFunc (Some i) endian signed in
+ let int_extract endian signed =
+ build_bitstring_call _loc ExtractFunc None endian signed in