Merge pull request #62 from BenceJanosSzabo/master
[deliverable/titan.core.git] / core / RInt.cc
1 /******************************************************************************
2 * Copyright (c) 2000-2016 Ericsson Telecom AB
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Balasko, Jeno
10 * Kovacs, Ferenc
11 * Raduly, Csaba
12 *
13 ******************************************************************************/
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17
18 #include "Error.hh"
19 #include "RInt.hh"
20 #include "../common/memory.h"
21
22 // Note: Do not use dbgnew.hh. It doesn't play well with Qt in mctr_gui.
23 // This class is mostly the same as compiler2/Int.cc/Int.hh. They should be
24 // merged later, but it doesn't seem to be easy.
25
26 #include <openssl/crypto.h>
27 #include <openssl/bn.h>
28
29 int_val_t::int_val_t() : native_flag(true)
30 {
31 val.openssl = NULL;
32 }
33
34 int_val_t::int_val_t(const int_val_t& v)
35 {
36 native_flag = v.is_native();
37 if (native_flag) val.native = v.get_val();
38 else val.openssl = BN_dup(v.get_val_openssl());
39 }
40
41 int_val_t::int_val_t(const char *s)
42 {
43 BIGNUM *n = NULL;
44 if (!BN_dec2bn(&n, *s == '+' ? s + 1 : s))
45 TTCN_error("Unexpected error when converting `%s' to integer", s);
46 if (BN_num_bits(n) > (int)sizeof(int) * 8 - 1) {
47 native_flag = false;
48 val.openssl = n;
49 } else {
50 native_flag = true;
51 val.native = string2RInt(s);
52 BN_free(n);
53 }
54 }
55
56 int_val_t::~int_val_t()
57 {
58 if (!native_flag) BN_free(val.openssl);
59 }
60
61 const RInt& int_val_t::get_val() const
62 {
63 if (!native_flag) TTCN_error("Invalid conversion of a large integer value");
64 return val.native;
65 }
66
67 BIGNUM *int_val_t::get_val_openssl() const
68 {
69 if (native_flag) TTCN_error("Invalid conversion of a large integer value");
70 return val.openssl;
71 }
72
73 // The returned string must be freed with Free by the caller. Returning
74 // CHARSTRING would be much better, but there're some linking issues.
75 char *int_val_t::as_string() const
76 {
77 if (native_flag) {
78 char *tmp = mprintf("%d", val.native);
79 return tmp;
80 } else {
81 char *tmp1 = NULL, *tmp2 = NULL;
82 if (!(tmp1 = BN_bn2dec(val.openssl))) TTCN_error("int_val_t::c_str()");
83 tmp2 = mcopystr(tmp1);
84 OPENSSL_free(tmp1);
85 return tmp2;
86 }
87 }
88
89 int_val_t& int_val_t::operator=(RInt v)
90 {
91 if (!native_flag) BN_free(val.openssl);
92 native_flag = true;
93 val.native = v;
94 return *this;
95 }
96
97 int_val_t& int_val_t::operator=(const int_val_t& right)
98 {
99 if (!native_flag) BN_free(val.openssl);
100 native_flag = right.is_native();
101 if (native_flag) val.native = right.get_val();
102 else val.openssl = BN_dup(right.get_val_openssl());
103 return *this;
104 }
105
106 int_val_t& int_val_t::operator<<=(RInt right)
107 {
108 // It makes no sense to support negative operands. GCC returns constant "0"
109 // with "warning: left shift count is negative" for these shifts.
110 if (right < 0) TTCN_error("The second operand of bitwise shift operators "
111 "cannot be negative");
112 if (right == 0) return *this;
113 if (native_flag) {
114 BIGNUM *result = BN_new();
115 // Ugly, but can't do better now.
116 char *tmp_str = as_string();
117 BN_dec2bn(&result, tmp_str);
118 Free(tmp_str);
119 BN_lshift(result, result, right);
120 if (BN_num_bits(result) > (int)sizeof(int) * 8 - 1) {
121 val.openssl = result;
122 native_flag = false;
123 } else {
124 val.native <<= right;
125 BN_free(result);
126 }
127 } else {
128 BN_lshift(val.openssl, val.openssl, right);
129 }
130 return *this;
131 }
132
133 int_val_t& int_val_t::operator>>=(RInt right)
134 {
135 if (right < 0) TTCN_error("The second operand of bitwise shift operators "
136 "cannot be negative");
137 if (right == 0) return *this;
138 if (native_flag) {
139 val.native >>= right;
140 } else {
141 BN_rshift(val.openssl, val.openssl, right);
142 if (BN_num_bits(val.openssl) <= (int)sizeof(RInt) * 8 - 1) {
143 char *result_str = BN_bn2dec(val.openssl);
144 RInt result_i = string2RInt(result_str);
145 OPENSSL_free(result_str);
146 native_flag = true;
147 BN_free(val.openssl);
148 val.native = result_i;
149 }
150 }
151 return *this;
152 }
153
154 int_val_t& int_val_t::operator+=(RInt right)
155 {
156 // Unfortunately we have to check the sign of the "right" operand and
157 // perform addition or subtraction accordingly.
158 if (right == 0) return *this;
159 bool neg = right < 0;
160 if (native_flag) {
161 BIGNUM *result = BN_new();
162 BN_set_word(result, (BN_ULONG)val.native);
163 if (neg) BN_sub_word(result, (BN_ULONG)right);
164 else BN_add_word(result, (BN_ULONG)right);
165 if (BN_num_bits(result) > (int)sizeof(int) * 8 - 1) {
166 val.openssl = result;
167 native_flag = false;
168 } else {
169 val.native += right;
170 BN_free(result);
171 }
172 } else {
173 if (neg) BN_sub_word(val.openssl, (BN_ULONG)right);
174 else BN_add_word(val.openssl, (BN_ULONG)right);
175 if (BN_num_bits(val.openssl) <= (int)sizeof(int) * 8 - 1) {
176 BN_ULONG tmp = BN_get_word(val.openssl);
177 if (BN_is_negative(val.openssl)) tmp *= -1;
178 BN_free(val.openssl);
179 val.native = tmp;
180 native_flag = true;
181 }
182 }
183 return *this;
184 }
185
186 bool int_val_t::operator==(const int_val_t& right) const
187 {
188 if (native_flag) {
189 if (right.is_native()) {
190 return val.native == right.val.native;
191 } else {
192 BIGNUM *this_big = to_openssl(val.native);
193 int eq = BN_cmp(this_big, right.get_val_openssl());
194 BN_free(this_big);
195 return eq == 0;
196 }
197 } else {
198 if (right.is_native()) {
199 BIGNUM *right_big = to_openssl(right.val.native);
200 int eq = BN_cmp(val.openssl, right_big);
201 BN_free(right_big);
202 return eq == 0;
203 } else {
204 return BN_cmp(val.openssl, right.val.openssl) == 0;
205 }
206 }
207 }
208
209 bool int_val_t::operator<(const int_val_t& v) const
210 {
211 if (native_flag) {
212 if (v.is_native()) {
213 return val.native < v.val.native;
214 } else {
215 BIGNUM *this_big = to_openssl(val.native);
216 int this_equ = BN_cmp(this_big, v.get_val_openssl());
217 BN_free(this_big);
218 return this_equ == -1;
219 }
220 } else {
221 if (v.is_native()) {
222 BIGNUM *v_big = to_openssl(v.val.native);
223 int v_equ = BN_cmp(val.openssl, v_big);
224 BN_free(v_big);
225 return v_equ == -1;
226 } else {
227 return BN_cmp(val.openssl, v.val.openssl) == -1;
228 }
229 }
230 }
231
232 int_val_t int_val_t::operator&(RInt right) const
233 {
234 // TODO Right can be int_val_t. BN_is_bit_set/BN_set_bit should be used.
235 BN_ULONG right_bn_ulong = (BN_ULONG)right;
236 if (right != (RInt)right_bn_ulong) TTCN_error("Bitmask is too big");
237 if (native_flag) {
238 return int_val_t(val.native & right);
239 } else {
240 BIGNUM *tmp = BN_dup(val.openssl);
241 BN_mask_bits(tmp, sizeof(BN_ULONG) * 8);
242 BN_ULONG word = BN_get_word(tmp);
243 BN_free(tmp);
244 return int_val_t(word & right_bn_ulong);
245 }
246 }
247
248 // Cannot be inline since bignum_st is just a forward declaration in the
249 // header. The compiler must know bignum_st at this point.
250 bool int_val_t::is_negative() const
251 {
252 return (native_flag && val.native < 0) ||
253 (!native_flag && BN_is_negative(val.openssl));
254 }
255
256 double int_val_t::to_real() const
257 {
258 if (native_flag) {
259 return (double)val.native;
260 } else {
261 char *result_str = BN_bn2dec(val.openssl);
262 double result = 0;
263 if (sscanf(result_str, "%lf", &result) != 1)
264 TTCN_error("Conversion of integer value `%s' to float failed",
265 result_str);
266 OPENSSL_free(result_str);
267 return result;
268 }
269 }
270
271 BIGNUM *to_openssl(int other_value)
272 {
273 BIGNUM *result = NULL;
274 char *str = mprintf("%d", other_value);
275 BN_dec2bn(&result, str);
276 Free(str);
277 return result;
278 }
279
280 RInt string2RInt(const char *s)
281 {
282 errno = 0;
283 RInt i = (RInt)strtol(s, (char **)NULL, 10);
284 switch (errno) {
285 case ERANGE:
286 TTCN_error("Overflow when converting `%s' to integer value: %s", s,
287 strerror(errno));
288 break;
289 case 0:
290 break;
291 default: // EINVAL and others.
292 TTCN_error("Unexpected error when converting `%s' to integer: %s", s,
293 strerror(errno));
294 break;
295 }
296 return i;
297 }
This page took 0.035399 seconds and 5 git commands to generate.