///|
const TPI_HEADER_SIZE : Int32 = 56
///|
const TPI_TYPE_INDEX_BEGIN : UInt32 = 0x1000U
///|
const LF_MODIFIER : UInt16 = 0x1001
///|
const LF_POINTER : UInt16 = 0x1002
///|
const LF_PROCEDURE : UInt16 = 0x1008
///|
const LF_MFUNCTION : UInt16 = 0x1009
///|
const LF_LABEL : UInt16 = 0x000E
///|
const LF_ARGLIST : UInt16 = 0x1201
///|
const LF_FIELDLIST : UInt16 = 0x1203
///|
const LF_BITFIELD : UInt16 = 0x1205
///|
const LF_ARRAY : UInt16 = 0x1503
///|
const LF_CLASS : UInt16 = 0x1504
///|
const LF_STRUCTURE : UInt16 = 0x1505
///|
const LF_UNION : UInt16 = 0x1506
///|
const LF_ENUM : UInt16 = 0x1507
///|
const LF_ALIAS : UInt16 = 0x150A
///|
const LF_TYPESERVER2 : UInt16 = 0x1515
///|
const LF_INTERFACE : UInt16 = 0x1519
///|
const LF_FUNC_ID : UInt16 = 0x1601
///|
const LF_MFUNC_ID : UInt16 = 0x1602
///|
const LF_BUILDINFO : UInt16 = 0x1603
///|
const LF_SUBSTR_LIST : UInt16 = 0x1604
///|
const LF_STRING_ID : UInt16 = 0x1605
///|
const LF_UDT_SRC_LINE : UInt16 = 0x1606
///|
const LF_UDT_MOD_SRC_LINE : UInt16 = 0x1607
///|
/// The direction represented by a simple CodeView type-index mode.
pub enum SimpleTypeMode {
Direct
NearPointer
FarPointer
HugePointer
Pointer32
FarPointer32
Pointer64
Reserved
}
///|
/// The logical category of a simple CodeView type.
pub enum SimpleTypeKind {
NoType
Void
SignedInteger
UnsignedInteger
Boolean
Real
Character
WideCharacter
Bit
Unknown
}
///|
/// A decoded simple type index below 0x1000.
pub struct SimpleType {
raw : UInt32
base : Int32
mode : SimpleTypeMode
kind : SimpleTypeKind
name : String
byte_size : Int32
}
///|
pub fn SimpleType::raw(self : SimpleType) -> UInt32 {
self.raw
}
///|
pub fn SimpleType::base(self : SimpleType) -> Int32 {
self.base
}
///|
pub fn SimpleType::mode(self : SimpleType) -> SimpleTypeMode {
self.mode
}
///|
pub fn SimpleType::kind(self : SimpleType) -> SimpleTypeKind {
self.kind
}
///|
pub fn SimpleType::name(self : SimpleType) -> String {
self.name
}
///|
pub fn SimpleType::byte_size(self : SimpleType) -> Int32 {
self.byte_size
}
///|
pub fn SimpleType::is_pointer(self : SimpleType) -> Bool {
match self.mode {
Direct => false
_ => true
}
}
///|
/// Resource limits for a TPI or IPI type stream.
pub struct TpiLimits {
max_stream_bytes : Int32
max_records : Int32
max_record_bytes : Int32
max_type_refs : Int32
}
///|
pub fn TpiLimits::default() -> TpiLimits {
TpiLimits::{
max_stream_bytes: 64 * 1024 * 1024,
max_records: 1_000_000,
max_record_bytes: 4 * 1024 * 1024,
max_type_refs: 1_000_000,
}
}
///|
/// The fixed 56-byte header shared by TPI and IPI streams.
pub struct TpiHeader {
version : UInt32
header_size : Int32
type_index_begin : UInt32
type_index_end : UInt32
type_record_bytes : Int32
hash_stream_index : Int32
hash_aux_stream_index : Int32
hash_key_size : UInt32
hash_bucket_count : UInt32
hash_value_offset : Int32
hash_value_bytes : Int32
index_offset_offset : Int32
index_offset_bytes : Int32
hash_adjust_offset : Int32
hash_adjust_bytes : Int32
}
///|
pub fn TpiHeader::version(self : TpiHeader) -> UInt32 {
self.version
}
///|
pub fn TpiHeader::header_size(self : TpiHeader) -> Int32 {
self.header_size
}
///|
pub fn TpiHeader::type_index_begin(self : TpiHeader) -> UInt32 {
self.type_index_begin
}
///|
pub fn TpiHeader::type_index_end(self : TpiHeader) -> UInt32 {
self.type_index_end
}
///|
pub fn TpiHeader::type_record_bytes(self : TpiHeader) -> Int32 {
self.type_record_bytes
}
///|
pub fn TpiHeader::type_record_count(self : TpiHeader) -> UInt32 {
self.type_index_end - self.type_index_begin
}
///|
pub fn TpiHeader::hash_stream_index(self : TpiHeader) -> Int32 {
self.hash_stream_index
}
///|
pub fn TpiHeader::hash_aux_stream_index(self : TpiHeader) -> Int32 {
self.hash_aux_stream_index
}
///|
pub fn TpiHeader::hash_key_size(self : TpiHeader) -> UInt32 {
self.hash_key_size
}
///|
pub fn TpiHeader::hash_bucket_count(self : TpiHeader) -> UInt32 {
self.hash_bucket_count
}
///|
pub fn TpiHeader::hash_value_offset(self : TpiHeader) -> Int32 {
self.hash_value_offset
}
///|
pub fn TpiHeader::hash_value_bytes(self : TpiHeader) -> Int32 {
self.hash_value_bytes
}
///|
pub fn TpiHeader::index_offset_offset(self : TpiHeader) -> Int32 {
self.index_offset_offset
}
///|
pub fn TpiHeader::index_offset_bytes(self : TpiHeader) -> Int32 {
self.index_offset_bytes
}
///|
pub fn TpiHeader::hash_adjust_offset(self : TpiHeader) -> Int32 {
self.hash_adjust_offset
}
///|
pub fn TpiHeader::hash_adjust_bytes(self : TpiHeader) -> Int32 {
self.hash_adjust_bytes
}
///|
/// A CodeView numeric leaf used for sizes, offsets, and enumerator values.
pub enum NumericValue {
Signed(Int64)
Unsigned(UInt64)
Opaque(UInt16, Bytes)
}
///|
pub fn NumericValue::as_signed(self : NumericValue) -> Int64? {
match self {
Signed(value) => Some(value)
Unsigned(value) =>
if value <= 0x7FFFFFFFFFFFFFFFUL {
Some(value.reinterpret_as_int64())
} else {
None
}
Opaque(_, _) => None
}
}
///|
pub fn NumericValue::as_unsigned(self : NumericValue) -> UInt64? {
match self {
Signed(value) =>
if value >= 0L {
Some(value.reinterpret_as_uint64())
} else {
None
}
Unsigned(value) => Some(value)
Opaque(_, _) => None
}
}
///|
pub struct ModifierType {
modified_type : UInt32
modifiers : UInt16
}
///|
pub fn ModifierType::modified_type(self : ModifierType) -> UInt32 {
self.modified_type
}
///|
pub fn ModifierType::modifiers(self : ModifierType) -> UInt16 {
self.modifiers
}
///|
pub fn ModifierType::is_const(self : ModifierType) -> Bool {
(self.modifiers & 0x0001) != 0
}
///|
pub fn ModifierType::is_volatile(self : ModifierType) -> Bool {
(self.modifiers & 0x0002) != 0
}
///|
pub fn ModifierType::is_unaligned(self : ModifierType) -> Bool {
(self.modifiers & 0x0004) != 0
}
///|
pub struct PointerType {
referent_type : UInt32
attributes : UInt32
containing_type : UInt32?
member_representation : UInt16?
}
///|
pub fn PointerType::referent_type(self : PointerType) -> UInt32 {
self.referent_type
}
///|
pub fn PointerType::attributes(self : PointerType) -> UInt32 {
self.attributes
}
///|
pub fn PointerType::kind(self : PointerType) -> UInt32 {
self.attributes & 0x1FU
}
///|
pub fn PointerType::mode(self : PointerType) -> UInt32 {
(self.attributes >> 5) & 0x07U
}
///|
pub fn PointerType::byte_size(self : PointerType) -> Int32 {
((self.attributes >> 13) & 0xFFU).reinterpret_as_int()
}
///|
pub fn PointerType::containing_type(self : PointerType) -> UInt32? {
self.containing_type
}
///|
pub fn PointerType::member_representation(self : PointerType) -> UInt16? {
self.member_representation
}
///|
pub fn PointerType::is_const(self : PointerType) -> Bool {
(self.attributes & 0x00000400U) != 0U
}
///|
pub fn PointerType::is_volatile(self : PointerType) -> Bool {
(self.attributes & 0x00000200U) != 0U
}
///|
pub fn PointerType::is_unaligned(self : PointerType) -> Bool {
(self.attributes & 0x00000800U) != 0U
}
///|
pub fn PointerType::is_restrict(self : PointerType) -> Bool {
(self.attributes & 0x00001000U) != 0U
}
///|
pub struct ProcedureType {
return_type : UInt32
calling_convention : Int32
function_options : Int32
parameter_count : UInt16
argument_list : UInt32
}
///|
pub fn ProcedureType::return_type(self : ProcedureType) -> UInt32 {
self.return_type
}
///|
pub fn ProcedureType::calling_convention(self : ProcedureType) -> Int32 {
self.calling_convention
}
///|
pub fn ProcedureType::function_options(self : ProcedureType) -> Int32 {
self.function_options
}
///|
pub fn ProcedureType::parameter_count(self : ProcedureType) -> UInt16 {
self.parameter_count
}
///|
pub fn ProcedureType::argument_list(self : ProcedureType) -> UInt32 {
self.argument_list
}
///|
pub struct MemberFunctionType {
return_type : UInt32
class_type : UInt32
this_type : UInt32
calling_convention : Int32
function_options : Int32
parameter_count : UInt16
argument_list : UInt32
this_adjustment : Int32
}
///|
pub fn MemberFunctionType::return_type(self : MemberFunctionType) -> UInt32 {
self.return_type
}
///|
pub fn MemberFunctionType::class_type(self : MemberFunctionType) -> UInt32 {
self.class_type
}
///|
pub fn MemberFunctionType::this_type(self : MemberFunctionType) -> UInt32 {
self.this_type
}
///|
pub fn MemberFunctionType::calling_convention(
self : MemberFunctionType,
) -> Int32 {
self.calling_convention
}
///|
pub fn MemberFunctionType::function_options(self : MemberFunctionType) -> Int32 {
self.function_options
}
///|
pub fn MemberFunctionType::parameter_count(self : MemberFunctionType) -> UInt16 {
self.parameter_count
}
///|
pub fn MemberFunctionType::argument_list(self : MemberFunctionType) -> UInt32 {
self.argument_list
}
///|
pub fn MemberFunctionType::this_adjustment(self : MemberFunctionType) -> Int32 {
self.this_adjustment
}
///|
pub struct ArgListType {
type_indices : Array[UInt32]
}
///|
pub fn ArgListType::count(self : ArgListType) -> Int32 {
self.type_indices.length()
}
///|
pub fn ArgListType::type_indices(self : ArgListType) -> Array[UInt32] {
self.type_indices.copy()
}
///|
pub fn ArgListType::type_index(self : ArgListType, index : Int32) -> UInt32? {
if index < 0 || index >= self.type_indices.length() {
None
} else {
Some(self.type_indices[index])
}
}
///|
pub struct FieldListType {
bytes : Bytes
members : Array[FieldListMember]
continuation : UInt32?
}
///|
pub fn FieldListType::bytes(self : FieldListType) -> Bytes {
self.bytes
}
///|
pub fn FieldListType::member_count(self : FieldListType) -> Int32 {
self.members.length()
}
///|
pub fn FieldListType::member_at(
self : FieldListType,
index : Int32,
) -> FieldListMember? {
if index < 0 || index >= self.members.length() {
None
} else {
Some(self.members[index])
}
}
///|
pub fn FieldListType::members(self : FieldListType) -> Array[FieldListMember] {
self.members.copy()
}
///|
pub fn FieldListType::continuation(self : FieldListType) -> UInt32? {
self.continuation
}
///|
pub struct BitFieldType {
underlying_type : UInt32
bit_size : Int32
bit_offset : Int32
}
///|
pub fn BitFieldType::underlying_type(self : BitFieldType) -> UInt32 {
self.underlying_type
}
///|
pub fn BitFieldType::bit_size(self : BitFieldType) -> Int32 {
self.bit_size
}
///|
pub fn BitFieldType::bit_offset(self : BitFieldType) -> Int32 {
self.bit_offset
}
///|
pub struct ArrayType {
element_type : UInt32
index_type : UInt32
size : NumericValue
name : String
}
///|
pub fn ArrayType::element_type(self : ArrayType) -> UInt32 {
self.element_type
}
///|
pub fn ArrayType::index_type(self : ArrayType) -> UInt32 {
self.index_type
}
///|
pub fn ArrayType::size(self : ArrayType) -> NumericValue {
self.size
}
///|
pub fn ArrayType::name(self : ArrayType) -> String {
self.name
}
///|
pub struct AggregateType {
leaf_kind : UInt16
member_count : UInt16
properties : UInt16
field_list : UInt32
derived_from : UInt32
vtable_shape : UInt32
size : NumericValue
name : String
unique_name : String?
}
///|
pub fn AggregateType::leaf_kind(self : AggregateType) -> UInt16 {
self.leaf_kind
}
///|
pub fn AggregateType::member_count(self : AggregateType) -> UInt16 {
self.member_count
}
///|
pub fn AggregateType::properties(self : AggregateType) -> UInt16 {
self.properties
}
///|
pub fn AggregateType::field_list(self : AggregateType) -> UInt32 {
self.field_list
}
///|
pub fn AggregateType::derived_from(self : AggregateType) -> UInt32 {
self.derived_from
}
///|
pub fn AggregateType::vtable_shape(self : AggregateType) -> UInt32 {
self.vtable_shape
}
///|
pub fn AggregateType::size(self : AggregateType) -> NumericValue {
self.size
}
///|
pub fn AggregateType::name(self : AggregateType) -> String {
self.name
}
///|
pub fn AggregateType::unique_name(self : AggregateType) -> String? {
self.unique_name
}
///|
pub fn AggregateType::is_forward_reference(self : AggregateType) -> Bool {
(self.properties & 0x0080) != 0
}
///|
pub fn AggregateType::has_unique_name(self : AggregateType) -> Bool {
(self.properties & 0x0200) != 0
}
///|
pub struct UnionType {
member_count : UInt16
properties : UInt16
field_list : UInt32
size : NumericValue
name : String
unique_name : String?
}
///|
pub fn UnionType::member_count(self : UnionType) -> UInt16 {
self.member_count
}
///|
pub fn UnionType::properties(self : UnionType) -> UInt16 {
self.properties
}
///|
pub fn UnionType::field_list(self : UnionType) -> UInt32 {
self.field_list
}
///|
pub fn UnionType::size(self : UnionType) -> NumericValue {
self.size
}
///|
pub fn UnionType::name(self : UnionType) -> String {
self.name
}
///|
pub fn UnionType::unique_name(self : UnionType) -> String? {
self.unique_name
}
///|
pub struct EnumType {
member_count : UInt16
properties : UInt16
underlying_type : UInt32
field_list : UInt32
name : String
unique_name : String?
}
///|
pub fn EnumType::member_count(self : EnumType) -> UInt16 {
self.member_count
}
///|
pub fn EnumType::properties(self : EnumType) -> UInt16 {
self.properties
}
///|
pub fn EnumType::underlying_type(self : EnumType) -> UInt32 {
self.underlying_type
}
///|
pub fn EnumType::field_list(self : EnumType) -> UInt32 {
self.field_list
}
///|
pub fn EnumType::name(self : EnumType) -> String {
self.name
}
///|
pub fn EnumType::unique_name(self : EnumType) -> String? {
self.unique_name
}
///|
pub struct AliasType {
underlying_type : UInt32
name : String
}
///|
pub fn AliasType::underlying_type(self : AliasType) -> UInt32 {
self.underlying_type
}
///|
pub fn AliasType::name(self : AliasType) -> String {
self.name
}
///|
pub struct LabelType {
mode : UInt16
}
///|
pub fn LabelType::mode(self : LabelType) -> UInt16 {
self.mode
}
///|
pub struct FunctionIdType {
parent_scope : UInt32
function_type : UInt32
name : String
}
///|
pub fn FunctionIdType::parent_scope(self : FunctionIdType) -> UInt32 {
self.parent_scope
}
///|
pub fn FunctionIdType::function_type(self : FunctionIdType) -> UInt32 {
self.function_type
}
///|
pub fn FunctionIdType::name(self : FunctionIdType) -> String {
self.name
}
///|
pub struct MemberFunctionIdType {
class_type : UInt32
function_type : UInt32
name : String
}
///|
pub fn MemberFunctionIdType::class_type(self : MemberFunctionIdType) -> UInt32 {
self.class_type
}
///|
pub fn MemberFunctionIdType::function_type(
self : MemberFunctionIdType,
) -> UInt32 {
self.function_type
}
///|
pub fn MemberFunctionIdType::name(self : MemberFunctionIdType) -> String {
self.name
}
///|
pub struct StringIdType {
id : UInt32
value : String
}
///|
pub fn StringIdType::id(self : StringIdType) -> UInt32 {
self.id
}
///|
pub fn StringIdType::value(self : StringIdType) -> String {
self.value
}
///|
pub struct BuildInfoType {
argument_indices : Array[UInt32]
}
///|
pub fn BuildInfoType::argument_indices(self : BuildInfoType) -> Array[UInt32] {
self.argument_indices.copy()
}
///|
pub struct UdtSourceLineType {
udt : UInt32
source_file : UInt32
line_number : UInt32
module_index : UInt32?
}
///|
pub fn UdtSourceLineType::udt(self : UdtSourceLineType) -> UInt32 {
self.udt
}
///|
pub fn UdtSourceLineType::source_file(self : UdtSourceLineType) -> UInt32 {
self.source_file
}
///|
pub fn UdtSourceLineType::line_number(self : UdtSourceLineType) -> UInt32 {
self.line_number
}
///|
pub fn UdtSourceLineType::module_index(self : UdtSourceLineType) -> UInt32? {
self.module_index
}
///|
pub struct TypeServer2Type {
guid : Bytes
age : UInt32
name : String
}
///|
pub fn TypeServer2Type::guid(self : TypeServer2Type) -> Bytes {
self.guid
}
///|
pub fn TypeServer2Type::age(self : TypeServer2Type) -> UInt32 {
self.age
}
///|
pub fn TypeServer2Type::name(self : TypeServer2Type) -> String {
self.name
}
///|
pub struct UnknownType {
kind : UInt16
payload : Bytes
}
///|
pub fn UnknownType::kind(self : UnknownType) -> UInt16 {
self.kind
}
///|
pub fn UnknownType::payload(self : UnknownType) -> Bytes {
self.payload
}
///|
/// The decoded representation of one CodeView type record.
pub enum TypeDetails {
Modifier(ModifierType)
Pointer(PointerType)
Procedure(ProcedureType)
MemberFunction(MemberFunctionType)
ArgList(ArgListType)
FieldList(FieldListType)
BitField(BitFieldType)
Array(ArrayType)
Aggregate(AggregateType)
Union(UnionType)
Enum(EnumType)
Alias(AliasType)
Label(LabelType)
FunctionId(FunctionIdType)
MemberFunctionId(MemberFunctionIdType)
StringId(StringIdType)
BuildInfo(BuildInfoType)
UdtSourceLine(UdtSourceLineType)
TypeServer2(TypeServer2Type)
Unknown(UnknownType)
}
///|
/// One indexed TPI or IPI record.
pub struct TypeRecord {
type_index : UInt32
kind : UInt16
payload : Bytes
details : TypeDetails
}
///|
pub fn TypeRecord::type_index(self : TypeRecord) -> UInt32 {
self.type_index
}
///|
pub fn TypeRecord::kind(self : TypeRecord) -> UInt16 {
self.kind
}
///|
pub fn TypeRecord::payload(self : TypeRecord) -> Bytes {
self.payload
}
///|
pub fn TypeRecord::details(self : TypeRecord) -> TypeDetails {
self.details
}
///|
/// A parsed TPI or IPI stream.
pub struct TypeTable {
data : Bytes
header : TpiHeader
records : Array[TypeRecord]
}
///|
pub fn TypeTable::data(self : TypeTable) -> Bytes {
self.data
}
///|
pub fn TypeTable::header(self : TypeTable) -> TpiHeader {
self.header
}
///|
pub fn TypeTable::record_count(self : TypeTable) -> Int32 {
self.records.length()
}
///|
pub fn TypeTable::record_at(self : TypeTable, position : Int32) -> TypeRecord? {
if position < 0 || position >= self.records.length() {
None
} else {
Some(self.records[position])
}
}
///|
pub fn TypeTable::record(self : TypeTable, type_index : UInt32) -> TypeRecord? {
let begin = self.header.type_index_begin
if type_index < begin || type_index >= self.header.type_index_end {
return None
}
let position = (type_index - begin).reinterpret_as_int()
self.record_at(position)
}
///|
pub fn TypeTable::records(self : TypeTable) -> Array[TypeRecord] {
self.records.copy()
}
///|
/// Parse stream 2 of an MSF file as a TPI type table.
pub fn MsfFile::tpi(
self : MsfFile,
limits : TpiLimits,
) -> TypeTable raise TpiError {
let bytes = self.stream(2) catch { _ => raise TypeStreamUnavailable(2) }
parse_tpi(bytes, limits)
}
///|
/// Parse stream 4 of an MSF file as an IPI type table.
pub fn MsfFile::ipi(
self : MsfFile,
limits : TpiLimits,
) -> TypeTable raise TpiError {
let bytes = self.stream(4) catch { _ => raise TypeStreamUnavailable(4) }
parse_tpi(bytes, limits)
}
///|
/// Parse a TPI or IPI stream, including all indexed type records.
pub fn parse_tpi(data : Bytes, limits : TpiLimits) -> TypeTable raise TpiError {
if data.length() > limits.max_stream_bytes {
raise TpiLimitExceeded("type stream byte limit exceeded")
}
let header = parse_tpi_header(data)
let records_end = checked_tpi_add(
header.header_size,
header.type_record_bytes,
"type record region",
)
if records_end > data.length() {
raise TruncatedTpi(records_end)
}
if header.type_record_count() > limits.max_records.reinterpret_as_uint() {
raise TpiLimitExceeded("type record count limit exceeded")
}
let records = []
let mut cursor = header.header_size
let mut position = 0
while cursor < records_end {
let (length_raw, next) = read_tpi_u16(data, cursor, records_end)
cursor = next
let length = length_raw.to_int()
if length < 2 {
raise InvalidTypeRecord("record length is smaller than its kind field")
}
if length > limits.max_record_bytes {
raise TpiLimitExceeded("type record byte limit exceeded")
}
let record_end = checked_tpi_add(cursor, length, "type record end")
if record_end > records_end {
raise TruncatedTpi(record_end)
}
let (kind, payload_start) = read_tpi_u16(data, cursor, record_end)
let payload = data
.exact_view(start=payload_start, end=record_end)
.to_owned()
let type_index = header.type_index_begin + position.reinterpret_as_uint()
if type_index >= header.type_index_end {
raise InvalidTypeRecord(
"type record count exceeds the declared type-index range",
)
}
records.push(TypeRecord::{
type_index,
kind,
payload,
details: decode_type_details(kind, payload, limits),
})
cursor = record_end
position = position + 1
}
if cursor != records_end {
raise InvalidTypeRecord("type record region has trailing bytes")
}
if position.reinterpret_as_uint() != header.type_record_count() {
raise InvalidTypeRecord(
"type record count does not match the declared type-index range",
)
}
TypeTable::{ data, header, records, }
}
///|
/// Return the standard interpretation of a simple type index below 0x1000.
pub fn simple_type(type_index : UInt32) -> SimpleType? {
if type_index >= TPI_TYPE_INDEX_BEGIN {
return None
}
let base_int = (type_index & 0xFFU).reinterpret_as_int()
let mode = match (type_index >> 8) & 0x7U {
0U => Direct
1U => NearPointer
2U => FarPointer
3U => HugePointer
4U => Pointer32
5U => FarPointer32
6U => Pointer64
_ => Reserved
}
let (kind, name, byte_size) = classify_simple_type(base_int)
Some(SimpleType::{
raw: type_index,
base: base_int,
mode,
kind,
name,
byte_size,
})
}
///|
fn classify_simple_type(base : Int32) -> (SimpleTypeKind, String, Int32) {
match base {
0x00 => (NoType, "notype", 0)
0x03 => (Void, "void", 0)
0x08 => (SignedInteger, "hresult", 4)
0x10 => (Character, "char", 1)
0x11 => (SignedInteger, "short", 2)
0x12 => (SignedInteger, "long", 4)
0x13 => (SignedInteger, "quad", 8)
0x14 => (SignedInteger, "oct", 16)
0x20 => (UnsignedInteger, "uchar", 1)
0x21 => (UnsignedInteger, "ushort", 2)
0x22 => (UnsignedInteger, "ulong", 4)
0x23 => (UnsignedInteger, "uquad", 8)
0x24 => (UnsignedInteger, "uoct", 16)
0x30 => (Boolean, "bool8", 1)
0x31 => (Boolean, "bool16", 2)
0x32 => (Boolean, "bool32", 4)
0x33 => (Boolean, "bool64", 8)
0x40 => (Real, "real32", 4)
0x41 => (Real, "real64", 8)
0x42 => (Real, "real80", 10)
0x43 => (Real, "real128", 16)
0x60 => (Bit, "bit", 0)
0x61 => (Character, "pascal_char", 1)
0x62 => (Boolean, "bool32_ff", 4)
0x68 => (SignedInteger, "int8", 1)
0x69 => (UnsignedInteger, "uint8", 1)
0x70 => (Character, "rchar", 1)
0x71 => (WideCharacter, "wchar", 2)
0x72 => (SignedInteger, "int16", 2)
0x73 => (UnsignedInteger, "uint16", 2)
0x74 => (SignedInteger, "int32", 4)
0x75 => (UnsignedInteger, "uint32", 4)
0x76 => (SignedInteger, "int64", 8)
0x77 => (UnsignedInteger, "uint64", 8)
0x78 => (SignedInteger, "int128", 16)
0x79 => (UnsignedInteger, "uint128", 16)
0x7A => (WideCharacter, "char16", 2)
0x7B => (WideCharacter, "char32", 4)
_ => (Unknown, "unknown", 0)
}
}
///|
fn parse_tpi_header(data : Bytes) -> TpiHeader raise TpiError {
if data.length() < TPI_HEADER_SIZE {
raise TruncatedTpi(data.length())
}
let (version, cursor) = read_tpi_u32(data, 0, data.length())
let (header_size_raw, cursor) = read_tpi_u32(data, cursor, data.length())
let header_size = tpi_uint_to_int(header_size_raw, "header size")
if header_size < TPI_HEADER_SIZE || header_size > data.length() {
raise InvalidTpiHeader("header size is outside the stream")
}
let (type_index_begin, cursor) = read_tpi_u32(data, cursor, data.length())
let (type_index_end, cursor) = read_tpi_u32(data, cursor, data.length())
if type_index_begin < TPI_TYPE_INDEX_BEGIN ||
type_index_end <= type_index_begin {
raise InvalidTpiHeader("invalid type-index range")
}
let (type_record_bytes_raw, cursor) = read_tpi_u32(
data,
cursor,
data.length(),
)
let type_record_bytes = tpi_uint_to_int(
type_record_bytes_raw, "type record bytes",
)
if type_record_bytes < 0 ||
checked_tpi_add(header_size, type_record_bytes, "type records") >
data.length() {
raise InvalidTpiHeader("type records are outside the stream")
}
let (hash_stream_index_raw, cursor) = read_tpi_u16(
data,
cursor,
data.length(),
)
let (hash_aux_stream_index_raw, cursor) = read_tpi_u16(
data,
cursor,
data.length(),
)
let (hash_key_size, cursor) = read_tpi_u32(data, cursor, data.length())
let (hash_bucket_count, cursor) = read_tpi_u32(data, cursor, data.length())
let (hash_value_offset_raw, cursor) = read_tpi_u32(
data,
cursor,
data.length(),
)
let (hash_value_bytes_raw, cursor) = read_tpi_u32(data, cursor, data.length())
let (index_offset_offset_raw, cursor) = read_tpi_u32(
data,
cursor,
data.length(),
)
let (index_offset_bytes_raw, cursor) = read_tpi_u32(
data,
cursor,
data.length(),
)
let (hash_adjust_offset_raw, cursor) = read_tpi_u32(
data,
cursor,
data.length(),
)
let (hash_adjust_bytes_raw, _) = read_tpi_u32(data, cursor, data.length())
let hash_value_offset = hash_value_offset_raw.reinterpret_as_int()
let hash_value_bytes = tpi_uint_to_int(
hash_value_bytes_raw, "hash value bytes",
)
let index_offset_offset = index_offset_offset_raw.reinterpret_as_int()
let index_offset_bytes = tpi_uint_to_int(
index_offset_bytes_raw, "index offset bytes",
)
let hash_adjust_offset = hash_adjust_offset_raw.reinterpret_as_int()
let hash_adjust_bytes = tpi_uint_to_int(
hash_adjust_bytes_raw, "hash adjust bytes",
)
validate_embedded_region(
data, hash_value_offset, hash_value_bytes, "hash values",
)
validate_embedded_region(
data, index_offset_offset, index_offset_bytes, "type index offsets",
)
validate_embedded_region(
data, hash_adjust_offset, hash_adjust_bytes, "hash adjusters",
)
TpiHeader::{
version,
header_size,
type_index_begin,
type_index_end,
type_record_bytes,
hash_stream_index: hash_stream_index_raw.to_int(),
hash_aux_stream_index: hash_aux_stream_index_raw.to_int(),
hash_key_size,
hash_bucket_count,
hash_value_offset,
hash_value_bytes,
index_offset_offset,
index_offset_bytes,
hash_adjust_offset,
hash_adjust_bytes,
}
}
///|
fn validate_embedded_region(
data : Bytes,
offset : Int32,
length : Int32,
field : String,
) -> Unit raise TpiError {
if length == 0 {
return
}
if offset < 0 || length < 0 {
raise InvalidTpiHeader("\{field} has a negative offset or length")
}
let end = checked_tpi_add(offset, length, field)
if end > data.length() {
raise InvalidTpiHeader("\{field} is outside the stream")
}
}
///|
fn decode_type_details(
kind : UInt16,
payload : Bytes,
limits : TpiLimits,
) -> TypeDetails raise TpiError {
match kind {
LF_MODIFIER => decode_modifier(payload)
LF_POINTER => decode_pointer(payload)
LF_PROCEDURE => decode_procedure(payload)
LF_MFUNCTION => decode_member_function(payload)
LF_ARGLIST | LF_SUBSTR_LIST =>
TypeDetails::ArgList(decode_arg_list(payload, limits))
LF_FIELDLIST => TypeDetails::FieldList(parse_field_list(payload))
LF_BITFIELD => decode_bitfield(payload)
LF_ARRAY => decode_array(payload)
LF_CLASS | LF_STRUCTURE | LF_INTERFACE => decode_aggregate(kind, payload)
LF_UNION => decode_union(payload)
LF_ENUM => decode_enum(payload)
LF_ALIAS => decode_alias(payload)
LF_LABEL => decode_label(payload)
LF_FUNC_ID => decode_function_id(payload)
LF_MFUNC_ID => decode_member_function_id(payload)
LF_STRING_ID => decode_string_id(payload)
LF_BUILDINFO => decode_build_info(payload, limits)
LF_UDT_SRC_LINE | LF_UDT_MOD_SRC_LINE =>
decode_udt_source_line(kind, payload)
LF_TYPESERVER2 => decode_type_server2(payload)
_ => Unknown(UnknownType::{ kind, payload, })
}
}
///|
fn decode_modifier(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 6, "LF_MODIFIER")
let (modified_type, cursor) = read_tpi_u32(payload, 0, payload.length())
let (modifiers, _) = read_tpi_u16(payload, cursor, payload.length())
Modifier(ModifierType::{ modified_type, modifiers, })
}
///|
fn decode_pointer(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 8, "LF_POINTER")
let (referent_type, cursor) = read_tpi_u32(payload, 0, payload.length())
let (attributes, cursor) = read_tpi_u32(payload, cursor, payload.length())
let mode = (attributes >> 5) & 0x07U
if mode == 2U || mode == 3U {
require_payload(payload, 14, "member LF_POINTER")
let (containing_type, cursor) = read_tpi_u32(
payload,
cursor,
payload.length(),
)
let (representation, next) = read_tpi_u16(payload, cursor, payload.length())
if next != payload.length() {
raise InvalidTypeRecord("member pointer has trailing bytes")
}
Pointer(PointerType::{
referent_type,
attributes,
containing_type: Some(containing_type),
member_representation: Some(representation),
})
} else {
if cursor != payload.length() {
raise InvalidTypeRecord("LF_POINTER has trailing bytes")
}
Pointer(PointerType::{
referent_type,
attributes,
containing_type: None,
member_representation: None,
})
}
}
///|
fn decode_procedure(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 12, "LF_PROCEDURE")
let (return_type, cursor) = read_tpi_u32(payload, 0, payload.length())
let (calling_convention, cursor) = read_tpi_u8(
payload,
cursor,
payload.length(),
)
let (function_options, cursor) = read_tpi_u8(
payload,
cursor,
payload.length(),
)
let (parameter_count, cursor) = read_tpi_u16(
payload,
cursor,
payload.length(),
)
let (argument_list, cursor) = read_tpi_u32(payload, cursor, payload.length())
require_exact_end(cursor, payload.length(), "LF_PROCEDURE")
Procedure(ProcedureType::{
return_type,
calling_convention,
function_options,
parameter_count,
argument_list,
})
}
///|
fn decode_member_function(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 24, "LF_MFUNCTION")
let (return_type, cursor) = read_tpi_u32(payload, 0, payload.length())
let (class_type, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (this_type, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (calling_convention, cursor) = read_tpi_u8(
payload,
cursor,
payload.length(),
)
let (function_options, cursor) = read_tpi_u8(
payload,
cursor,
payload.length(),
)
let (parameter_count, cursor) = read_tpi_u16(
payload,
cursor,
payload.length(),
)
let (argument_list, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (this_adjustment, cursor) = read_tpi_u32(
payload,
cursor,
payload.length(),
)
require_exact_end(cursor, payload.length(), "LF_MFUNCTION")
MemberFunction(MemberFunctionType::{
return_type,
class_type,
this_type,
calling_convention,
function_options,
parameter_count,
argument_list,
this_adjustment: this_adjustment.reinterpret_as_int(),
})
}
///|
fn decode_arg_list(
payload : Bytes,
limits : TpiLimits,
) -> ArgListType raise TpiError {
let (count_raw, cursor0) = read_tpi_u32(payload, 0, payload.length())
let mut cursor = cursor0
let count = tpi_uint_to_int(count_raw, "argument count")
if count > limits.max_type_refs {
raise TpiLimitExceeded("argument type-reference limit exceeded")
}
let expected_end = checked_tpi_add(
cursor,
checked_tpi_mul(count, 4, "argument list"),
"argument list",
)
if expected_end != payload.length() {
raise InvalidTypeRecord("argument list size does not match its count")
}
let indices = []
for _ in 0.. TypeDetails raise TpiError {
require_payload(payload, 6, "LF_BITFIELD")
let (underlying_type, cursor) = read_tpi_u32(payload, 0, payload.length())
let (bit_size, cursor) = read_tpi_u8(payload, cursor, payload.length())
let (bit_offset, cursor) = read_tpi_u8(payload, cursor, payload.length())
require_exact_end(cursor, payload.length(), "LF_BITFIELD")
BitField(BitFieldType::{ underlying_type, bit_size, bit_offset, })
}
///|
fn decode_array(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 9, "LF_ARRAY")
let (element_type, cursor) = read_tpi_u32(payload, 0, payload.length())
let (index_type, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (size, cursor) = read_numeric_value(payload, cursor, payload.length())
let (name, cursor) = read_tpi_string(payload, cursor, payload.length())
require_exact_end(cursor, payload.length(), "LF_ARRAY")
Array(ArrayType::{ element_type, index_type, size, name, })
}
///|
fn decode_aggregate(
kind : UInt16,
payload : Bytes,
) -> TypeDetails raise TpiError {
require_payload(payload, 18, "LF_CLASS/LF_STRUCTURE")
let (member_count, cursor) = read_tpi_u16(payload, 0, payload.length())
let (properties, cursor) = read_tpi_u16(payload, cursor, payload.length())
let (field_list, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (derived_from, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (vtable_shape, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (size, cursor) = read_numeric_value(payload, cursor, payload.length())
let (name, cursor) = read_tpi_string(payload, cursor, payload.length())
let (unique_name, cursor) = if (properties & 0x0200) != 0 {
let (value, next) = read_tpi_string(payload, cursor, payload.length())
(Some(value), next)
} else {
(None, cursor)
}
require_exact_end(cursor, payload.length(), "aggregate")
Aggregate(AggregateType::{
leaf_kind: kind,
member_count,
properties,
field_list,
derived_from,
vtable_shape,
size,
name,
unique_name,
})
}
///|
fn decode_union(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 10, "LF_UNION")
let (member_count, cursor) = read_tpi_u16(payload, 0, payload.length())
let (properties, cursor) = read_tpi_u16(payload, cursor, payload.length())
let (field_list, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (size, cursor) = read_numeric_value(payload, cursor, payload.length())
let (name, cursor) = read_tpi_string(payload, cursor, payload.length())
let (unique_name, cursor) = if (properties & 0x0200) != 0 {
let (value, next) = read_tpi_string(payload, cursor, payload.length())
(Some(value), next)
} else {
(None, cursor)
}
require_exact_end(cursor, payload.length(), "LF_UNION")
Union(UnionType::{
member_count,
properties,
field_list,
size,
name,
unique_name,
})
}
///|
fn decode_enum(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 12, "LF_ENUM")
let (member_count, cursor) = read_tpi_u16(payload, 0, payload.length())
let (properties, cursor) = read_tpi_u16(payload, cursor, payload.length())
let (underlying_type, cursor) = read_tpi_u32(
payload,
cursor,
payload.length(),
)
let (field_list, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (name, cursor) = read_tpi_string(payload, cursor, payload.length())
let (unique_name, cursor) = if (properties & 0x0200) != 0 {
let (value, next) = read_tpi_string(payload, cursor, payload.length())
(Some(value), next)
} else {
(None, cursor)
}
require_exact_end(cursor, payload.length(), "LF_ENUM")
Enum(EnumType::{
member_count,
properties,
underlying_type,
field_list,
name,
unique_name,
})
}
///|
fn decode_alias(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 5, "LF_ALIAS")
let (underlying_type, cursor) = read_tpi_u32(payload, 0, payload.length())
let (name, cursor) = read_tpi_string(payload, cursor, payload.length())
require_exact_end(cursor, payload.length(), "LF_ALIAS")
Alias(AliasType::{ underlying_type, name, })
}
///|
fn decode_label(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 2, "LF_LABEL")
let (mode, cursor) = read_tpi_u16(payload, 0, payload.length())
require_exact_end(cursor, payload.length(), "LF_LABEL")
Label(LabelType::{ mode, })
}
///|
fn decode_function_id(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 9, "LF_FUNC_ID")
let (parent_scope, cursor) = read_tpi_u32(payload, 0, payload.length())
let (function_type, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (name, cursor) = read_tpi_string(payload, cursor, payload.length())
require_exact_end(cursor, payload.length(), "LF_FUNC_ID")
FunctionId(FunctionIdType::{ parent_scope, function_type, name, })
}
///|
fn decode_member_function_id(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 9, "LF_MFUNC_ID")
let (class_type, cursor) = read_tpi_u32(payload, 0, payload.length())
let (function_type, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (name, cursor) = read_tpi_string(payload, cursor, payload.length())
require_exact_end(cursor, payload.length(), "LF_MFUNC_ID")
MemberFunctionId(MemberFunctionIdType::{ class_type, function_type, name, })
}
///|
fn decode_string_id(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 5, "LF_STRING_ID")
let (id, cursor) = read_tpi_u32(payload, 0, payload.length())
let (value, cursor) = read_tpi_string(payload, cursor, payload.length())
require_exact_end(cursor, payload.length(), "LF_STRING_ID")
StringId(StringIdType::{ id, value, })
}
///|
fn decode_build_info(
payload : Bytes,
limits : TpiLimits,
) -> TypeDetails raise TpiError {
require_payload(payload, 2, "LF_BUILDINFO")
let (count_raw, cursor0) = read_tpi_u16(payload, 0, payload.length())
let mut cursor = cursor0
let count = count_raw.to_int()
if count > limits.max_type_refs {
raise TpiLimitExceeded("build-info type-reference limit exceeded")
}
let references_end = checked_tpi_add(
2,
checked_tpi_mul(count, 4, "build info"),
"build info",
)
if payload.length() != references_end {
raise InvalidTypeRecord("build-info size does not match its count")
}
let indices = []
for _ in 0.. TypeDetails raise TpiError {
let expected = if kind == LF_UDT_MOD_SRC_LINE { 16 } else { 12 }
require_payload(payload, expected, "LF_UDT_SRC_LINE")
let (udt, cursor) = read_tpi_u32(payload, 0, payload.length())
let (source_file, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (line_number, cursor) = read_tpi_u32(payload, cursor, payload.length())
let (module_index, cursor) = if kind == LF_UDT_MOD_SRC_LINE {
let (value, next) = read_tpi_u32(payload, cursor, payload.length())
(Some(value), next)
} else {
(None, cursor)
}
require_exact_end(cursor, payload.length(), "LF_UDT_SRC_LINE")
UdtSourceLine(UdtSourceLineType::{
udt,
source_file,
line_number,
module_index,
})
}
///|
fn decode_type_server2(payload : Bytes) -> TypeDetails raise TpiError {
require_payload(payload, 21, "LF_TYPESERVER2")
let guid = payload.exact_view(start=0, end=16).to_owned()
let (age, cursor) = read_tpi_u32(payload, 16, payload.length())
let (name, cursor) = read_tpi_string(payload, cursor, payload.length())
require_exact_end(cursor, payload.length(), "LF_TYPESERVER2")
TypeServer2(TypeServer2Type::{ guid, age, name, })
}
///|
fn read_numeric_value(
data : Bytes,
start : Int32,
end : Int32,
) -> (NumericValue, Int32) raise TpiError {
let (kind, cursor) = read_tpi_u16(data, start, end)
match kind {
0x8000 => {
let (value, next) = read_tpi_u8(data, cursor, end)
let signed = value
let normalized = if signed >= 0x80 { signed - 0x100 } else { signed }
(Signed(normalized.to_int64()), next)
}
0x8001 => {
let (value, next) = read_tpi_u16(data, cursor, end)
let signed = value.to_int()
let normalized = if signed >= 0x8000 { signed - 0x10000 } else { signed }
(Signed(normalized.to_int64()), next)
}
0x8002 => {
let (value, next) = read_tpi_u16(data, cursor, end)
(Unsigned(value.to_uint().to_uint64()), next)
}
0x8003 => {
let (value, next) = read_tpi_u32(data, cursor, end)
(Signed(value.reinterpret_as_int().to_int64()), next)
}
0x8004 => {
let (value, next) = read_tpi_u32(data, cursor, end)
(Unsigned(value.to_uint64()), next)
}
0x8009 => {
let (value, next) = read_tpi_u64(data, cursor, end)
(Signed(value.reinterpret_as_int64()), next)
}
0x800A => {
let (value, next) = read_tpi_u64(data, cursor, end)
(Unsigned(value), next)
}
0x8010 => {
let (length_raw, cursor) = read_tpi_u16(data, cursor, end)
let length = length_raw.to_int()
let next = checked_tpi_add(cursor, length, "variable numeric value")
if next > end {
raise TruncatedTpi(next)
}
(Opaque(kind, data.exact_view(start=cursor, end=next).to_owned()), next)
}
0x8017 | 0x8018 => {
let next = checked_tpi_add(cursor, 16, "128-bit numeric value")
if next > end {
raise TruncatedTpi(next)
}
(Opaque(kind, data.exact_view(start=cursor, end=next).to_owned()), next)
}
0x8005 => skip_opaque_numeric(data, cursor, end, kind, 4)
0x8006 => skip_opaque_numeric(data, cursor, end, kind, 8)
0x8007 => skip_opaque_numeric(data, cursor, end, kind, 10)
0x8008 => skip_opaque_numeric(data, cursor, end, kind, 16)
0x800B => skip_opaque_numeric(data, cursor, end, kind, 6)
0x800C => skip_opaque_numeric(data, cursor, end, kind, 8)
0x800D => skip_opaque_numeric(data, cursor, end, kind, 16)
0x800E => skip_opaque_numeric(data, cursor, end, kind, 20)
0x800F => skip_opaque_numeric(data, cursor, end, kind, 32)
0x801C => skip_opaque_numeric(data, cursor, end, kind, 2)
_ => {
let type_index = UInt32(kind.to_uint())
(Unsigned(type_index.to_uint64()), cursor)
}
}
}
///|
fn skip_opaque_numeric(
data : Bytes,
start : Int32,
end : Int32,
kind : UInt16,
length : Int32,
) -> (NumericValue, Int32) raise TpiError {
let next = checked_tpi_add(start, length, "numeric value")
if next > end {
raise TruncatedTpi(next)
}
(Opaque(kind, data.exact_view(start~, end=next).to_owned()), next)
}
///|
fn read_tpi_string(
data : Bytes,
start : Int32,
end : Int32,
) -> (String, Int32) raise TpiError {
let mut cursor = start
while cursor < end {
if data.at(cursor) == 0 {
let value = @utf8.decode(data.exact_view(start~, end=cursor)) catch {
_ => raise InvalidTypeRecord("type name is not valid UTF-8")
}
return (value, cursor + 1)
}
cursor = cursor + 1
}
raise InvalidTypeRecord("type name is not NUL-terminated")
}
///|
fn require_payload(
payload : Bytes,
minimum : Int32,
_record : String,
) -> Unit raise TpiError {
if payload.length() < minimum {
raise TruncatedTpi(payload.length())
}
}
///|
fn require_exact_end(
cursor : Int32,
expected : Int32,
_record : String,
) -> Unit raise TpiError {
if cursor != expected {
raise InvalidTypeRecord("\{_record} has trailing bytes")
}
}
///|
fn read_tpi_u8(
data : Bytes,
pos : Int32,
end : Int32,
) -> (Int32, Int32) raise TpiError {
if pos < 0 || pos >= end || pos >= data.length() {
raise TruncatedTpi(pos)
}
(data.at(pos).to_int(), pos + 1)
}
///|
fn read_tpi_u16(
data : Bytes,
pos : Int32,
end : Int32,
) -> (UInt16, Int32) raise TpiError {
let (b0, pos) = read_tpi_u8(data, pos, end)
let (b1, pos) = read_tpi_u8(data, pos, end)
(((b1 << 8) | b0).to_uint16(), pos)
}
///|
fn read_tpi_u32(
data : Bytes,
pos : Int32,
end : Int32,
) -> (UInt32, Int32) raise TpiError {
let (b0, pos) = read_tpi_u8(data, pos, end)
let (b1, pos) = read_tpi_u8(data, pos, end)
let (b2, pos) = read_tpi_u8(data, pos, end)
let (b3, pos) = read_tpi_u8(data, pos, end)
(
UInt32(
(b3.reinterpret_as_uint() << 24) |
(b2.reinterpret_as_uint() << 16) |
(b1.reinterpret_as_uint() << 8) |
b0.reinterpret_as_uint(),
),
pos,
)
}
///|
fn read_tpi_u64(
data : Bytes,
pos : Int32,
end : Int32,
) -> (UInt64, Int32) raise TpiError {
let (low, pos) = read_tpi_u32(data, pos, end)
let (high, pos) = read_tpi_u32(data, pos, end)
((high.to_uint64() << 32) | low.to_uint64(), pos)
}
///|
fn tpi_uint_to_int(value : UInt32, field : String) -> Int32 raise TpiError {
if value > 0x7FFFFFFFU {
raise InvalidTpiHeader("\{field} exceeds Int range")
}
value.reinterpret_as_int()
}
///|
fn checked_tpi_add(
left : Int32,
right : Int32,
field : String,
) -> Int32 raise TpiError {
if left < 0 || right < 0 || left > 0x7FFFFFFF - right {
raise InvalidTpiHeader("\{field} overflows Int range")
}
left + right
}
///|
fn checked_tpi_mul(
left : Int32,
right : Int32,
field : String,
) -> Int32 raise TpiError {
if left < 0 || right < 0 || (left != 0 && right > 0x7FFFFFFF / left) {
raise InvalidTypeRecord("\{field} overflows Int range")
}
left * right
}