1/*
2 * particle.c
3 *
4 * Copyright (C) 2008-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.h"
25
26#include <stdbool.h>
27#include <stddef.h>
28#include <stdint.h>
29#include <string.h>
30
31#include "aerospike/as_buffer.h"
32#include "aerospike/as_msgpack.h"
33#include "aerospike/as_serializer.h"
34#include "aerospike/as_val.h"
35#include "citrusleaf/alloc.h"
36#include "citrusleaf/cf_byte_order.h"
37
38#include "dynbuf.h"
39#include "fault.h"
40
41#include "base/datamodel.h"
42#include "base/proto.h"
43#include "fabric/partition.h"
44#include "storage/storage.h"
45
46
47//==========================================================
48// Typedefs & constants.
49//
50
51extern const as_particle_vtable integer_vtable;
52extern const as_particle_vtable float_vtable;
53extern const as_particle_vtable string_vtable;
54extern const as_particle_vtable blob_vtable;
55extern const as_particle_vtable map_vtable;
56extern const as_particle_vtable list_vtable;
57extern const as_particle_vtable geojson_vtable;
58
59// Array of particle vtable pointers.
60const as_particle_vtable *particle_vtable[] = {
61 [AS_PARTICLE_TYPE_NULL] = NULL,
62 [AS_PARTICLE_TYPE_INTEGER] = &integer_vtable,
63 [AS_PARTICLE_TYPE_FLOAT] = &float_vtable,
64 [AS_PARTICLE_TYPE_STRING] = &string_vtable,
65 [AS_PARTICLE_TYPE_BLOB] = &blob_vtable,
66 [AS_PARTICLE_TYPE_JAVA_BLOB] = &blob_vtable,
67 [AS_PARTICLE_TYPE_CSHARP_BLOB] = &blob_vtable,
68 [AS_PARTICLE_TYPE_PYTHON_BLOB] = &blob_vtable,
69 [AS_PARTICLE_TYPE_RUBY_BLOB] = &blob_vtable,
70 [AS_PARTICLE_TYPE_PHP_BLOB] = &blob_vtable,
71 [AS_PARTICLE_TYPE_ERLANG_BLOB] = &blob_vtable,
72 [AS_PARTICLE_TYPE_MAP] = &map_vtable,
73 [AS_PARTICLE_TYPE_LIST] = &list_vtable,
74 [AS_PARTICLE_TYPE_GEOJSON] = &geojson_vtable
75};
76
77
78//==========================================================
79// Local utilities.
80//
81
82// Particle type check.
83static inline as_particle_type
84safe_particle_type(uint8_t type)
85{
86 switch ((as_particle_type)type) {
87 case AS_PARTICLE_TYPE_INTEGER:
88 case AS_PARTICLE_TYPE_FLOAT:
89 case AS_PARTICLE_TYPE_STRING:
90 case AS_PARTICLE_TYPE_BLOB:
91 case AS_PARTICLE_TYPE_JAVA_BLOB:
92 case AS_PARTICLE_TYPE_CSHARP_BLOB:
93 case AS_PARTICLE_TYPE_PYTHON_BLOB:
94 case AS_PARTICLE_TYPE_RUBY_BLOB:
95 case AS_PARTICLE_TYPE_PHP_BLOB:
96 case AS_PARTICLE_TYPE_ERLANG_BLOB:
97 case AS_PARTICLE_TYPE_MAP:
98 case AS_PARTICLE_TYPE_LIST:
99 case AS_PARTICLE_TYPE_GEOJSON:
100 return (as_particle_type)type;
101 // Note - AS_PARTICLE_TYPE_NULL is considered bad here.
102 default:
103 cf_warning(AS_PARTICLE, "encountered bad particle type %u", type);
104 return AS_PARTICLE_TYPE_BAD;
105 }
106}
107
108
109//==========================================================
110// Particle "class static" functions.
111//
112
113as_particle_type
114as_particle_type_from_asval(const as_val *val)
115{
116 as_val_t vtype = as_val_type(val);
117
118 switch (vtype) {
119 case AS_UNDEF: // if val was null - handle quietly
120 case AS_NIL:
121 return AS_PARTICLE_TYPE_NULL;
122 case AS_BOOLEAN:
123 case AS_INTEGER:
124 return AS_PARTICLE_TYPE_INTEGER;
125 case AS_DOUBLE:
126 return AS_PARTICLE_TYPE_FLOAT;
127 case AS_STRING:
128 return AS_PARTICLE_TYPE_STRING;
129 case AS_BYTES:
130 return AS_PARTICLE_TYPE_BLOB;
131 case AS_GEOJSON:
132 return AS_PARTICLE_TYPE_GEOJSON;
133 case AS_LIST:
134 return AS_PARTICLE_TYPE_LIST;
135 case AS_MAP:
136 return AS_PARTICLE_TYPE_MAP;
137 case AS_REC:
138 case AS_PAIR:
139 default:
140 cf_warning(AS_PARTICLE, "no particle type for as_val_t %d", vtype);
141 return AS_PARTICLE_TYPE_NULL;
142 }
143}
144
145as_particle_type
146as_particle_type_from_msgpack(const uint8_t *packed, uint32_t packed_size)
147{
148 as_val_t vtype = as_unpack_buf_peek_type(packed, packed_size);
149
150 switch (vtype) {
151 case AS_NIL:
152 return AS_PARTICLE_TYPE_NULL;
153 case AS_BOOLEAN:
154 case AS_INTEGER:
155 return AS_PARTICLE_TYPE_INTEGER;
156 case AS_DOUBLE:
157 return AS_PARTICLE_TYPE_FLOAT;
158 case AS_STRING:
159 return AS_PARTICLE_TYPE_STRING;
160 case AS_BYTES:
161 return AS_PARTICLE_TYPE_BLOB;
162 case AS_GEOJSON:
163 return AS_PARTICLE_TYPE_GEOJSON;
164 case AS_LIST:
165 return AS_PARTICLE_TYPE_LIST;
166 case AS_MAP:
167 return AS_PARTICLE_TYPE_MAP;
168 case AS_UNDEF:
169 case AS_REC:
170 case AS_PAIR:
171 default:
172 cf_warning(AS_PARTICLE, "encountered bad as_val_t %d", vtype);
173 return AS_PARTICLE_TYPE_BAD;
174 }
175}
176
177uint32_t
178as_particle_size_from_asval(const as_val *val)
179{
180 as_particle_type type = as_particle_type_from_asval(val);
181
182 if (type == AS_PARTICLE_TYPE_NULL) {
183 // Currently UDF code just skips unmanageable as_val types.
184 return 0;
185 }
186
187 return particle_vtable[type]->size_from_asval_fn(val);
188}
189
190uint32_t
191as_particle_asval_client_value_size(const as_val *val)
192{
193 as_particle_type type = as_particle_type_from_asval(val);
194
195 if (type == AS_PARTICLE_TYPE_NULL) {
196 // Currently UDF code just sends bin-op with NULL particle to client.
197 return 0;
198 }
199
200 return particle_vtable[type]->asval_wire_size_fn(val);
201}
202
203uint32_t
204as_particle_asval_to_client(const as_val *val, as_msg_op *op)
205{
206 as_particle_type type = as_particle_type_from_asval(val);
207
208 op->particle_type = type;
209
210 if (type == AS_PARTICLE_TYPE_NULL) {
211 // Currently UDF code just sends bin-op with NULL particle to client.
212 return 0;
213 }
214
215 uint8_t *value = (uint8_t *)op + sizeof(as_msg_op) + op->name_sz;
216 uint32_t added_size = particle_vtable[type]->asval_to_wire_fn(val, value);
217
218 op->op_sz += added_size;
219
220 return added_size;
221}
222
223const uint8_t *
224as_particle_skip_flat(const uint8_t *flat, const uint8_t *end)
225{
226 if (flat >= end) {
227 cf_warning(AS_PARTICLE, "incomplete flat particle");
228 return NULL;
229 }
230
231 as_particle_type type = safe_particle_type(*flat);
232
233 if (type == AS_PARTICLE_TYPE_BAD) {
234 return NULL;
235 }
236
237 // Skip the flat particle.
238 return particle_vtable[type]->skip_flat_fn(flat, end);
239}
240
241
242//==========================================================
243// as_bin particle functions.
244//
245
246//------------------------------------------------
247// Destructor, etc.
248//
249
250void
251as_bin_particle_destroy(as_bin *b, bool free_particle)
252{
253 if (free_particle && as_bin_is_external_particle(b) && b->particle) {
254 particle_vtable[as_bin_get_particle_type(b)]->destructor_fn(b->particle);
255 }
256
257 b->particle = NULL;
258}
259
260uint32_t
261as_bin_particle_size(as_bin *b)
262{
263 if (! as_bin_inuse(b)) {
264 // Single-bin will get here.
265 // TODO - clean up code paths so this doesn't happen?
266 return 0;
267 }
268
269 return particle_vtable[as_bin_get_particle_type(b)]->size_fn(b->particle);
270}
271
272//------------------------------------------------
273// Handle "wire" format.
274//
275
276int
277as_bin_particle_alloc_modify_from_client(as_bin *b, const as_msg_op *op)
278{
279 // This method does not destroy the existing particle, if any. We assume
280 // there is a copy of this bin (and particle reference) elsewhere, and that
281 // the copy will be responsible for the existing particle. Therefore it's
282 // important on failure to leave the existing particle intact.
283
284 uint8_t operation = op->op;
285 as_particle_type op_type = safe_particle_type(op->particle_type);
286
287 if (op_type == AS_PARTICLE_TYPE_BAD) {
288 return -AS_ERR_PARAMETER;
289 }
290
291 uint32_t op_value_size = as_msg_op_get_value_sz(op);
292 uint8_t *op_value = as_msg_op_get_value_p((as_msg_op *)op);
293
294 // Currently all operations become creates if there's no existing particle.
295 if (! as_bin_inuse(b)) {
296 int32_t mem_size = particle_vtable[op_type]->size_from_wire_fn(op_value, op_value_size);
297
298 if (mem_size < 0) {
299 return (int)mem_size;
300 }
301
302 as_particle *old_particle = b->particle;
303
304 if (mem_size != 0) {
305 b->particle = cf_malloc_ns((size_t)mem_size);
306 }
307
308 // Load the new particle into the bin.
309 int result = particle_vtable[op_type]->from_wire_fn(op_type, op_value, op_value_size, &b->particle);
310
311 // Set the bin's iparticle metadata.
312 if (result == 0) {
313 as_bin_state_set_from_type(b, op_type);
314 }
315 else {
316 if (mem_size != 0) {
317 cf_free(b->particle);
318 }
319
320 b->particle = old_particle;
321 }
322
323 return result;
324 }
325
326 // There is an existing particle, which we will modify.
327 uint8_t existing_type = as_bin_get_particle_type(b);
328 int32_t new_mem_size = 0;
329 as_particle *new_particle = NULL;
330
331 as_particle *old_particle = b->particle;
332 int result = 0;
333
334 switch (operation) {
335 case AS_MSG_OP_INCR:
336 result = particle_vtable[existing_type]->incr_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
337 break;
338 case AS_MSG_OP_APPEND:
339 new_mem_size = particle_vtable[existing_type]->concat_size_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
340 if (new_mem_size < 0) {
341 return new_mem_size;
342 }
343 new_particle = cf_malloc_ns((size_t)new_mem_size);
344 memcpy(new_particle, b->particle, particle_vtable[existing_type]->size_fn(b->particle));
345 b->particle = new_particle;
346 result = particle_vtable[existing_type]->append_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
347 break;
348 case AS_MSG_OP_PREPEND:
349 new_mem_size = particle_vtable[existing_type]->concat_size_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
350 if (new_mem_size < 0) {
351 return new_mem_size;
352 }
353 new_particle = cf_malloc_ns((size_t)new_mem_size);
354 memcpy(new_particle, b->particle, particle_vtable[existing_type]->size_fn(b->particle));
355 b->particle = new_particle;
356 result = particle_vtable[existing_type]->prepend_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
357 break;
358 default:
359 // TODO - just crash?
360 return -AS_ERR_UNKNOWN;
361 }
362
363 if (result < 0) {
364 if (new_mem_size != 0) {
365 cf_free(b->particle);
366 }
367
368 b->particle = old_particle;
369 }
370
371 return result;
372}
373
374int
375as_bin_particle_stack_modify_from_client(as_bin *b, cf_ll_buf *particles_llb, const as_msg_op *op)
376{
377 uint8_t operation = op->op;
378 as_particle_type op_type = safe_particle_type(op->particle_type);
379
380 if (op_type == AS_PARTICLE_TYPE_BAD) {
381 return -AS_ERR_PARAMETER;
382 }
383
384 uint32_t op_value_size = as_msg_op_get_value_sz(op);
385 uint8_t *op_value = as_msg_op_get_value_p((as_msg_op *)op);
386
387 // Currently all operations become creates if there's no existing particle.
388 if (! as_bin_inuse(b)) {
389 int32_t mem_size = particle_vtable[op_type]->size_from_wire_fn(op_value, op_value_size);
390
391 if (mem_size < 0) {
392 return (int)mem_size;
393 }
394
395 as_particle *old_particle = b->particle;
396
397 // Instead of allocating, we use the stack buffer provided. (Note that
398 // embedded types like integer will overwrite this with the value.)
399 cf_ll_buf_reserve(particles_llb, (size_t)mem_size, (uint8_t **)&b->particle);
400
401 // Load the new particle into the bin.
402 int result = particle_vtable[op_type]->from_wire_fn(op_type, op_value, op_value_size, &b->particle);
403
404 // Set the bin's iparticle metadata.
405 if (result == 0) {
406 as_bin_state_set_from_type(b, op_type);
407 }
408 else {
409 b->particle = old_particle;
410 }
411
412 return result;
413 }
414
415 // There is an existing particle, which we will modify.
416 uint8_t existing_type = as_bin_get_particle_type(b);
417 int32_t new_mem_size = 0;
418
419 as_particle *old_particle = b->particle;
420 int result = 0;
421
422 switch (operation) {
423 case AS_MSG_OP_INCR:
424 result = particle_vtable[existing_type]->incr_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
425 break;
426 case AS_MSG_OP_APPEND:
427 new_mem_size = particle_vtable[existing_type]->concat_size_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
428 if (new_mem_size < 0) {
429 return (int)new_mem_size;
430 }
431 cf_ll_buf_reserve(particles_llb, (size_t)new_mem_size, (uint8_t **)&b->particle);
432 memcpy(b->particle, old_particle, particle_vtable[existing_type]->size_fn(old_particle));
433 result = particle_vtable[existing_type]->append_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
434 break;
435 case AS_MSG_OP_PREPEND:
436 new_mem_size = particle_vtable[existing_type]->concat_size_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
437 if (new_mem_size < 0) {
438 return (int)new_mem_size;
439 }
440 cf_ll_buf_reserve(particles_llb, (size_t)new_mem_size, (uint8_t **)&b->particle);
441 memcpy(b->particle, old_particle, particle_vtable[existing_type]->size_fn(old_particle));
442 result = particle_vtable[existing_type]->prepend_from_wire_fn(op_type, op_value, op_value_size, &b->particle);
443 break;
444 default:
445 // TODO - just crash?
446 return -AS_ERR_UNKNOWN;
447 }
448
449 if (result < 0) {
450 b->particle = old_particle;
451 }
452
453 return result;
454}
455
456int
457as_bin_particle_alloc_from_client(as_bin *b, const as_msg_op *op)
458{
459 // This method does not destroy the existing particle, if any. We assume
460 // there is a copy of this bin (and particle reference) elsewhere, and that
461 // the copy will be responsible for the existing particle. Therefore it's
462 // important on failure to leave the existing particle intact.
463
464 as_particle_type type = safe_particle_type(op->particle_type);
465
466 if (type == AS_PARTICLE_TYPE_BAD) {
467 return -AS_ERR_PARAMETER;
468 }
469
470 uint32_t value_size = as_msg_op_get_value_sz(op);
471 uint8_t *value = as_msg_op_get_value_p((as_msg_op *)op);
472 int32_t mem_size = particle_vtable[type]->size_from_wire_fn(value, value_size);
473
474 if (mem_size < 0) {
475 return (int)mem_size;
476 }
477
478 as_particle *old_particle = b->particle;
479
480 if (mem_size != 0) {
481 b->particle = cf_malloc_ns((size_t)mem_size);
482 }
483
484 // Load the new particle into the bin.
485 int result = particle_vtable[type]->from_wire_fn(type, value, value_size, &b->particle);
486
487 // Set the bin's iparticle metadata.
488 if (result == 0) {
489 as_bin_state_set_from_type(b, type);
490 }
491 else {
492 if (mem_size != 0) {
493 cf_free(b->particle);
494 }
495
496 b->particle = old_particle;
497 }
498
499 return result;
500}
501
502int
503as_bin_particle_stack_from_client(as_bin *b, cf_ll_buf *particles_llb, const as_msg_op *op)
504{
505 // We assume that if we're using stack particles, the old particle is either
506 // nonexistent or also a stack particle - either way, don't destroy.
507
508 as_particle_type type = safe_particle_type(op->particle_type);
509
510 if (type == AS_PARTICLE_TYPE_BAD) {
511 return -AS_ERR_PARAMETER;
512 }
513
514 uint32_t value_size = as_msg_op_get_value_sz(op);
515 uint8_t *value = as_msg_op_get_value_p((as_msg_op *)op);
516 int32_t mem_size = particle_vtable[type]->size_from_wire_fn(value, value_size);
517
518 if (mem_size < 0) {
519 return (int)mem_size;
520 }
521
522 as_particle *old_particle = b->particle;
523
524 // Instead of allocating, we use the stack buffer provided. (Note that
525 // embedded types like integer will overwrite this with the value.)
526 cf_ll_buf_reserve(particles_llb, (size_t)mem_size, (uint8_t **)&b->particle);
527
528 // Load the new particle into the bin.
529 int result = particle_vtable[type]->from_wire_fn(type, value, value_size, &b->particle);
530
531 // Set the bin's iparticle metadata.
532 if (result == 0) {
533 as_bin_state_set_from_type(b, type);
534 }
535 else {
536 b->particle = old_particle;
537 }
538
539 return result;
540}
541
542// TODO - old pickle - remove in "six months".
543int
544as_bin_particle_alloc_from_pickled(as_bin *b, const uint8_t **p_pickled, const uint8_t *end)
545{
546 // This method does not destroy the existing particle, if any. We assume
547 // there is a copy of this bin (and particle reference) elsewhere, and that
548 // the copy will be responsible for the existing particle. Therefore it's
549 // important on failure to leave the existing particle intact.
550
551 const uint8_t *pickled = (const uint8_t *)*p_pickled;
552
553 if (pickled + 1 + 4 > end) {
554 cf_warning(AS_PARTICLE, "incomplete pickled particle");
555 return -AS_ERR_UNKNOWN;
556 }
557
558 as_particle_type type = safe_particle_type(*pickled++);
559
560 if (type == AS_PARTICLE_TYPE_BAD) {
561 return -AS_ERR_UNKNOWN;
562 }
563
564 const uint32_t *p32 = (const uint32_t *)pickled;
565 uint32_t value_size = cf_swap_from_be32(*p32++);
566 const uint8_t *value = (const uint8_t *)p32;
567
568 *p_pickled = value + value_size;
569
570 // TODO - does this serve as a value_size sanity check?
571 if (*p_pickled > end) {
572 cf_warning(AS_PARTICLE, "incomplete pickled particle");
573 return -AS_ERR_UNKNOWN;
574 }
575
576 int32_t mem_size = particle_vtable[type]->size_from_wire_fn(value, value_size);
577
578 if (mem_size < 0) {
579 return (int)mem_size;
580 }
581
582 as_particle *old_particle = b->particle;
583
584 if (mem_size != 0) {
585 b->particle = cf_malloc_ns((size_t)mem_size);
586 }
587
588 // Load the new particle into the bin.
589 int result = particle_vtable[type]->from_wire_fn(type, value, value_size, &b->particle);
590
591 if (result < 0) {
592 if (mem_size != 0) {
593 cf_free(b->particle);
594 }
595
596 b->particle = old_particle;
597 return result;
598 }
599
600 // Set the bin's iparticle metadata.
601 as_bin_state_set_from_type(b, type);
602
603 return 0;
604}
605
606// TODO - old pickle - remove in "six months".
607int
608as_bin_particle_stack_from_pickled(as_bin *b, cf_ll_buf *particles_llb, const uint8_t **p_pickled, const uint8_t *end)
609{
610 // We assume that if we're using stack particles, the old particle is either
611 // nonexistent or also a stack particle - either way, don't destroy.
612
613 const uint8_t *pickled = (const uint8_t *)*p_pickled;
614
615 if (pickled + 1 + 4 > end) {
616 cf_warning(AS_PARTICLE, "incomplete pickled particle");
617 return -AS_ERR_UNKNOWN;
618 }
619
620 as_particle_type type = safe_particle_type(*pickled++);
621
622 if (type == AS_PARTICLE_TYPE_BAD) {
623 return -AS_ERR_UNKNOWN;
624 }
625
626 const uint32_t *p32 = (const uint32_t *)pickled;
627 uint32_t value_size = cf_swap_from_be32(*p32++);
628 const uint8_t *value = (const uint8_t *)p32;
629
630 *p_pickled = value + value_size;
631
632 // TODO - does this serve as a value_size sanity check?
633 if (*p_pickled > end) {
634 cf_warning(AS_PARTICLE, "incomplete pickled particle");
635 return -AS_ERR_UNKNOWN;
636 }
637
638 int32_t mem_size = particle_vtable[type]->size_from_wire_fn(value, value_size);
639
640 if (mem_size < 0) {
641 // Leave existing particle intact.
642 return (int)mem_size;
643 }
644
645 as_particle *old_particle = b->particle;
646
647 // Instead of allocating, we use the stack buffer provided. (Note that
648 // embedded types like integer will overwrite this with the value.)
649 cf_ll_buf_reserve(particles_llb, (size_t)mem_size, (uint8_t **)&b->particle);
650
651 // Load the new particle into the bin.
652 int result = particle_vtable[type]->from_wire_fn(type, value, value_size, &b->particle);
653
654 if (result < 0) {
655 b->particle = old_particle;
656 return result;
657 }
658
659 // Set the bin's iparticle metadata.
660 as_bin_state_set_from_type(b, type);
661
662 return 0;
663}
664
665uint32_t
666as_bin_particle_client_value_size(const as_bin *b)
667{
668 if (! as_bin_inuse(b)) {
669 // UDF result bin (bin name "SUCCESS" or "FAILURE") will get here.
670 return 0;
671 }
672
673 uint8_t type = as_bin_get_particle_type(b);
674
675 return particle_vtable[type]->wire_size_fn(b->particle);
676}
677
678uint32_t
679as_bin_particle_to_client(const as_bin *b, as_msg_op *op)
680{
681 if (! (b && as_bin_inuse(b))) {
682 // UDF result bin (bin name "SUCCESS" or "FAILURE") will get here.
683 // Ordered ops that find no bin will get here.
684 op->particle_type = AS_PARTICLE_TYPE_NULL;
685 return 0;
686 }
687
688 uint8_t type = as_bin_get_particle_type(b);
689
690 op->particle_type = type;
691
692 uint8_t *value = (uint8_t *)op + sizeof(as_msg_op) + op->name_sz;
693 uint32_t added_size = particle_vtable[type]->to_wire_fn(b->particle, value);
694
695 op->op_sz += added_size;
696
697 return added_size;
698}
699
700// TODO - old pickle - remove in "six months".
701uint32_t
702as_bin_particle_pickled_size(const as_bin *b)
703{
704 uint8_t type = as_bin_get_particle_type(b);
705
706 // Always a type byte and a 32-bit size.
707 return 1 + 4 + particle_vtable[type]->wire_size_fn(b->particle);
708}
709
710// TODO - old pickle - remove in "six months".
711uint32_t
712as_bin_particle_to_pickled(const as_bin *b, uint8_t *pickled)
713{
714 uint8_t type = as_bin_get_particle_type(b);
715
716 *pickled++ = type;
717
718 uint32_t *p_size = (uint32_t *)pickled;
719 uint8_t *value = (uint8_t *)(p_size + 1);
720 uint32_t size = particle_vtable[type]->to_wire_fn(b->particle, value);
721
722 *p_size = cf_swap_to_be32(size);
723
724 return 1 + 4 + size;
725}
726
727//------------------------------------------------
728// Handle as_val translation.
729//
730
731int
732as_bin_particle_replace_from_asval(as_bin *b, const as_val *val)
733{
734 uint8_t old_type = as_bin_get_particle_type(b);
735 as_particle_type new_type = as_particle_type_from_asval(val);
736
737 if (new_type == AS_PARTICLE_TYPE_NULL) {
738 // Currently UDF code just skips unmanageable as_val types.
739 return 0;
740 }
741
742 uint32_t new_mem_size = particle_vtable[new_type]->size_from_asval_fn(val);
743 // TODO - could this ever fail?
744
745 as_particle *old_particle = b->particle;
746
747 if (new_mem_size != 0) {
748 b->particle = cf_malloc_ns(new_mem_size);
749 }
750
751 // Load the new particle into the bin.
752 particle_vtable[new_type]->from_asval_fn(val, &b->particle);
753 // TODO - could this ever fail?
754
755 if (as_bin_inuse(b)) {
756 // Destroy the old particle.
757 particle_vtable[old_type]->destructor_fn(old_particle);
758 }
759
760 // Set the bin's iparticle metadata.
761 as_bin_state_set_from_type(b, new_type);
762
763 return 0;
764}
765
766void
767as_bin_particle_stack_from_asval(as_bin *b, uint8_t* stack, const as_val *val)
768{
769 // We assume that if we're using stack particles, the old particle is either
770 // nonexistent or also a stack particle - either way, don't destroy.
771
772 as_particle_type type = as_particle_type_from_asval(val);
773
774 if (type == AS_PARTICLE_TYPE_NULL) {
775 // Currently UDF code just skips unmanageable as_val types.
776 return;
777 }
778
779 // Instead of allocating, we use the stack buffer provided. (Note that
780 // embedded types like integer will overwrite this with the value.)
781 b->particle = (as_particle *)stack;
782
783 // Load the new particle into the bin.
784 particle_vtable[type]->from_asval_fn(val, &b->particle);
785 // TODO - could this ever fail?
786
787 // Set the bin's iparticle metadata.
788 as_bin_state_set_from_type(b, type);
789
790 // TODO - we don't bother returning size written, since nothing yet needs
791 // it and it's very expensive for CDTs to do an extra size_from_asval_fn()
792 // call. Perhaps we could have from_asval_fn() return the size if needed?
793}
794
795as_val *
796as_bin_particle_to_asval(const as_bin *b)
797{
798 uint8_t type = as_bin_get_particle_type(b);
799
800 // Caller is responsible for freeing as_val returned here.
801 return particle_vtable[type]->to_asval_fn(b->particle);
802}
803
804//------------------------------------------------
805// Handle msgpack translation.
806//
807
808int
809as_bin_particle_alloc_from_msgpack(as_bin *b, const uint8_t *packed, uint32_t packed_size)
810{
811 // We assume the bin is empty.
812
813 as_particle_type type = as_particle_type_from_msgpack(packed, packed_size);
814
815 if (type == AS_PARTICLE_TYPE_BAD) {
816 return -AS_ERR_UNKNOWN;
817 }
818
819 if (type == AS_PARTICLE_TYPE_NULL) {
820 return AS_OK;
821 }
822
823 uint32_t mem_size = particle_vtable[type]->size_from_msgpack_fn(packed, packed_size);
824
825 if (mem_size != 0) {
826 b->particle = cf_malloc(mem_size); // response, so not cf_malloc_ns()
827 }
828
829 particle_vtable[type]->from_msgpack_fn(packed, packed_size, &b->particle);
830
831 // Set the bin's iparticle metadata.
832 as_bin_state_set_from_type(b, type);
833
834 return AS_OK;
835}
836
837//------------------------------------------------
838// Handle on-device "flat" format.
839//
840
841const uint8_t *
842as_bin_particle_cast_from_flat(as_bin *b, const uint8_t *flat, const uint8_t *end)
843{
844 cf_assert(! as_bin_inuse(b), AS_PARTICLE, "cast from flat into used bin");
845
846 if (flat >= end) {
847 cf_warning(AS_PARTICLE, "incomplete flat particle");
848 return NULL;
849 }
850
851 as_particle_type type = safe_particle_type(*flat);
852
853 if (type == AS_PARTICLE_TYPE_BAD) {
854 return NULL;
855 }
856
857 // Cast the new particle into the bin.
858 flat = particle_vtable[type]->cast_from_flat_fn(flat, end, &b->particle);
859
860 // Set the bin's iparticle metadata.
861 if (flat) {
862 as_bin_state_set_from_type(b, type);
863 }
864 // else - bin remains empty.
865
866 return flat;
867}
868
869// TODO - re-do to leave original intact on failure.
870const uint8_t *
871as_bin_particle_replace_from_flat(as_bin *b, const uint8_t *flat, const uint8_t *end)
872{
873 uint8_t old_type = as_bin_get_particle_type(b);
874 as_particle_type new_type = safe_particle_type(*flat);
875
876 if (new_type == AS_PARTICLE_TYPE_BAD) {
877 return NULL;
878 }
879
880 // Just destroy the old particle, if any - we're replacing it.
881 if (as_bin_inuse(b)) {
882 particle_vtable[old_type]->destructor_fn(b->particle);
883 }
884
885 // Load the new particle into the bin.
886 flat = particle_vtable[new_type]->from_flat_fn(flat, end, &b->particle);
887
888 // Set the bin's iparticle metadata.
889 if (flat) {
890 as_bin_state_set_from_type(b, new_type);
891 }
892 else {
893 as_bin_set_empty(b);
894 }
895
896 return flat;
897}
898
899uint32_t
900as_bin_particle_flat_size(as_bin *b)
901{
902 cf_assert(as_bin_inuse(b), AS_PARTICLE, "flat sizing unused bin");
903
904 uint8_t type = as_bin_get_particle_type(b);
905
906 return particle_vtable[type]->flat_size_fn(b->particle);
907}
908
909uint32_t
910as_bin_particle_to_flat(const as_bin *b, uint8_t *flat)
911{
912 cf_assert(as_bin_inuse(b), AS_PARTICLE, "flattening unused bin");
913
914 uint8_t type = as_bin_get_particle_type(b);
915
916 *flat = type;
917
918 return particle_vtable[type]->to_flat_fn(b->particle, flat);
919}
920
921
922//==========================================================
923// as_bin particle functions specific to blobs.
924//
925
926//------------------------------------------------
927// Handle "wire" format.
928//
929
930int
931as_bin_bits_read_from_client(const as_bin *b, as_msg_op *op, as_bin *result)
932{
933 return as_bin_bits_packed_read(b, op, result);
934}
935
936int
937as_bin_bits_alloc_modify_from_client(as_bin *b, as_msg_op *op)
938{
939 return as_bin_bits_packed_modify(b, op, NULL);
940}
941
942int
943as_bin_bits_stack_modify_from_client(as_bin *b, cf_ll_buf *particles_llb, as_msg_op *op)
944{
945 return as_bin_bits_packed_modify(b, op, particles_llb);
946}
947
948
949//==========================================================
950// as_bin particle functions specific to CDTs.
951//
952
953//------------------------------------------------
954// Handle "wire" format.
955//
956
957int
958as_bin_cdt_read_from_client(const as_bin *b, as_msg_op *op, as_bin *result)
959{
960 return as_bin_cdt_packed_read(b, op, result);
961}
962
963int
964as_bin_cdt_alloc_modify_from_client(as_bin *b, as_msg_op *op, as_bin *result)
965{
966 return as_bin_cdt_packed_modify(b, op, result, NULL);
967}
968
969int
970as_bin_cdt_stack_modify_from_client(as_bin *b, cf_ll_buf *particles_llb, as_msg_op *op, as_bin *result)
971{
972 return as_bin_cdt_packed_modify(b, op, result, particles_llb);
973}
974