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