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.
46const uint8_t *integer_skip_flat(const uint8_t *flat, const uint8_t *end);
47const uint8_t *integer_from_flat(const uint8_t *flat, const uint8_t *end, as_particle **pp);
48uint32_t integer_flat_size(const as_particle *p);
49uint32_t integer_to_flat(const as_particle *p, uint8_t *flat);
50
51
52//==========================================================
53// INTEGER particle interface - vtable.
54//
55
56const 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
91typedef 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
96typedef 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
111void
112integer_destruct(as_particle *p)
113{
114 // Nothing to do - integer values live in the as_bin.
115}
116
117uint32_t
118integer_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
128int32_t
129integer_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
135int
136integer_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
142int
143integer_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
149int
150integer_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
190int32_t
191integer_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
197int
198integer_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
225int
226integer_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
254uint32_t
255integer_wire_size(const as_particle *p)
256{
257 return (uint32_t)sizeof(uint64_t);
258}
259
260uint32_t
261integer_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
272uint32_t
273integer_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
279void
280integer_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
303as_val *
304integer_to_asval(const as_particle *p)
305{
306 return (as_val *)as_integer_new((uint64_t)p);
307}
308
309uint32_t
310integer_asval_wire_size(const as_val *val)
311{
312 return (uint32_t)sizeof(uint64_t);
313}
314
315uint32_t
316integer_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
345uint32_t
346integer_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
352void
353integer_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
384const uint8_t *
385integer_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
397const uint8_t *
398integer_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
442uint32_t
443integer_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
462uint32_t
463integer_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
496int64_t
497as_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
503void
504as_bin_particle_integer_set(as_bin *b, int64_t i)
505{
506 b->particle = (as_particle *)i;
507}
508