241 lines
7.5 KiB
Text
241 lines
7.5 KiB
Text
/// offset_of returns the byte offset of a field within the type T.
|
|
///
|
|
/// Note: T must be a struct type.
|
|
///
|
|
/// MyType :: struct { x: int; y: int; z: int; };
|
|
/// offset_of(MyType, #code y); // 8
|
|
///
|
|
offset_of :: ($T: Type, ident: Code, loc := #caller_location) -> int #expand {
|
|
#run (loc: Source_Code_Location) {
|
|
if !TypeIsStruct(T) {
|
|
CompileError("jc: offset_of can only be used on struct types", loc = loc);
|
|
}
|
|
}(loc);
|
|
|
|
return #run -> int {
|
|
t: T = ---;
|
|
return (*t.#insert ident).(*void) - (*t).(*void);
|
|
};
|
|
}
|
|
|
|
/// offset_of returns the byte offset of a field within the type of value.
|
|
///
|
|
/// Note: If offset_of is given a pointer value, it will use the type pointed to.
|
|
///
|
|
/// value := struct{ x: int; y: int; z: int; }.{};
|
|
/// offset_of(value, #code y); // 8
|
|
///
|
|
offset_of :: (#discard value: $T, ident: Code, loc := #caller_location) -> int #expand {
|
|
type :: #run -> Type {
|
|
info := T.(*Type_Info);
|
|
|
|
ok, pinfo := TypeIsPointer(T);
|
|
if ok {
|
|
// @question(judah): do we want it to traverse all the way up to a non-pointer type?
|
|
// I opted against because if you have a *T, you only want offset_of to get an offset
|
|
// from that pointer. What would you do with a field offset from **T?
|
|
if TypeIsPointer(pinfo.pointer_to) {
|
|
CompileError("jc: offset_of only allows one level of pointer indirection.", loc = loc);
|
|
}
|
|
|
|
info = pinfo.pointer_to;
|
|
}
|
|
|
|
return get_type(info);
|
|
};
|
|
|
|
return offset_of(type, ident, loc = loc);
|
|
}
|
|
|
|
/// align_of returns the alignment of type T.
|
|
align_of :: ($T: Type) -> int #expand {
|
|
return #run -> int {
|
|
if size_of(T) == 0
|
|
{ return 0; }
|
|
return offset_of(struct{ _: u8; t: T; }, #code t);
|
|
};
|
|
}
|
|
|
|
/// default_of returns a value of type T as if it was just instantiated.
|
|
///
|
|
/// Note: default_of will call the initializer for aggregate types, so you
|
|
/// may want zero_of instead.
|
|
default_of :: ($T: Type) -> T #expand {
|
|
default: T;
|
|
return default;
|
|
}
|
|
|
|
/// undefined_of returns a value of type T that has not been initialized.
|
|
undefined_of :: ($T: Type) -> T #expand {
|
|
uninit: T = ---;
|
|
return uninit;
|
|
}
|
|
|
|
/// zero_of returns a value of type T that has been zero-initialized.
|
|
///
|
|
/// Note: zero_of will not call the initializer for aggregate types, so you
|
|
/// may want default_of instead.
|
|
zero_of :: ($T: Type) -> T #expand {
|
|
zero := undefined_of(T);
|
|
MemZero(*zero);
|
|
return zero;
|
|
}
|
|
|
|
/// min_of returns the minimum value T can represent.
|
|
///
|
|
/// Note: T must be an integer, float, or enum type.
|
|
min_of :: ($T: Type, loc := #caller_location) -> T #expand {
|
|
return #run -> T {
|
|
info := T.(*Type_Info);
|
|
if info.type == {
|
|
case .INTEGER;
|
|
i := info.(*Type_Info_Integer);
|
|
if i.runtime_size == {
|
|
case 1; return (ifx i.signed then -0x80 else 0).(T, no_check);
|
|
case 2; return (ifx i.signed then -0x8000 else 0).(T, no_check);
|
|
case 4; return (ifx i.signed then -0x8000_0000 else 0).(T, no_check);
|
|
case 8; return (ifx i.signed then -0x8000_0000_0000_0000 else 0).(T, no_check);
|
|
case ; CompileError("jc: unknown integer size", loc = loc);
|
|
}
|
|
|
|
case .FLOAT;
|
|
if info.runtime_size == {
|
|
case 4; return (0h0080_0000).(T, no_check);
|
|
case 8; return (0h00100000_00000000).(T, no_check);
|
|
case ; CompileError("jc: unknown float size", loc = loc);
|
|
}
|
|
|
|
case .ENUM;
|
|
i := info.(*Type_Info_Enum);
|
|
if i.values.count == 0 {
|
|
return 0;
|
|
}
|
|
|
|
min: T = i.values[0].(T, no_check);
|
|
if i.internal_type.signed {
|
|
for i.values if it.(T) < min {
|
|
min = it.(T);
|
|
}
|
|
}
|
|
else {
|
|
for i.values if it.(T) < min {
|
|
min = it.(T);
|
|
}
|
|
}
|
|
|
|
return min;
|
|
|
|
case;
|
|
CompileError("jc: min_of requires an enum, integer, or float type", loc = loc);
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
}
|
|
|
|
/// max_of returns the maximum value T can represent.
|
|
///
|
|
/// Note: T must be an integer, float, or enum type.
|
|
max_of :: ($T: Type, loc := #caller_location) -> T #expand {
|
|
return #run -> T {
|
|
info := T.(*Type_Info);
|
|
if info.type == {
|
|
case .INTEGER;
|
|
i := info.(*Type_Info_Integer);
|
|
if i.runtime_size == {
|
|
case 1; return (ifx i.signed then 0x7f else 0xff).(T, no_check);
|
|
case 2; return (ifx i.signed then 0x7fff else 0xffff).(T, no_check);
|
|
case 4; return (ifx i.signed then 0x7fff_ffff else 0xffff_ffff).(T, no_check);
|
|
case 8; return (ifx i.signed then 0x7fff_ffff_ffff_ffff else 0xffff_ffff_ffff_ffff).(T, no_check);
|
|
case ; CompileError("jc: unknown integer size", loc = loc);
|
|
}
|
|
|
|
case .FLOAT;
|
|
if info.runtime_size == {
|
|
case 4; return (0h7F7FFFFF).(T, no_check);
|
|
case 8; return (0h7FEFFFFF_FFFFFFFF).(T, no_check);
|
|
case ; CompileError("jc: unknown float size", loc = loc);
|
|
}
|
|
|
|
case .ENUM;
|
|
i := info.(*Type_Info_Enum);
|
|
if i.values.count == 0 {
|
|
return 0;
|
|
}
|
|
|
|
max := i.values[0].(T, no_check);
|
|
if i.internal_type.signed {
|
|
for i.values if xx it > max {
|
|
max = xx it;
|
|
}
|
|
}
|
|
else {
|
|
for i.values if xx it > max {
|
|
max = xx it;
|
|
}
|
|
}
|
|
|
|
return max;
|
|
|
|
case;
|
|
CompileError("jc: max_of requires an enum, integer, or float type", loc = loc);
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
}
|
|
|
|
/// range_of returns the minimum and maximum values T can represent.
|
|
///
|
|
/// Note: T must be an integer, float, or enum type.
|
|
range_of :: ($T: Type, loc := #caller_location) -> (T, T) #expand {
|
|
return min_of(T, loc = loc), max_of(T, loc = loc);
|
|
}
|
|
|
|
|
|
#scope_file
|
|
|
|
#if RunTests #run {
|
|
Test("min_of/max_of:enums", t => {
|
|
U8Enum :: enum u8 { lo :: -1; hi :: +1; }
|
|
S8Enum :: enum s8 { lo :: -2; hi :: -1; }
|
|
{
|
|
Expect(min_of(U8Enum) == U8Enum.lo);
|
|
Expect(min_of(S8Enum) == S8Enum.lo);
|
|
Expect(max_of(U8Enum) == U8Enum.hi);
|
|
Expect(max_of(S8Enum) == S8Enum.hi);
|
|
}
|
|
|
|
U16Enum :: enum u16 { lo :: -1; hi :: +1; }
|
|
S16Enum :: enum s16 { lo :: -2; hi :: -1; }
|
|
{
|
|
Expect(min_of(U16Enum) == U16Enum.lo);
|
|
Expect(min_of(S16Enum) == S16Enum.lo);
|
|
Expect(max_of(U16Enum) == U16Enum.hi);
|
|
Expect(max_of(S16Enum) == S16Enum.hi);
|
|
}
|
|
|
|
U32Enum :: enum u32 { lo :: -1; hi :: +1; }
|
|
S32Enum :: enum s32 { lo :: -2; hi :: -1; }
|
|
{
|
|
Expect(min_of(U32Enum) == U32Enum.lo);
|
|
Expect(min_of(S32Enum) == S32Enum.lo);
|
|
Expect(max_of(U32Enum) == U32Enum.hi);
|
|
Expect(max_of(S32Enum) == S32Enum.hi);
|
|
}
|
|
|
|
U64Enum :: enum u64 { lo :: -1; hi :: +1; }
|
|
S64Enum :: enum s64 { lo :: -2; hi :: -1; }
|
|
{
|
|
Expect(min_of(U64Enum) == U64Enum.lo);
|
|
Expect(min_of(S64Enum) == S64Enum.lo);
|
|
Expect(max_of(U64Enum) == U64Enum.hi);
|
|
Expect(max_of(S64Enum) == S64Enum.hi);
|
|
}
|
|
|
|
// @note(judah): just making sure this compiles
|
|
lo, hi := range_of(U64Enum);
|
|
Expect(lo == U64Enum.lo);
|
|
Expect(hi == U64Enum.hi);
|
|
});
|
|
}
|