Repository navigation
Expand file tree
/
Copy pathnum.c
More file actions
164 lines (137 loc) · 5.7 KB
/
Copy pathnum.c
File metadata and controls
164 lines (137 loc) · 5.7 KB
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
/* CC0 license applied, see LICENCE.md */
#include <string.h>
#include "prov/num.h"
typedef enum { BIG = 1, LITTLE = -1 } endian_t;
typedef enum { NEGATIVE = 0xff, POSITIVE = 0x00 } sign_t;
static endian_t nativeendian(void)
{
const int endiantest = 1;
return *((char *)&endiantest) == 1 ? LITTLE : BIG;
}
static sign_t paramsign(const OSSL_PARAM *param)
{
size_t srcmsb = nativeendian() == BIG ? 0 : param->data_size - 1;
return
param->data_type == OSSL_PARAM_UNSIGNED_INTEGER
? POSITIVE
: (((unsigned char *)param->data)[srcmsb] & 0x80
? NEGATIVE
: POSITIVE);
}
struct numdesc {
void *data;
unsigned int data_type; /* The OSSL_PARAM data type */
/* These fields concern the whole number */
size_t size;
endian_t endian;
sign_t sign;
/* These fields concern the limbs of the number */
size_t limbsize;
endian_t limbendian;
/* This is for odd archs. */
/* see the manual for mpz_import() for an in depth explanation. */
size_t limbnailbits;
};
struct resultdesc {
size_t size;
int result; /* 1 or (negative) error */
};
static struct resultdesc provnum_copy(struct numdesc dest, struct numdesc src)
{
struct resultdesc result = { dest.size, 1, };
if (src.data_type != OSSL_PARAM_INTEGER
&& src.data_type != OSSL_PARAM_UNSIGNED_INTEGER) {
result.result = PROVNUM_E_WRONG_TYPE;
return result;
}
if (src.size == 0) {
if (dest.data != NULL)
memset(dest.data, 0, dest.size);
return result;
}
if (src.data == NULL) {
result.result = PROVNUM_E_NULL;
return result;
}
/* Extra data */
size_t srcmsb = src.endian == BIG ? 0 : src.size - 1;
int srcmsb2lsb = src.endian == BIG ? 1 : -1;
/*
* If the source is bigger than the destination, analyse to see if the
* most significant byte is just padding that can be ignored.
* The rules to determine if the most significant byte is just padding
* are:
*
* 1. the most significant byte equals srcsigned, which just so happens
* to have the 2's complement padding value.
* 2. The most significant bit of the next to most significant byte
* equals the most significant bit of srcsigned.
*/
size_t end = dest.data == NULL ? 1 : dest.size;
for (; src.size > end; srcmsb += srcmsb2lsb, src.size--)
if (((unsigned char *)src.data)[srcmsb] != src.sign
|| ((((unsigned char *)src.data)[srcmsb + srcmsb2lsb] & 0x80)
!= (src.sign & 0x80)))
break;
if (src.size > dest.size) {
result.result = PROVNUM_E_TOOBIG;
return result;
}
size_t srclsb = srcmsb + srcmsb2lsb * (src.size - 1);
if (dest.data == NULL) {
result.result = PROVNUM_E_NULL;
return result;
}
/* Simple case, all significant details match */
if (dest.endian == src.endian
&& dest.limbsize == 1
&& dest.limbnailbits == 0
&& (dest.data_type == OSSL_PARAM_INTEGER || src.sign == POSITIVE)) {
if (src.size < dest.size) {
size_t padstart = dest.endian == BIG ? 0 : dest.size - src.size;
memset((unsigned char *)dest.data + padstart, src.sign,
dest.size - src.size);
}
size_t deststart = dest.endian == BIG ? dest.size - src.size : 0;
size_t srcstart = src.endian == BIG ? srcmsb : srclsb;
memcpy((unsigned char *)dest.data + deststart,
(unsigned char *)src.data + srcstart,
src.size);
return result;
}
/* Complex case, for sign or limb conversion. Currently unsupported */
result.result = PROVNUM_E_UNSUPPORTED;
return result;
}
#define implement_provnum(T, DT) \
int provnum_get_##T(T *dest, const OSSL_PARAM *param) \
{ \
endian_t endian = nativeendian(); \
struct numdesc destnd = { \
dest, DT, sizeof(T), endian, POSITIVE, 1, endian, 0 \
}; \
struct numdesc srcnd = { \
param->data, param->data_type, param->data_size, \
endian, paramsign(param), 1, endian, 0 \
}; \
\
struct resultdesc result = provnum_copy(destnd, srcnd); \
return result.result; \
} \
int provnum_set_##T(OSSL_PARAM *param, T src) \
{ \
endian_t endian = nativeendian(); \
struct numdesc destnd = { \
param->data, param->data_type, param->data_size, \
endian, POSITIVE, 1, endian, 0 \
}; \
struct numdesc srcnd = { \
&src, DT, sizeof(T), endian, POSITIVE, 1, endian, 0 \
}; \
\
struct resultdesc result = provnum_copy(destnd, srcnd); \
param->return_size = result.size; \
return result.result; \
}
implement_provnum(size_t, OSSL_PARAM_UNSIGNED_INTEGER)
implement_provnum(int, OSSL_PARAM_INTEGER)