File size: 5,178 Bytes
78d2150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <gmp.h>

/* 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();
}