#include "../../unity/unity.h" #include #include #include #include /* Unity setUp/tearDown */ void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } /* Helper: allocate decimal string for a number represented by two limbs (hi, lo). Uses mpz_import to construct the number hi*B + lo, then returns its base-10 string. Caller must free the returned string. */ static char *make_dec_from_limbs(mp_limb_t hi, mp_limb_t lo) { mpz_t z; mpz_init(z); mp_limb_t limbs[2]; limbs[0] = hi; /* most significant limb */ limbs[1] = lo; /* least significant limb */ /* order=1 (most significant first), size=sizeof(mp_limb_t), endian=0 (native), nails=0 */ mpz_import(z, 2, 1, sizeof(mp_limb_t), 0, 0, limbs); /* Allocate buffer of appropriate size and get decimal string */ size_t need = mpz_sizeinbase(z, 10) + 2; /* +2 for sign and NUL safety */ char *buf = (char *)malloc(need); TEST_ASSERT_NOT_NULL(buf); mpz_get_str(buf, 10, z); mpz_clear(z); return buf; } /* Helper: allocate decimal string for 2^bits. Caller must free. */ static char *make_dec_pow2(size_t bits) { mpz_t z; mpz_init(z); mpz_set_ui(z, 1); mpz_mul_2exp(z, z, bits); size_t need = mpz_sizeinbase(z, 10) + 2; char *buf = (char *)malloc(need); TEST_ASSERT_NOT_NULL(buf); mpz_get_str(buf, 10, z); mpz_clear(z); return buf; } /* Helper: allocate decimal string for 2^bits - 1. Caller must free. */ static char *make_dec_pow2_minus1(size_t bits) { mpz_t z; mpz_init(z); mpz_set_ui(z, 1); mpz_mul_2exp(z, z, bits); mpz_sub_ui(z, z, 1); size_t need = mpz_sizeinbase(z, 10) + 2; char *buf = (char *)malloc(need); TEST_ASSERT_NOT_NULL(buf); mpz_get_str(buf, 10, z); mpz_clear(z); return buf; } /* Helper: assert that strtouuint parses `dec` to (exphi,exlo) with LONGINT_OK */ static void assert_parse_ok_to_limbs(const char *dec, mp_limb_t exphi, mp_limb_t exlo) { uuint out; strtol_error e = strtouuint(&out, dec); TEST_ASSERT_EQUAL_INT_MESSAGE(LONGINT_OK, e, "Expected LONGINT_OK"); TEST_ASSERT_MESSAGE(hi(out) == exphi, "High limb mismatch"); TEST_ASSERT_MESSAGE(lo(out) == exlo, "Low limb mismatch"); } /* Helper: assert that strtouuint(dec) returns the specified error code. */ static void assert_parse_error(const char *dec, strtol_error expected) { uuint out; /* unused on error */ strtol_error e = strtouuint(&out, dec); TEST_ASSERT_EQUAL_INT(expected, e); } /* Test: basic limb decomposition across key boundaries using dynamically built decimal strings. */ static void test_strtouuint_parses_key_boundaries(void) { /* B-1 => (hi=0, lo=MP_LIMB_MAX) */ char *s_bminus1 = make_dec_pow2_minus1(W_TYPE_SIZE); assert_parse_ok_to_limbs(s_bminus1, 0, MP_LIMB_MAX); free(s_bminus1); /* B => (hi=1, lo=0) */ char *s_b = make_dec_pow2(W_TYPE_SIZE); assert_parse_ok_to_limbs(s_b, 1, 0); free(s_b); /* B^2 - 1 => (hi=MP_LIMB_MAX, lo=MP_LIMB_MAX) */ char *s_b2minus1 = make_dec_pow2_minus1(2 * (size_t)W_TYPE_SIZE); assert_parse_ok_to_limbs(s_b2minus1, MP_LIMB_MAX, MP_LIMB_MAX); free(s_b2minus1); } /* Test: overflow exactly at B^2 and beyond. */ static void test_strtouuint_overflow_at_B2(void) { /* B^2 should overflow (does not fit two limbs). */ char *s_b2 = make_dec_pow2(2 * (size_t)W_TYPE_SIZE); assert_parse_error(s_b2, LONGINT_OVERFLOW); free(s_b2); } /* Test: invalid inputs (non-digit, empty, trailing/leading junk). */ static void test_strtouuint_invalid_inputs(void) { assert_parse_error("", LONGINT_INVALID); assert_parse_error("abc", LONGINT_INVALID); assert_parse_error("123abc", LONGINT_INVALID); assert_parse_error("+123", LONGINT_INVALID); assert_parse_error(" 123", LONGINT_INVALID); assert_parse_error("12+3", LONGINT_INVALID); assert_parse_error("123 ", LONGINT_INVALID); } /* Test: leading zeros are accepted and produce correct values. */ static void test_strtouuint_leading_zeros(void) { assert_parse_ok_to_limbs("0", 0, 0); assert_parse_ok_to_limbs("0000", 0, 0); assert_parse_ok_to_limbs("000123", 0, 123); } /* Test: various limb-composed values roundtrip via decimal strings. */ static void test_strtouuint_various_composed_values(void) { struct { mp_limb_t hi; mp_limb_t lo; } cases[] = { {0, 0}, {0, 1}, {0, 9}, {0, 10}, {0, 123456}, {0, MP_LIMB_MAX}, /* B - 1 */ {1, 0}, /* B */ {1, 12345}, /* B + 12345 */ {MP_LIMB_MAX, 1}, /* B^2 - B + 1 */ {MP_LIMB_MAX, MP_LIMB_MAX} /* B^2 - 1 */ }; for (size_t i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) { char *dec = make_dec_from_limbs(cases[i].hi, cases[i].lo); assert_parse_ok_to_limbs(dec, cases[i].hi, cases[i].lo); free(dec); } } int main(void) { UNITY_BEGIN(); RUN_TEST(test_strtouuint_parses_key_boundaries); RUN_TEST(test_strtouuint_overflow_at_B2); RUN_TEST(test_strtouuint_invalid_inputs); RUN_TEST(test_strtouuint_leading_zeros); RUN_TEST(test_strtouuint_various_composed_values); return UNITY_END(); }