libguf/tools/ckdint-gen.py
2025-05-11 08:55:03 +02:00

254 lines
10 KiB
Python

"""
Generate typesafe checked arithmetic functions for libguf/src/guf_math_ckdint.h
"""
from dataclasses import dataclass
import textwrap
@dataclass
class IntType:
INT_TYPE: str
INT_TYPE_ABBR: str
INT_MIN: str
INT_MAX: str
@dataclass
class UintType:
INT_TYPE: str
INT_TYPE_ABBR: str
INT_MIN: str
INT_MAX: str
def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
ckd_add_sub_uint = textwrap.dedent("""
static inline guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b)
{{
if (b > 0 && a > {int_max} - b) {{
return GUF_MATH_CKD_OVERFLOW;
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}}
static inline guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b)
{{
if (b > a) {{
return GUF_MATH_CKD_UNDERFLOW;
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}}
""")
ckd_add_sub_int = textwrap.dedent("""
static inline guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b)
{{
if (b > 0 && a > {int_max} - b) {{
return GUF_MATH_CKD_OVERFLOW;
}} else if (b < 0 && a < {int_min} - b) {{
return GUF_MATH_CKD_UNDERFLOW;
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}}
static inline guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b)
{{
if (b < 0 && a > {int_max} + b) {{
return GUF_MATH_CKD_OVERFLOW;
}} else if (b > 0 && a < {int_min} + b) {{
return GUF_MATH_CKD_UNDERFLOW;
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}}
""")
saturating_wrapping_int = textwrap.dedent("""
static inline guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a + b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = {int_max};
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = {int_min};
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
static inline guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a - b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = {int_max};
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = {int_min};
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
static inline guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a + b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = (a + {int_min}) + (b + {int_min});
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = (a - {int_min}) + (b - {int_min});
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
static inline guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a - b;
break;
case GUF_MATH_CKD_OVERFLOW:
GUF_ASSERT(b < 0);
*result = (a + {int_min}) - (b - {int_min}); // TODO: not sure
break;
case GUF_MATH_CKD_UNDERFLOW:
GUF_ASSERT(b > 0);
*result = (a - {int_min}) - (b + {int_min}); // TODO: not sure
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
""")
saturating_wrapping_uint = textwrap.dedent("""
static inline guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a + b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = {int_max};
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
static inline guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_UNDERFLOW);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a - b;
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = 0;
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
static inline guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
if (result) {{
*result = a + b;
}}
return check;
}}
static inline guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
if (result) {{
*result = a - b;
}}
return check;
}}
""")
text_result = "// Signed integer add/sub checks (generated with libguf/tools/ckdint-gen.py)\n"
for type in int_types:
text_result += ckd_add_sub_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX)
text_result += "\n// Unsigned integer add/sub checks (generated with libguf/tools/ckdint-gen.py) \n"
for type in uint_types:
text_result += ckd_add_sub_uint.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX)
text_result += "\n\n// Signed saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)\n"
for type in int_types:
text_result += saturating_wrapping_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + "\n"
text_result += "\n// Unsigned saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)\n"
for type in uint_types:
text_result += saturating_wrapping_uint.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + "\n"
return text_result
if __name__ == "__main__":
int_types = [
IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX"),
IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX"),
IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX"),
IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX"),
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX"),
IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX"),
]
uint_types = [
UintType(INT_TYPE = "unsigned char", INT_TYPE_ABBR = "uchar", INT_MIN = "0", INT_MAX = "UCHAR_MAX"),
UintType(INT_TYPE = "unsigned", INT_TYPE_ABBR = "unsigned", INT_MIN = "0", INT_MAX = "UINT_MAX"),
UintType(INT_TYPE = "uint8_t", INT_TYPE_ABBR = "u8", INT_MIN = "0", INT_MAX = "UINT8_MAX"),
UintType(INT_TYPE = "uint16_t", INT_TYPE_ABBR = "u16", INT_MIN = "0", INT_MAX = "UINT16_MAX"),
UintType(INT_TYPE = "uint32_t", INT_TYPE_ABBR = "u32", INT_MIN = "0", INT_MAX = "UINT32_MAX"),
UintType(INT_TYPE = "uint64_t", INT_TYPE_ABBR = "u64", INT_MIN = "0", INT_MAX = "UINT64_MAX"),
UintType(INT_TYPE = "size_t", INT_TYPE_ABBR = "size_t", INT_MIN = "0", INT_MAX = "SIZE_MAX"),
]
ckd_fun_code = generate_ckdint_functions(int_types = int_types, uint_types= uint_types)
print(ckd_fun_code)