/home/travis/build/MoarVM/MoarVM/src/6model/reprs/P6bigint.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | #ifndef MIN |
4 | | #define MIN(x,y) ((x)<(y)?(x):(y)) |
5 | | #endif |
6 | | |
7 | | /* Get a native int64 from an mp_int. */ |
8 | 15 | static MVMint64 mp_get_int64(MVMThreadContext *tc, mp_int * a) { |
9 | 15 | MVMuint64 res; |
10 | 15 | MVMuint64 signed_max = 9223372036854775807ULL; |
11 | 15 | const int bits = mp_count_bits(a); |
12 | 15 | |
13 | 15 | /* For 64-bit 2's complement numbers the positive max is 2**63-1, which is 63 bits, |
14 | 15 | * but the negative max is -(2**63), which is 64 bits. */ |
15 | 15 | if (MP_NEG == SIGN(a)) { |
16 | 9 | if (bits > 64) { |
17 | 0 | MVM_exception_throw_adhoc(tc, "Cannot unbox %d bit wide bigint into native integer", bits); |
18 | 0 | } |
19 | 9 | ++signed_max; |
20 | 9 | } |
21 | 6 | else { |
22 | 6 | if (bits > 63) { |
23 | 0 | MVM_exception_throw_adhoc(tc, "Cannot unbox %d bit wide bigint into native integer", bits); |
24 | 0 | } |
25 | 6 | } |
26 | 15 | |
27 | 15 | res = mp_get_long_long(a); |
28 | 15 | |
29 | 15 | if (res > signed_max) { |
30 | 0 | /* The mp_int was bigger than a signed result could be. */ |
31 | 0 | MVM_exception_throw_adhoc(tc, "Cannot unbox %d bit wide bigint into native integer", bits); |
32 | 0 | } |
33 | 15 | |
34 | 15 | return MP_NEG == SIGN(a) ? -res : res; |
35 | 15 | } |
36 | | |
37 | | /* Get a native uint64 from an mp_int. */ |
38 | 0 | static MVMuint64 mp_get_uint64(MVMThreadContext *tc, mp_int * a) { |
39 | 0 | const int bits = mp_count_bits(a); |
40 | 0 |
|
41 | 0 | if (bits > 64) { |
42 | 0 | MVM_exception_throw_adhoc(tc, "Cannot unbox %d bit wide bigint into native integer", bits); |
43 | 0 | } |
44 | 0 |
|
45 | 0 | return mp_get_long_long(a); |
46 | 0 | } |
47 | | |
48 | | /* This representation's function pointer table. */ |
49 | | static const MVMREPROps P6bigint_this_repr; |
50 | | |
51 | | /* Creates a new type object of this representation, and associates it with |
52 | | * the given HOW. */ |
53 | 138 | static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) { |
54 | 138 | MVMSTable *st = MVM_gc_allocate_stable(tc, &P6bigint_this_repr, HOW); |
55 | 138 | |
56 | 138 | MVMROOT(tc, st, { |
57 | 138 | MVMObject *obj = MVM_gc_allocate_type_object(tc, st); |
58 | 138 | MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj); |
59 | 138 | st->size = sizeof(MVMP6bigint); |
60 | 138 | }); |
61 | 138 | |
62 | 138 | return st->WHAT; |
63 | 138 | } |
64 | | |
65 | | /* Initializes a new instance. */ |
66 | 1.33k | static void initialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) { |
67 | 1.33k | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
68 | 1.33k | body->u.smallint.flag = MVM_BIGINT_32_FLAG; |
69 | 1.33k | } |
70 | | |
71 | | /* Copies the body of one object to another. */ |
72 | 2 | static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) { |
73 | 2 | MVMP6bigintBody *src_body = (MVMP6bigintBody *)src; |
74 | 2 | MVMP6bigintBody *dest_body = (MVMP6bigintBody *)dest; |
75 | 2 | if (MVM_BIGINT_IS_BIG(src_body)) { |
76 | 0 | dest_body->u.bigint = MVM_malloc(sizeof(mp_int)); |
77 | 0 | mp_init_copy(dest_body->u.bigint, src_body->u.bigint); |
78 | 0 | } |
79 | 2 | else { |
80 | 2 | dest_body->u.smallint.flag = src_body->u.smallint.flag; |
81 | 2 | dest_body->u.smallint.value = src_body->u.smallint.value; |
82 | 2 | } |
83 | 2 | } |
84 | | |
85 | 402 | static void set_int(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMint64 value) { |
86 | 402 | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
87 | 402 | if (MVM_IS_32BIT_INT(value)) { |
88 | 402 | body->u.smallint.flag = MVM_BIGINT_32_FLAG; |
89 | 402 | body->u.smallint.value = (MVMint32)value; |
90 | 402 | } |
91 | 0 | else { |
92 | 0 | mp_int *i = MVM_malloc(sizeof(mp_int)); |
93 | 0 | mp_init(i); |
94 | 0 | if (value >= 0) { |
95 | 0 | MVM_bigint_mp_set_uint64(i, (MVMuint64)value); |
96 | 0 | } |
97 | 0 | else { |
98 | 0 | MVM_bigint_mp_set_uint64(i, (MVMuint64)-value); |
99 | 0 | mp_neg(i, i); |
100 | 0 | } |
101 | 0 | body->u.bigint = i; |
102 | 0 | } |
103 | 402 | } |
104 | 44 | static MVMint64 get_int(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) { |
105 | 44 | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
106 | 44 | if (MVM_BIGINT_IS_BIG(body)) { |
107 | 15 | mp_int *i = body->u.bigint; |
108 | 15 | return mp_get_int64(tc, i); |
109 | 15 | } |
110 | 29 | else { |
111 | 29 | return body->u.smallint.value; |
112 | 29 | } |
113 | 44 | } |
114 | | |
115 | 0 | static void set_uint(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMuint64 value) { |
116 | 0 | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
117 | 0 | if (value < 2147483647ULL) { |
118 | 0 | body->u.smallint.flag = MVM_BIGINT_32_FLAG; |
119 | 0 | body->u.smallint.value = (MVMint32)value; |
120 | 0 | } |
121 | 0 | else { |
122 | 0 | mp_int *i = MVM_malloc(sizeof(mp_int)); |
123 | 0 | mp_init(i); |
124 | 0 | MVM_bigint_mp_set_uint64(i, value); |
125 | 0 | body->u.bigint = i; |
126 | 0 | } |
127 | 0 | } |
128 | 0 | static MVMuint64 get_uint(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) { |
129 | 0 | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
130 | 0 | if (MVM_BIGINT_IS_BIG(body)) { |
131 | 0 | mp_int *i = body->u.bigint; |
132 | 0 | if (MP_NEG == SIGN(i)) |
133 | 0 | MVM_exception_throw_adhoc(tc, "Cannot unbox negative bigint into native unsigned integer"); |
134 | 0 | else |
135 | 0 | return mp_get_uint64(tc, i); |
136 | 0 | } |
137 | 0 | else { |
138 | 0 | return body->u.smallint.value; |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | 2.68k | static void * get_boxed_ref(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMuint32 repr_id) { |
143 | 2.68k | if (repr_id == MVM_REPR_ID_P6bigint) |
144 | 2.68k | return data; |
145 | 2.68k | |
146 | 0 | MVM_exception_throw_adhoc(tc, |
147 | 0 | "P6bigint representation cannot unbox to other types"); |
148 | 0 | } |
149 | | |
150 | | |
151 | | static const MVMStorageSpec storage_spec = { |
152 | | MVM_STORAGE_SPEC_INLINED, /* inlineable */ |
153 | | sizeof(MVMP6bigintBody) * 8, /* bits */ |
154 | | ALIGNOF(MVMP6bigintBody), /* align */ |
155 | | MVM_STORAGE_SPEC_BP_INT, /* boxed_primitive */ |
156 | | MVM_STORAGE_SPEC_CAN_BOX_INT, /* can_box */ |
157 | | 0, /* is_unsigned */ |
158 | | }; |
159 | | |
160 | | |
161 | | /* Gets the storage specification for this representation. */ |
162 | 27 | static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) { |
163 | 27 | return &storage_spec; |
164 | 27 | } |
165 | | |
166 | | /* Compose the representation. */ |
167 | 7 | static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *info) { |
168 | 7 | /* Nothing to do for this REPR. */ |
169 | 7 | } |
170 | | |
171 | 0 | static void gc_cleanup(MVMThreadContext *tc, MVMSTable *st, void *data) { |
172 | 0 | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
173 | 0 | if (MVM_BIGINT_IS_BIG(body)) { |
174 | 0 | mp_clear(body->u.bigint); |
175 | 0 | MVM_free(body->u.bigint); |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | 174 | static void gc_free(MVMThreadContext *tc, MVMObject *obj) { |
180 | 174 | MVMP6bigintBody *body = &((MVMP6bigint *)obj)->body; |
181 | 174 | if (MVM_BIGINT_IS_BIG(body)) { |
182 | 117 | mp_clear(body->u.bigint); |
183 | 117 | MVM_free(body->u.bigint); |
184 | 117 | } |
185 | 174 | } |
186 | | |
187 | | /* Serializes the bigint. */ |
188 | 3 | static void serialize(MVMThreadContext *tc, MVMSTable *st, void *data, MVMSerializationWriter *writer) { |
189 | 3 | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
190 | 3 | if (MVM_BIGINT_IS_BIG(body)) { |
191 | 0 | mp_int *i = body->u.bigint; |
192 | 0 | int len; |
193 | 0 | char *buf; |
194 | 0 | MVMString *str; |
195 | 0 | mp_radix_size(i, 10, &len); |
196 | 0 | buf = (char *)MVM_malloc(len); |
197 | 0 | mp_toradix(i, buf, 10); |
198 | 0 |
|
199 | 0 | /* len - 1 because buf is \0-terminated */ |
200 | 0 | str = MVM_string_ascii_decode(tc, tc->instance->VMString, buf, len - 1); |
201 | 0 |
|
202 | 0 | /* write the "is small" flag */ |
203 | 0 | MVM_serialization_write_int(tc, writer, 0); |
204 | 0 | MVM_serialization_write_str(tc, writer, str); |
205 | 0 | MVM_free(buf); |
206 | 0 | } |
207 | 3 | else { |
208 | 3 | /* write the "is small" flag */ |
209 | 3 | MVM_serialization_write_int(tc, writer, 1); |
210 | 3 | MVM_serialization_write_int(tc, writer, body->u.smallint.value); |
211 | 3 | } |
212 | 3 | } |
213 | | |
214 | | /* Set the size on the STable. */ |
215 | 2 | static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) { |
216 | 2 | st->size = sizeof(MVMP6bigint); |
217 | 2 | } |
218 | | |
219 | | /* Deserializes the bigint. */ |
220 | 3 | static void deserialize(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMSerializationReader *reader) { |
221 | 3 | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
222 | 3 | |
223 | 3 | if (MVM_serialization_read_int(tc, reader) == 1) { /* Is it small int? */ |
224 | 3 | body->u.smallint.flag = MVM_BIGINT_32_FLAG; |
225 | 3 | body->u.smallint.value = MVM_serialization_read_int(tc, reader); |
226 | 0 | } else { /* big int */ |
227 | 0 | char *buf = MVM_string_ascii_encode(tc, MVM_serialization_read_str(tc, reader), NULL, 0); |
228 | 0 | body->u.bigint = MVM_malloc(sizeof(mp_int)); |
229 | 0 | mp_init(body->u.bigint); |
230 | 0 | mp_read_radix(body->u.bigint, buf, 10); |
231 | 0 | MVM_free(buf); |
232 | 0 | } |
233 | 3 | } |
234 | | |
235 | | /* Calculates the non-GC-managed memory we hold on to. */ |
236 | 18 | static MVMuint64 unmanaged_size(MVMThreadContext *tc, MVMSTable *st, void *data) { |
237 | 18 | MVMP6bigintBody *body = (MVMP6bigintBody *)data; |
238 | 18 | if (MVM_BIGINT_IS_BIG(body)) |
239 | 0 | return body->u.bigint->alloc; |
240 | 18 | else |
241 | 18 | return 0; |
242 | 18 | } |
243 | | |
244 | | /* Initializes the representation. */ |
245 | 130 | const MVMREPROps * MVMP6bigint_initialize(MVMThreadContext *tc) { |
246 | 130 | return &P6bigint_this_repr; |
247 | 130 | } |
248 | | |
249 | | static const MVMREPROps P6bigint_this_repr = { |
250 | | type_object_for, |
251 | | MVM_gc_allocate_object, |
252 | | initialize, |
253 | | copy_to, |
254 | | MVM_REPR_DEFAULT_ATTR_FUNCS, |
255 | | { |
256 | | set_int, |
257 | | get_int, |
258 | | MVM_REPR_DEFAULT_SET_NUM, |
259 | | MVM_REPR_DEFAULT_GET_NUM, |
260 | | MVM_REPR_DEFAULT_SET_STR, |
261 | | MVM_REPR_DEFAULT_GET_STR, |
262 | | set_uint, |
263 | | get_uint, |
264 | | get_boxed_ref |
265 | | }, /* box_funcs */ |
266 | | MVM_REPR_DEFAULT_POS_FUNCS, |
267 | | MVM_REPR_DEFAULT_ASS_FUNCS, |
268 | | MVM_REPR_DEFAULT_ELEMS, |
269 | | get_storage_spec, |
270 | | NULL, /* change_type */ |
271 | | serialize, |
272 | | deserialize, |
273 | | NULL, /* serialize_repr_data */ |
274 | | NULL, /* deserialize_repr_data */ |
275 | | deserialize_stable_size, |
276 | | NULL, /* gc_mark */ |
277 | | gc_free, |
278 | | gc_cleanup, |
279 | | NULL, /* gc_mark_repr_data */ |
280 | | NULL, /* gc_free_repr_data */ |
281 | | compose, |
282 | | NULL, /* spesh */ |
283 | | "P6bigint", /* name */ |
284 | | MVM_REPR_ID_P6bigint, |
285 | | unmanaged_size, /* unmanaged_size */ |
286 | | NULL, /* describe_refs */ |
287 | | }; |