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 | |
51 | extern const as_particle_vtable integer_vtable; |
52 | extern const as_particle_vtable float_vtable; |
53 | extern const as_particle_vtable string_vtable; |
54 | extern const as_particle_vtable blob_vtable; |
55 | extern const as_particle_vtable map_vtable; |
56 | extern const as_particle_vtable list_vtable; |
57 | extern const as_particle_vtable geojson_vtable; |
58 | |
59 | // Array of particle vtable pointers. |
60 | const 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. |
83 | static inline as_particle_type |
84 | safe_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 | |
113 | as_particle_type |
114 | as_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 | |
145 | as_particle_type |
146 | as_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 | |
177 | uint32_t |
178 | as_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 | |
190 | uint32_t |
191 | as_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 | |
203 | uint32_t |
204 | as_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 | |
223 | const uint8_t * |
224 | as_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 | |
250 | void |
251 | as_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 | |
260 | uint32_t |
261 | as_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 | |
276 | int |
277 | as_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 | |
374 | int |
375 | as_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 | |
456 | int |
457 | as_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 | |
502 | int |
503 | as_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". |
543 | int |
544 | as_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". |
607 | int |
608 | as_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 | |
665 | uint32_t |
666 | as_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 | |
678 | uint32_t |
679 | as_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". |
701 | uint32_t |
702 | as_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". |
711 | uint32_t |
712 | as_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 | |
731 | int |
732 | as_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 | |
766 | void |
767 | as_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 | |
795 | as_val * |
796 | as_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 | |
808 | int |
809 | as_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 | |
841 | const uint8_t * |
842 | as_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. |
870 | const uint8_t * |
871 | as_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 | |
899 | uint32_t |
900 | as_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 | |
909 | uint32_t |
910 | as_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 | |
930 | int |
931 | as_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 | |
936 | int |
937 | as_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 | |
942 | int |
943 | as_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 | |
957 | int |
958 | as_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 | |
963 | int |
964 | as_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 | |
969 | int |
970 | as_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 | |