1 | /* |
2 | * particle_integer.c |
3 | * |
4 | * Copyright (C) 2015 Aerospike, Inc. |
5 | * |
6 | * Portions may be licensed to Aerospike, Inc. under one or more contributor |
7 | * license agreements. |
8 | * |
9 | * This program is free software: you can redistribute it and/or modify it under |
10 | * the terms of the GNU Affero General Public License as published by the Free |
11 | * Software Foundation, either version 3 of the License, or (at your option) any |
12 | * later version. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, but WITHOUT |
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
16 | * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
17 | * details. |
18 | * |
19 | * You should have received a copy of the GNU Affero General Public License |
20 | * along with this program. If not, see http://www.gnu.org/licenses/ |
21 | */ |
22 | |
23 | |
24 | #include "base/particle_integer.h" |
25 | |
26 | #include <stddef.h> |
27 | #include <stdint.h> |
28 | |
29 | #include "aerospike/as_boolean.h" |
30 | #include "aerospike/as_integer.h" |
31 | #include "aerospike/as_msgpack.h" |
32 | #include "aerospike/as_val.h" |
33 | #include "citrusleaf/cf_byte_order.h" |
34 | |
35 | #include "fault.h" |
36 | |
37 | #include "base/datamodel.h" |
38 | #include "base/particle.h" |
39 | #include "base/proto.h" |
40 | |
41 | |
42 | // Some INTEGER particle interface function declarations are in |
43 | // particle_integer.h since INTEGER functions are used by other particles. |
44 | |
45 | // Handle on-device "flat" format. |
46 | const uint8_t *integer_skip_flat(const uint8_t *flat, const uint8_t *end); |
47 | const uint8_t *integer_from_flat(const uint8_t *flat, const uint8_t *end, as_particle **pp); |
48 | uint32_t integer_flat_size(const as_particle *p); |
49 | uint32_t integer_to_flat(const as_particle *p, uint8_t *flat); |
50 | |
51 | |
52 | //========================================================== |
53 | // INTEGER particle interface - vtable. |
54 | // |
55 | |
56 | const as_particle_vtable integer_vtable = { |
57 | integer_destruct, |
58 | integer_size, |
59 | |
60 | integer_concat_size_from_wire, |
61 | integer_append_from_wire, |
62 | integer_prepend_from_wire, |
63 | integer_incr_from_wire, |
64 | integer_size_from_wire, |
65 | integer_from_wire, |
66 | integer_compare_from_wire, |
67 | integer_wire_size, |
68 | integer_to_wire, |
69 | |
70 | integer_size_from_asval, |
71 | integer_from_asval, |
72 | integer_to_asval, |
73 | integer_asval_wire_size, |
74 | integer_asval_to_wire, |
75 | |
76 | integer_size_from_msgpack, |
77 | integer_from_msgpack, |
78 | |
79 | integer_skip_flat, |
80 | integer_from_flat, // cast copies embedded value out |
81 | integer_from_flat, |
82 | integer_flat_size, |
83 | integer_to_flat |
84 | }; |
85 | |
86 | |
87 | //========================================================== |
88 | // Typedefs & constants. |
89 | // |
90 | |
91 | typedef struct integer_mem_s { |
92 | uint8_t do_not_use; // already know it's an int type |
93 | uint64_t i; |
94 | } __attribute__ ((__packed__)) integer_mem; |
95 | |
96 | typedef struct integer_flat_s { |
97 | uint8_t type; |
98 | uint8_t size; |
99 | uint8_t data[]; |
100 | } __attribute__ ((__packed__)) integer_flat; |
101 | |
102 | |
103 | //========================================================== |
104 | // INTEGER particle interface - function definitions. |
105 | // |
106 | |
107 | //------------------------------------------------ |
108 | // Destructor, etc. |
109 | // |
110 | |
111 | void |
112 | integer_destruct(as_particle *p) |
113 | { |
114 | // Nothing to do - integer values live in the as_bin. |
115 | } |
116 | |
117 | uint32_t |
118 | integer_size(const as_particle *p) |
119 | { |
120 | // Integer values live in the as_bin instead of a pointer. |
121 | return 0; |
122 | } |
123 | |
124 | //------------------------------------------------ |
125 | // Handle "wire" format. |
126 | // |
127 | |
128 | int32_t |
129 | integer_concat_size_from_wire(as_particle_type wire_type, const uint8_t *wire_value, uint32_t value_size, as_particle **pp) |
130 | { |
131 | cf_warning(AS_PARTICLE, "concat size for integer/float" ); |
132 | return -AS_ERR_INCOMPATIBLE_TYPE; |
133 | } |
134 | |
135 | int |
136 | integer_append_from_wire(as_particle_type wire_type, const uint8_t *wire_value, uint32_t value_size, as_particle **pp) |
137 | { |
138 | cf_warning(AS_PARTICLE, "append to integer/float" ); |
139 | return -AS_ERR_INCOMPATIBLE_TYPE; |
140 | } |
141 | |
142 | int |
143 | integer_prepend_from_wire(as_particle_type wire_type, const uint8_t *wire_value, uint32_t value_size, as_particle **pp) |
144 | { |
145 | cf_warning(AS_PARTICLE, "prepend to integer/float" ); |
146 | return -AS_ERR_INCOMPATIBLE_TYPE; |
147 | } |
148 | |
149 | int |
150 | integer_incr_from_wire(as_particle_type wire_type, const uint8_t *wire_value, uint32_t value_size, as_particle **pp) |
151 | { |
152 | if (wire_type != AS_PARTICLE_TYPE_INTEGER) { |
153 | cf_warning(AS_PARTICLE, "increment with non integer type %u" , wire_type); |
154 | return -AS_ERR_INCOMPATIBLE_TYPE; |
155 | } |
156 | |
157 | uint64_t i; |
158 | |
159 | switch (value_size) { |
160 | case 8: |
161 | i = cf_swap_from_be64(*(uint64_t *)wire_value); |
162 | break; |
163 | case 4: |
164 | i = (uint64_t)cf_swap_from_be32(*(uint32_t *)wire_value); |
165 | break; |
166 | case 2: |
167 | i = (uint64_t)cf_swap_from_be16(*(uint16_t *)wire_value); |
168 | break; |
169 | case 1: |
170 | i = (uint64_t)*wire_value; |
171 | break; |
172 | case 16: // memcache increment - it's special |
173 | i = cf_swap_from_be64(*(uint64_t *)wire_value); |
174 | // For memcache, decrements floor at 0. |
175 | if ((int64_t)i < 0 && *(uint64_t *)pp + i > *(uint64_t *)pp) { |
176 | *pp = 0; |
177 | return 0; |
178 | } |
179 | break; |
180 | default: |
181 | cf_warning(AS_PARTICLE, "unexpected value size %u" , value_size); |
182 | return -AS_ERR_PARAMETER; |
183 | } |
184 | |
185 | (*(uint64_t *)pp) += i; |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | int32_t |
191 | integer_size_from_wire(const uint8_t *wire_value, uint32_t value_size) |
192 | { |
193 | // Integer values live in the as_bin instead of a pointer. |
194 | return 0; |
195 | } |
196 | |
197 | int |
198 | integer_from_wire(as_particle_type wire_type, const uint8_t *wire_value, uint32_t value_size, as_particle **pp) |
199 | { |
200 | uint64_t i; |
201 | |
202 | switch (value_size) { |
203 | case 8: |
204 | i = cf_swap_from_be64(*(uint64_t *)wire_value); |
205 | break; |
206 | case 4: |
207 | i = (uint64_t)cf_swap_from_be32(*(uint32_t *)wire_value); |
208 | break; |
209 | case 2: |
210 | i = (uint64_t)cf_swap_from_be16(*(uint16_t *)wire_value); |
211 | break; |
212 | case 1: |
213 | i = (uint64_t)*wire_value; |
214 | break; |
215 | default: |
216 | cf_warning(AS_PARTICLE, "unexpected value size %u" , value_size); |
217 | return -AS_ERR_PARAMETER; |
218 | } |
219 | |
220 | *pp = (as_particle *)i; |
221 | |
222 | return 0; |
223 | } |
224 | |
225 | int |
226 | integer_compare_from_wire(const as_particle *p, as_particle_type wire_type, const uint8_t *wire_value, uint32_t value_size) |
227 | { |
228 | if (wire_type != AS_PARTICLE_TYPE_INTEGER) { |
229 | return 1; |
230 | } |
231 | |
232 | uint64_t i; |
233 | |
234 | switch (value_size) { |
235 | case 8: |
236 | i = cf_swap_from_be64(*(uint64_t *)wire_value); |
237 | break; |
238 | case 4: |
239 | i = (uint64_t)cf_swap_from_be32(*(uint32_t *)wire_value); |
240 | break; |
241 | case 2: |
242 | i = (uint64_t)cf_swap_from_be16(*(uint16_t *)wire_value); |
243 | break; |
244 | case 1: |
245 | i = (uint64_t)*wire_value; |
246 | break; |
247 | default: |
248 | return -AS_ERR_UNKNOWN; |
249 | } |
250 | |
251 | return (uint64_t)p == i ? 0 : 1; |
252 | } |
253 | |
254 | uint32_t |
255 | integer_wire_size(const as_particle *p) |
256 | { |
257 | return (uint32_t)sizeof(uint64_t); |
258 | } |
259 | |
260 | uint32_t |
261 | integer_to_wire(const as_particle *p, uint8_t *wire) |
262 | { |
263 | *(uint64_t *)wire = cf_swap_to_be64((uint64_t)p); |
264 | |
265 | return (uint32_t)sizeof(uint64_t); |
266 | } |
267 | |
268 | //------------------------------------------------ |
269 | // Handle as_val translation. |
270 | // |
271 | |
272 | uint32_t |
273 | integer_size_from_asval(const as_val *val) |
274 | { |
275 | // Integer values live in the as_bin instead of a pointer. |
276 | return 0; |
277 | } |
278 | |
279 | void |
280 | integer_from_asval(const as_val *val, as_particle **pp) |
281 | { |
282 | // Unfortunately AS_BOOLEANs (as well as AS_INTEGERs) become INTEGER |
283 | // particles, so we have to check the as_val type here. |
284 | |
285 | as_val_t vtype = as_val_type(val); |
286 | int64_t i; |
287 | |
288 | switch (vtype) { |
289 | case AS_INTEGER: |
290 | i = as_integer_get(as_integer_fromval(val)); |
291 | break; |
292 | case AS_BOOLEAN: |
293 | i = as_boolean_get(as_boolean_fromval(val)) ? 1 : 0; |
294 | break; |
295 | default: |
296 | cf_crash(AS_PARTICLE, "unexpected as_val_t %d" , vtype); |
297 | return; |
298 | } |
299 | |
300 | *pp = (as_particle *)i; |
301 | } |
302 | |
303 | as_val * |
304 | integer_to_asval(const as_particle *p) |
305 | { |
306 | return (as_val *)as_integer_new((uint64_t)p); |
307 | } |
308 | |
309 | uint32_t |
310 | integer_asval_wire_size(const as_val *val) |
311 | { |
312 | return (uint32_t)sizeof(uint64_t); |
313 | } |
314 | |
315 | uint32_t |
316 | integer_asval_to_wire(const as_val *val, uint8_t *wire) |
317 | { |
318 | // Unfortunately AS_BOOLEANs (as well as AS_INTEGERs) become INTEGER |
319 | // particles, so we have to check the as_val type here. |
320 | |
321 | as_val_t vtype = as_val_type(val); |
322 | int64_t i; |
323 | |
324 | switch (vtype) { |
325 | case AS_INTEGER: |
326 | i = as_integer_get(as_integer_fromval(val)); |
327 | break; |
328 | case AS_BOOLEAN: |
329 | i = as_boolean_get(as_boolean_fromval(val)) ? 1 : 0; |
330 | break; |
331 | default: |
332 | cf_crash(AS_PARTICLE, "unexpected as_val_t %d" , vtype); |
333 | return 0; |
334 | } |
335 | |
336 | *(uint64_t *)wire = cf_swap_to_be64((uint64_t)i); |
337 | |
338 | return (uint32_t)sizeof(uint64_t); |
339 | } |
340 | |
341 | //------------------------------------------------ |
342 | // Handle msgpack translation. |
343 | // |
344 | |
345 | uint32_t |
346 | integer_size_from_msgpack(const uint8_t *packed, uint32_t packed_size) |
347 | { |
348 | // Integer values live in the as_bin instead of a pointer. |
349 | return 0; |
350 | } |
351 | |
352 | void |
353 | integer_from_msgpack(const uint8_t *packed, uint32_t packed_size, |
354 | as_particle **pp) |
355 | { |
356 | int64_t i; |
357 | |
358 | if (*packed == 0xc2) { // false |
359 | i = 0; |
360 | } |
361 | else if (*packed == 0xc3) { // true |
362 | i = 1; |
363 | } |
364 | else { |
365 | as_unpacker pk = { |
366 | .buffer = packed, |
367 | .offset = 0, |
368 | .length = packed_size |
369 | }; |
370 | |
371 | if (as_unpack_int64(&pk, &i) != 0) { |
372 | cf_fault_hex_dump("msgpack" , pk.buffer, pk.length); |
373 | cf_crash(AS_PARTICLE, "invalid msgpack" ); |
374 | } |
375 | } |
376 | |
377 | *pp = (as_particle *)i; |
378 | } |
379 | |
380 | //------------------------------------------------ |
381 | // Handle on-device "flat" format. |
382 | // |
383 | |
384 | const uint8_t * |
385 | integer_skip_flat(const uint8_t *flat, const uint8_t *end) |
386 | { |
387 | if (flat + sizeof(integer_flat) > end) { |
388 | cf_warning(AS_PARTICLE, "incomplete flat integer" ); |
389 | return NULL; |
390 | } |
391 | |
392 | integer_flat *p_int_flat = (integer_flat *)flat; |
393 | |
394 | return flat + sizeof(integer_flat) + p_int_flat->size; |
395 | } |
396 | |
397 | const uint8_t * |
398 | integer_from_flat(const uint8_t *flat, const uint8_t *end, as_particle **pp) |
399 | { |
400 | if (flat + sizeof(integer_flat) > end) { |
401 | cf_warning(AS_PARTICLE, "incomplete flat integer" ); |
402 | return NULL; |
403 | } |
404 | |
405 | const integer_flat *p_int_flat = (const integer_flat *)flat; |
406 | // Type is correct, since we got here - no need to check against end. |
407 | |
408 | flat += sizeof(integer_flat) + p_int_flat->size; |
409 | |
410 | if (flat > end) { |
411 | cf_warning(AS_PARTICLE, "incomplete flat integer" ); |
412 | return NULL; |
413 | } |
414 | |
415 | uint64_t i; |
416 | |
417 | switch (p_int_flat->size) { |
418 | case 8: |
419 | i = *(uint64_t *)p_int_flat->data; |
420 | break; |
421 | case 4: |
422 | i = *(uint32_t *)p_int_flat->data; |
423 | break; |
424 | case 2: |
425 | i = *(uint16_t *)p_int_flat->data; |
426 | break; |
427 | case 1: |
428 | i = *(uint8_t *)p_int_flat->data; |
429 | break; |
430 | default: |
431 | cf_warning(AS_PARTICLE, "invalid flat integer size" ); |
432 | return NULL; |
433 | } |
434 | |
435 | // Integer values live in an as_bin instead of a pointer. Also, flat |
436 | // integers are host order, so no byte swap. |
437 | *pp = (as_particle *)i; |
438 | |
439 | return flat; |
440 | } |
441 | |
442 | uint32_t |
443 | integer_flat_size(const as_particle *p) |
444 | { |
445 | uint64_t i = (uint64_t)p; |
446 | |
447 | if ((i & ~0xFFFFffffL) != 0) { |
448 | return (uint32_t)(sizeof(integer_flat) + 8); |
449 | } |
450 | |
451 | if ((i & ~0xFFFFL) != 0) { |
452 | return (uint32_t)(sizeof(integer_flat) + 4); |
453 | } |
454 | |
455 | if ((i & ~0xFFL) != 0) { |
456 | return (uint32_t)(sizeof(integer_flat) + 2); |
457 | } |
458 | |
459 | return (uint32_t)(sizeof(integer_flat) + 1); |
460 | } |
461 | |
462 | uint32_t |
463 | integer_to_flat(const as_particle *p, uint8_t *flat) |
464 | { |
465 | integer_flat *p_int_flat = (integer_flat *)flat; |
466 | |
467 | // Already wrote the type. |
468 | |
469 | uint64_t i = (uint64_t)p; |
470 | |
471 | if ((i & ~0xFFFFffffL) != 0) { |
472 | p_int_flat->size = 8; |
473 | *(uint64_t *)p_int_flat->data = i; |
474 | } |
475 | else if ((i & ~0xFFFFL) != 0) { |
476 | p_int_flat->size = 4; |
477 | *(uint32_t *)p_int_flat->data = (uint32_t)i; |
478 | } |
479 | else if ((i & ~0xFFL) != 0) { |
480 | p_int_flat->size = 2; |
481 | *(uint16_t *)p_int_flat->data = (uint16_t)i; |
482 | } |
483 | else { |
484 | p_int_flat->size = 1; |
485 | *(uint8_t *)p_int_flat->data = (uint8_t)i; |
486 | } |
487 | |
488 | return (uint32_t)(sizeof(integer_flat) + p_int_flat->size); |
489 | } |
490 | |
491 | |
492 | //========================================================== |
493 | // as_bin particle functions specific to INTEGER. |
494 | // |
495 | |
496 | int64_t |
497 | as_bin_particle_integer_value(const as_bin *b) |
498 | { |
499 | // Caller must ensure this is called only for INTEGER particles. |
500 | return (int64_t)b->particle; |
501 | } |
502 | |
503 | void |
504 | as_bin_particle_integer_set(as_bin *b, int64_t i) |
505 | { |
506 | b->particle = (as_particle *)i; |
507 | } |
508 | |