413 lines
18 KiB
Python
413 lines
18 KiB
Python
"""
|
|
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;
|
|
}} 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_UNDERFLOW;
|
|
}} 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 = a * b;
|
|
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : 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;
|
|
}} else if (b < 0 && a < {int_min} - b) {{
|
|
return GUF_MATH_CKD_UNDERFLOW;
|
|
}} 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;
|
|
}} else if (b > 0 && a < {int_min} + b) {{
|
|
return GUF_MATH_CKD_UNDERFLOW;
|
|
}} 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;
|
|
}} else if (a < {int_min} / b) {{
|
|
return GUF_MATH_CKD_UNDERFLOW;
|
|
}} 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 : GUF_MATH_CKD_SUCCESS;
|
|
}} else if (a < {int_max} / b) {{
|
|
return GUF_MATH_CKD_OVERFLOW;
|
|
}} else if (a > {int_min} / b) {{
|
|
return GUF_MATH_CKD_UNDERFLOW;
|
|
}} 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:
|
|
*result = {int_max};
|
|
break;
|
|
case GUF_MATH_CKD_UNDERFLOW:
|
|
*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:
|
|
*result = {int_max};
|
|
break;
|
|
case GUF_MATH_CKD_UNDERFLOW:
|
|
*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:
|
|
*result = {int_max};
|
|
break;
|
|
case GUF_MATH_CKD_UNDERFLOW:
|
|
*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:
|
|
*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;
|
|
}}
|
|
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:
|
|
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;
|
|
}}
|
|
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:
|
|
case GUF_MATH_CKD_UNDERFLOW:
|
|
*result = ({type})(({uint_type})a * ({uint_type})b); // TODO: Implementation defined...
|
|
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);
|
|
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;
|
|
}}
|
|
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_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;
|
|
}}
|
|
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);
|
|
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;
|
|
}}
|
|
|
|
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 = 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 = 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 = 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, GUF_MATH_CKD_UNDERFLOW} guf_math_ckd_result;
|
|
|
|
"""))
|
|
|
|
print(code_header)
|
|
|
|
print("#if defined(GUF_MATH_CKDINT_IMPL) || defined(GUF_MATH_CKDINT_IMPL_STATIC)")
|
|
print('#include "guf_assert.h"')
|
|
print(code_impl)
|
|
print("#endif\n")
|
|
|
|
print("#endif")
|
|
|
|
print("#undef GUF_MATH_CKDINT_IMPL")
|
|
print("#undef GUF_MATH_CKDINT_IMPL_STATIC\n")
|