File Coverage

cc_bignum.c
Criterion Covered Total %
statement 126 138 91.3
branch 47 62 75.8
condition n/a
subroutine n/a
pod n/a
total 173 200 86.5


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4             #include
5             #include "cc_bignum.h"
6              
7             /* I needed a bignum library but couldn't use GMP because I can't assume it's installed everywhere.
8             Since the amount of things I need to do is really small, I rolled my own. */
9              
10 4           void cc_bignum_init_bytes(struct cc_bignum *bn, unsigned char *bytes, size_t length)
11             {
12 4 50         if (length > 0) {
13 4           bn->number = malloc(length);
14 4           bn->length = length;
15 4           memcpy(bn->number, bytes, length);
16 4 100         if (bn->number[length-1] & 0x80) {
17             int i;
18 12 100         for (i = 0; i < bn->length; i++) {
19 11           bn->number[i] = ~bn->number[i];
20             }
21 1           bn->is_negative = 1;
22 1           cc_bignum_add(bn, 1);
23             } else {
24 3           bn->is_negative = 0;
25             }
26             } else {
27 0           bn->number = calloc(1, 1);
28 0           bn->length = 1;
29 0           bn->is_negative = 0;
30             }
31 4           }
32              
33 5           void cc_bignum_init_string(struct cc_bignum *bn, char *string, size_t length)
34             {
35 5           size_t pos = 0;
36              
37 5           bn->number = calloc(1, 1);
38 5           bn->length = 1;
39 5           bn->is_negative = 0;
40              
41 5 100         if (string[pos] == '-') {
42 1           pos++;
43 1           bn->is_negative = 1;
44 4 50         } else if (string[pos] == '+') {
45 0           pos++;
46             }
47              
48 224 100         for (; pos < length; pos++) {
49 219           cc_bignum_mul(bn, 10);
50 219           cc_bignum_add(bn, string[pos]-'0');
51             }
52 5           }
53              
54 170           void cc_bignum_destroy(struct cc_bignum *bn)
55             {
56 170 50         if (bn->number)
57 170           free(bn->number);
58 170           bn->number = NULL;
59 170           }
60              
61 9           void cc_bignum_copy(struct cc_bignum *out, struct cc_bignum *in)
62             {
63 9           out->length = in->length;
64 9           out->number = calloc(1, in->length);
65 9           memcpy(out->number, in->number, in->length);
66 9           out->is_negative = in->is_negative;
67 9           }
68              
69 152           void cc_bignum_move(struct cc_bignum *out, struct cc_bignum *in)
70             {
71 152           out->length = in->length;
72 152           out->number = in->number;
73 152           out->is_negative = in->is_negative;
74 152           in->number = NULL;
75 152           in->length = 0;
76 152           in->is_negative = 0;
77 152           }
78              
79             /* https://stackoverflow.com/a/10525503 */
80 152           uint32_t cc_bignum_divide_8bit(struct cc_bignum *n, uint8_t d, struct cc_bignum *out)
81             {
82             size_t i;
83             uint32_t temp;
84              
85 152           temp = 0;
86 152           out->number = calloc(1, n->length);
87 152           i = n->length;
88 3103 100         while (i > 0) {
89 2951           i--;
90 2951           temp <<= 8;
91 2951           temp |= n->number[i];
92 2951           out->number[i] = temp / d;
93 2951           temp -= out->number[i] * d;
94             }
95              
96 152           out->length = n->length;
97 152           out->is_negative = n->is_negative;
98              
99             /* Probably not correct when the number is negative. But good enough for our use cases. */
100 152           return temp;
101             }
102              
103 219           void cc_bignum_mul(struct cc_bignum *n, uint8_t mul)
104             {
105             size_t i;
106             uint32_t temp;
107              
108 219           temp = 0;
109 219           i = 0;
110 2576 100         while (i < n->length) {
111 2357           temp += (n->number[i] * mul);
112 2357           n->number[i] = temp % 256;
113 2357           temp >>= 8;
114 2357           i++;
115             }
116 219 100         if (temp) {
117 86 50         assert(temp < 256);
118 86           n->length++;
119 86           n->number = realloc(n->number, n->length);
120 86           n->number[i] = temp;
121             }
122 219           }
123              
124 221           void cc_bignum_add(struct cc_bignum *n, uint8_t howmuch)
125             {
126             int i;
127             uint8_t carry;
128 221           carry = howmuch;
129 221 100         if (!carry)
130 195           return;
131 32 50         for (i = 0; i < n->length; i++) {
132 32 100         if (n->number[i] < 256-carry) {
133 26           n->number[i] += carry;
134 26           return;
135             } else {
136 6           n->number[i] += carry;
137 6           carry = 1;
138             }
139             }
140 0           n->number = realloc(n->number, n->length+1);
141 0           n->length++;
142 0           n->number[i] = carry;
143             }
144              
145 160           int cc_bignum_is_zero(struct cc_bignum *n)
146             {
147             int i;
148 160 50         if (n->length == 1 && n->number[0] == 0)
    0          
149 0           return 1;
150 549 100         for (i = 0; i < n->length; i++) {
151 545 100         if (n->number[i] != 0)
152 156           return 0;
153             }
154 4           return 1;
155             }
156              
157             /*
158             void cc_bignum_dump(struct cc_bignum *bn)
159             {
160             int i;
161             printf("BN: ");
162             for (i = 0; i < bn->length; i++) {
163             printf("%.2x ", bn->number[i]);
164             }
165             printf("\n");
166             }
167             */
168              
169 4           void cc_bignum_stringify(struct cc_bignum *bn, char *out, size_t outlen)
170             {
171             struct cc_bignum cur;
172             size_t i, j, tmp_buf_len;
173             char *tmp_buf;
174              
175 4 50         if (cc_bignum_is_zero(bn)) {
176 0           out[0] = '0';
177 0           out[1] = 0;
178 0 0         assert(outlen >= 2);
179 0           return;
180             }
181              
182 4           tmp_buf_len = bn->length*4 + 2;
183 4           tmp_buf = calloc(1, tmp_buf_len);
184              
185 4           cc_bignum_copy(&cur, bn);
186 4           i = 0;
187              
188 156 100         while (!cc_bignum_is_zero(&cur)) {
189             struct cc_bignum new;
190 152           uint8_t remain = cc_bignum_divide_8bit(&cur, 10, &new);
191 152           cc_bignum_destroy(&cur);
192 152           cc_bignum_move(&cur, &new);
193 152           tmp_buf[i++] = '0' + remain;
194 152 50         assert(i < tmp_buf_len);
195             }
196              
197 4 100         if (bn->is_negative)
198 1           tmp_buf[i++] = '-';
199              
200 4 50         assert(i < outlen);
201              
202 157 100         for (j = 0; j < i; j++) {
203 153           out[j] = tmp_buf[i-j-1];
204             }
205 4           out[i] = 0;
206              
207 4           free(tmp_buf);
208 4           cc_bignum_destroy(&cur);
209             }
210              
211 5           size_t cc_bignum_byteify(struct cc_bignum *bn, unsigned char *out, size_t outlen)
212             {
213             struct cc_bignum copy;
214             size_t needed_bytes;
215 5           cc_bignum_copy(©, bn);
216              
217 5 50         assert(copy.length < outlen);
218              
219 5 100         if (copy.is_negative) {
220             int i;
221 11 100         for (i = 0; i < copy.length; i++) {
222 10           copy.number[i] = ~copy.number[i];
223             }
224 1           cc_bignum_add(©, 1);
225 1           out[copy.length] = 0xff;
226             } else {
227 4           out[copy.length] = 0;
228             }
229              
230 5           memcpy(out, copy.number, copy.length);
231              
232 5           needed_bytes = copy.length+1;
233 8 50         while (needed_bytes > 1) {
234 8 100         if (out[needed_bytes-1] == out[copy.length]) {
235 5 100         if ((out[needed_bytes-1]&0x80) == (out[needed_bytes-2]&0x80)) {
236 3           needed_bytes--;
237 3           continue;
238             }
239             }
240 5           break;
241             }
242              
243 5           cc_bignum_destroy(©);
244              
245 5           return needed_bytes;
246             }