""" Generate typesafe checked arithmetic functions for libguf/src/guf_math_ckdint.h """ from dataclasses import dataclass from typing import Tuple import textwrap @dataclass class IntType: INT_TYPE: str INT_TYPE_ABBR: str INT_MIN: str INT_MAX: str UINT_TYPE: str = "size_t" @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) -> Tuple[str, str]: ckd_add_sub_uint_header = textwrap.dedent(""" GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b); """) ckd_add_sub_uint = textwrap.dedent(""" GUF_MATH_CKDINT_KWRDS 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_POS; }} else {{ return GUF_MATH_CKD_SUCCESS; }} }} GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b) {{ if (b > a) {{ return GUF_MATH_CKD_OVERFLOW_NEG; }} else {{ return GUF_MATH_CKD_SUCCESS; }} }} GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b) {{ const {type} c = 1u * a * b; return a != 0 && ((1u * c / a) != b) ? GUF_MATH_CKD_OVERFLOW_POS : GUF_MATH_CKD_SUCCESS; }} """) ckd_add_sub_int_header = textwrap.dedent(""" GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b); """) ckd_add_sub_int = textwrap.dedent(""" GUF_MATH_CKDINT_KWRDS 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_POS; }} else if (b < 0 && a < {int_min} - b) {{ return GUF_MATH_CKD_OVERFLOW_NEG; }} else {{ return GUF_MATH_CKD_SUCCESS; }} }} GUF_MATH_CKDINT_KWRDS 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_POS; }} else if (b > 0 && a < {int_min} + b) {{ return GUF_MATH_CKD_OVERFLOW_NEG; }} else {{ return GUF_MATH_CKD_SUCCESS; }} }} GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b) {{ if (b > 0) {{ if (a > {int_max} / b) {{ return GUF_MATH_CKD_OVERFLOW_POS; }} else if (a < {int_min} / b) {{ return GUF_MATH_CKD_OVERFLOW_NEG; }} else {{ return GUF_MATH_CKD_SUCCESS; }} }} else if (b < 0) {{ if ({int_min} != -{int_max} && b == -1) {{ // Prevent potential ({int_min} / b) overflow for b == -1 return a == {int_min} ? GUF_MATH_CKD_OVERFLOW_POS : GUF_MATH_CKD_SUCCESS; }} else if (a < {int_max} / b) {{ return GUF_MATH_CKD_OVERFLOW_POS; }} else if (a > {int_min} / b) {{ return GUF_MATH_CKD_OVERFLOW_NEG; }} else {{ return GUF_MATH_CKD_SUCCESS; }} }} else {{ return GUF_MATH_CKD_SUCCESS; }} }} """) saturating_wrapping_int_header = textwrap.dedent(""" GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result); """) saturating_wrapping_int = textwrap.dedent(""" GUF_MATH_CKDINT_KWRDS 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_POS: *result = {int_max}; break; case GUF_MATH_CKD_OVERFLOW_NEG: *result = {int_min}; break; default: GUF_ASSERT(false); }} }} return check; }} GUF_MATH_CKDINT_KWRDS 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_POS: *result = {int_max}; break; case GUF_MATH_CKD_OVERFLOW_NEG: *result = {int_min}; break; default: GUF_ASSERT(false); }} }} return check; }} GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result) {{ const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b); if (result) {{ switch (check) {{ case GUF_MATH_CKD_SUCCESS: *result = a * b; break; case GUF_MATH_CKD_OVERFLOW_POS: *result = {int_max}; break; case GUF_MATH_CKD_OVERFLOW_NEG: *result = {int_min}; break; default: GUF_ASSERT(false); }} }} return check; }} GUF_MATH_CKDINT_KWRDS 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_POS: *result = (a + {int_min}) + (b + {int_min}); break; case GUF_MATH_CKD_OVERFLOW_NEG: *result = (a - {int_min}) + (b - {int_min}); break; default: GUF_ASSERT(false); }} }} return check; }} GUF_MATH_CKDINT_KWRDS 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_POS: GUF_ASSERT(b < 0); *result = (a + {int_min}) - (b - {int_min}); // TODO: not sure break; case GUF_MATH_CKD_OVERFLOW_NEG: GUF_ASSERT(b > 0); *result = (a - {int_min}) - (b + {int_min}); // TODO: not sure break; default: GUF_ASSERT(false); }} }} return check; }} GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result) {{ const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b); if (result) {{ switch (check) {{ case GUF_MATH_CKD_SUCCESS: *result = a * b; break; case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_NEG: {{ {uint_type} res = 1u * ({uint_type})a * ({uint_type})b; if (res > {int_max}) {{ // This is the fix for implementation defined conversion from unsigned to signed. const {uint_type} mod = (1u + ({uint_type}){int_max}); res = mod > 0 ? (1u * res % mod) : res; *result = {int_min} + ({type})res; }} else {{ *result = ({type})res; }} break; }} default: GUF_ASSERT(false); }} }} return check; }} """) saturating_wrapping_uint_header = textwrap.dedent(""" GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result); """) saturating_wrapping_uint = textwrap.dedent(""" GUF_MATH_CKDINT_KWRDS 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_POS); if (result) {{ switch (check) {{ case GUF_MATH_CKD_SUCCESS: *result = a + b; break; case GUF_MATH_CKD_OVERFLOW_POS: *result = {int_max}; break; default: GUF_ASSERT(false); }} }} return check; }} GUF_MATH_CKDINT_KWRDS 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_OVERFLOW_NEG); if (result) {{ switch (check) {{ case GUF_MATH_CKD_SUCCESS: *result = a - b; break; case GUF_MATH_CKD_OVERFLOW_NEG: *result = 0; break; default: GUF_ASSERT(false); }} }} return check; }} GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result) {{ const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b); GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_POS); if (result) {{ switch (check) {{ case GUF_MATH_CKD_SUCCESS: *result = a * b; break; case GUF_MATH_CKD_OVERFLOW_POS: *result = {int_max}; break; default: GUF_ASSERT(false); }} }} return check; }} GUF_MATH_CKDINT_KWRDS 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 = 1u * a + b; }} return check; }} GUF_MATH_CKDINT_KWRDS 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 = 1u * a - b; }} return check; }} GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result) {{ const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b); if (result) {{ *result = 1u * a * b; }} return check; }} """) text_result = "// Signed integer arithmetic checks (generated with libguf/tools/ckdint-gen.py)\n" text_result_header = text_result 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_header += ckd_add_sub_int_header.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 arithmetic checks (generated with libguf/tools/ckdint-gen.py) \n" text_result_header += "\n// Unsigned integer arithmetic 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_header += ckd_add_sub_uint_header.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 arithmetic (generated with libguf/tools/ckdint-gen.py)\n" text_result_header += "\n\n// Signed saturating/wrapping arithmetic (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, uint_type = type.UINT_TYPE) + "\n" text_result_header += saturating_wrapping_int_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, uint_type = type.UINT_TYPE) + "\n" text_result += "\n// Unsigned saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n" text_result_header += "\n// Unsigned saturating/wrapping arithmetic (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" text_result_header += saturating_wrapping_uint_header.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_header, text_result) if __name__ == "__main__": int_types = [ IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX", UINT_TYPE = "unsigned"), IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX", UINT_TYPE = "uint8_t"), IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX", UINT_TYPE = "uint16_t"), IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX", UINT_TYPE = "uint32_t"), IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX", UINT_TYPE = "uint64_t"), IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX", UINT_TYPE = "size_t"), # TODO: size_t is not necessarily the unsigned ptrdiff_t equivalent ] 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"), ] code_header, code_impl = generate_ckdint_functions(int_types = int_types, uint_types= uint_types) print(textwrap.dedent( """ #if defined(GUF_MATH_CKDINT_IMPL_STATIC) #define GUF_MATH_CKDINT_KWRDS static inline #else #define GUF_MATH_CKDINT_KWRDS #endif #ifndef GUF_MATH_CKDINT_H #define GUF_MATH_CKDINT_H #include "guf_common.h" typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLOW_POS, GUF_MATH_CKD_OVERFLOW_NEG} guf_math_ckd_result; """)) print("#if !defined(GUF_MATH_CKDINT_IMPL_STATIC) && !defined(GUF_MATH_CKDINT_IMPL)") print(code_header) print("#endif") print("#if defined(GUF_MATH_CKDINT_IMPL) || defined(GUF_MATH_CKDINT_IMPL_STATIC)") print('#include "guf_assert.h"') print(code_impl) print("#endif /* End impl */\n") print("#endif /* End header-guard */\n") print("#undef GUF_MATH_CKDINT_KWRDS") print("#undef GUF_MATH_CKDINT_IMPL") print("#undef GUF_MATH_CKDINT_IMPL_STATIC\n")