1 | /* |
2 | * predexp.c |
3 | * |
4 | * Copyright (C) 2016-2017 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 | #include "base/predexp.h" |
24 | |
25 | #include <inttypes.h> |
26 | #include <regex.h> |
27 | |
28 | #include <aerospike/as_arraylist.h> |
29 | #include <aerospike/as_arraylist_iterator.h> |
30 | #include <aerospike/as_hashmap_iterator.h> |
31 | #include <aerospike/as_map.h> |
32 | |
33 | #include "citrusleaf/alloc.h" |
34 | #include "citrusleaf/cf_byte_order.h" |
35 | #include "citrusleaf/cf_clock.h" |
36 | |
37 | #include "fault.h" |
38 | |
39 | #include "base/particle.h" |
40 | #include "geospatial/geospatial.h" |
41 | #include "storage/storage.h" |
42 | |
43 | |
44 | typedef struct wrapped_as_bin_s { |
45 | as_bin bin; |
46 | bool must_free; |
47 | } wrapped_as_bin_t; |
48 | |
49 | // Called to destroy a predexp when no longer needed. |
50 | typedef void (*predexp_eval_dtor_fn)(predexp_eval_t* bp); |
51 | |
52 | typedef predexp_retval_t (*predexp_eval_eval_fn)(predexp_eval_t* bp, |
53 | predexp_args_t* argsp, |
54 | wrapped_as_bin_t* wbinp); |
55 | |
56 | // Convenience macro, converts boolean to retval. |
57 | #define PREDEXP_RETVAL(bb) ((bb) ? PREDEXP_TRUE : PREDEXP_FALSE) |
58 | |
59 | #define PREDEXP_VALUE_NODE 0x01 // represents a value |
60 | #define PREDEXP_IMMEDIATE_NODE 0x02 // constant per-query value |
61 | |
62 | struct predexp_eval_base_s { |
63 | predexp_eval_t* next; |
64 | predexp_eval_dtor_fn dtor_fn; |
65 | predexp_eval_eval_fn eval_fn; |
66 | uint8_t flags; |
67 | uint8_t type; |
68 | }; |
69 | |
70 | struct predexp_var_s { |
71 | char vname[AS_BIN_NAME_MAX_SZ]; |
72 | as_bin bin; |
73 | as_predexp_var_t* next; |
74 | }; |
75 | |
76 | // This function can set bin values for all bloblike types (strings) |
77 | |
78 | extern const as_particle_vtable *particle_vtable[]; |
79 | |
80 | #if 0 |
81 | static void predexp_eval_base_dtor(predexp_eval_t* bp) |
82 | { |
83 | cf_free(bp); |
84 | } |
85 | #endif |
86 | |
87 | static void predexp_eval_base_init(predexp_eval_t* bp, |
88 | predexp_eval_dtor_fn dtor_fn, |
89 | predexp_eval_eval_fn eval_fn, |
90 | uint8_t flags, |
91 | uint8_t type) |
92 | { |
93 | bp->next = NULL; |
94 | bp->dtor_fn = dtor_fn; |
95 | bp->eval_fn = eval_fn; |
96 | bp->flags = flags; |
97 | bp->type = type; |
98 | } |
99 | |
100 | // ---------------------------------------------------------------- |
101 | // Helper Functions |
102 | // ---------------------------------------------------------------- |
103 | |
104 | static void |
105 | destroy_list(predexp_eval_t* bp) |
106 | { |
107 | while (bp != NULL) { |
108 | predexp_eval_t* next = bp->next; |
109 | (*bp->dtor_fn)(bp); |
110 | bp = next; |
111 | } |
112 | } |
113 | |
114 | // ---------------------------------------------------------------- |
115 | // Tag Definitions |
116 | // ---------------------------------------------------------------- |
117 | |
118 | // TODO - put in common to share with clients? |
119 | // |
120 | #define AS_PREDEXP_AND 1 |
121 | #define AS_PREDEXP_OR 2 |
122 | #define AS_PREDEXP_NOT 3 |
123 | |
124 | #define AS_PREDEXP_INTEGER_VALUE 10 |
125 | #define AS_PREDEXP_STRING_VALUE 11 |
126 | #define AS_PREDEXP_GEOJSON_VALUE 12 |
127 | |
128 | #define AS_PREDEXP_INTEGER_BIN 100 |
129 | #define AS_PREDEXP_STRING_BIN 101 |
130 | #define AS_PREDEXP_GEOJSON_BIN 102 |
131 | #define AS_PREDEXP_LIST_BIN 103 |
132 | #define AS_PREDEXP_MAP_BIN 104 |
133 | |
134 | #define AS_PREDEXP_INTEGER_VAR 120 |
135 | #define AS_PREDEXP_STRING_VAR 121 |
136 | #define AS_PREDEXP_GEOJSON_VAR 122 |
137 | |
138 | #define AS_PREDEXP_REC_DEVICE_SIZE 150 |
139 | #define AS_PREDEXP_REC_LAST_UPDATE 151 |
140 | #define AS_PREDEXP_REC_VOID_TIME 152 |
141 | #define AS_PREDEXP_REC_DIGEST_MODULO 153 |
142 | |
143 | #define AS_PREDEXP_INTEGER_EQUAL 200 |
144 | #define AS_PREDEXP_INTEGER_UNEQUAL 201 |
145 | #define AS_PREDEXP_INTEGER_GREATER 202 |
146 | #define AS_PREDEXP_INTEGER_GREATEREQ 203 |
147 | #define AS_PREDEXP_INTEGER_LESS 204 |
148 | #define AS_PREDEXP_INTEGER_LESSEQ 205 |
149 | |
150 | #define AS_PREDEXP_STRING_EQUAL 210 |
151 | #define AS_PREDEXP_STRING_UNEQUAL 211 |
152 | #define AS_PREDEXP_STRING_REGEX 212 |
153 | |
154 | #define AS_PREDEXP_GEOJSON_WITHIN 220 |
155 | #define AS_PREDEXP_GEOJSON_CONTAINS 221 |
156 | |
157 | #define AS_PREDEXP_LIST_ITERATE_OR 250 |
158 | #define AS_PREDEXP_MAPKEY_ITERATE_OR 251 |
159 | #define AS_PREDEXP_MAPVAL_ITERATE_OR 252 |
160 | #define AS_PREDEXP_LIST_ITERATE_AND 253 |
161 | #define AS_PREDEXP_MAPKEY_ITERATE_AND 254 |
162 | #define AS_PREDEXP_MAPVAL_ITERATE_AND 255 |
163 | |
164 | // ---------------------------------------------------------------- |
165 | // AS_PREDEXP_AND |
166 | // ---------------------------------------------------------------- |
167 | |
168 | typedef struct { |
169 | predexp_eval_t base; |
170 | predexp_eval_t* child; |
171 | } predexp_eval_and_t; |
172 | |
173 | static void |
174 | destroy_and(predexp_eval_t* bp) |
175 | { |
176 | predexp_eval_and_t* dp = (predexp_eval_and_t *) bp; |
177 | destroy_list(dp->child); |
178 | cf_free(dp); |
179 | } |
180 | |
181 | static predexp_retval_t |
182 | eval_and(predexp_eval_t* bp, predexp_args_t* argsp, wrapped_as_bin_t* wbinp) |
183 | { |
184 | predexp_eval_and_t* dp = (predexp_eval_and_t *) bp; |
185 | |
186 | // Start optimistically. |
187 | predexp_retval_t retval = PREDEXP_TRUE; |
188 | |
189 | // Scan the children. |
190 | for (predexp_eval_t* cp = dp->child; cp != NULL; cp = cp->next) { |
191 | |
192 | switch ((*cp->eval_fn)(cp, argsp, NULL)) { |
193 | case PREDEXP_FALSE: |
194 | // Shortcut, skip remaining children. |
195 | return PREDEXP_FALSE; |
196 | case PREDEXP_UNKNOWN: |
197 | // Downgrade our return value, continue scanning children. |
198 | retval = PREDEXP_UNKNOWN; |
199 | break; |
200 | case PREDEXP_TRUE: |
201 | // Continue scanning children. |
202 | break; |
203 | case PREDEXP_VALUE: |
204 | case PREDEXP_NOVALUE: |
205 | // Child can't be value node; shouldn't ever happen. |
206 | cf_crash(AS_PREDEXP, "eval_and child was value node" ); |
207 | } |
208 | } |
209 | |
210 | return retval; |
211 | } |
212 | |
213 | static bool |
214 | build_and(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp) |
215 | { |
216 | if (len != sizeof(uint16_t)) { |
217 | cf_warning(AS_PREDEXP, "predexp_and: unexpected size %d" , len); |
218 | return false; |
219 | } |
220 | uint16_t nterms = cf_swap_from_be16(* (uint16_t *) pp); |
221 | pp += sizeof(uint16_t); |
222 | |
223 | predexp_eval_and_t* dp = |
224 | (predexp_eval_and_t *) cf_malloc(sizeof(predexp_eval_and_t)); |
225 | |
226 | // Start optimistically. |
227 | predexp_eval_base_init((predexp_eval_t *) dp, |
228 | destroy_and, |
229 | eval_and, |
230 | 0, |
231 | AS_PARTICLE_TYPE_NULL); |
232 | dp->child = NULL; |
233 | |
234 | for (uint16_t ndx = 0; ndx < nterms; ++ndx) { |
235 | // If there is not an available child expr cleanup and fail. |
236 | if (! *stackpp) { |
237 | cf_warning(AS_PREDEXP, "predexp_and: missing child %d" , ndx); |
238 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
239 | return false; |
240 | } |
241 | |
242 | // Transfer the expr at the top of the stack to our child list. |
243 | predexp_eval_t* child; |
244 | child = *stackpp; // Child from the top of the stack. |
245 | *stackpp = child->next; // Stack points around the child. |
246 | child->next = dp->child; // Child now points to prior list head. |
247 | dp->child = child; // Child is now the top of our list. |
248 | |
249 | // Make sure the child is not a value node. |
250 | if (dp->child->flags & PREDEXP_VALUE_NODE) { |
251 | cf_warning(AS_PREDEXP, "predexp_and: child %d is value node" , ndx); |
252 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
253 | return false; |
254 | } |
255 | } |
256 | |
257 | // Success, push ourself onto the stack. |
258 | dp->base.next = *stackpp; // We point next at the old top. |
259 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
260 | |
261 | cf_debug(AS_PREDEXP, "%p: predexp_and(%d)" , stackpp, nterms); |
262 | |
263 | return true; |
264 | } |
265 | |
266 | // ---------------------------------------------------------------- |
267 | // AS_PREDEXP_OR |
268 | // ---------------------------------------------------------------- |
269 | |
270 | typedef struct { |
271 | predexp_eval_t base; |
272 | predexp_eval_t* child; |
273 | } predexp_eval_or_t; |
274 | |
275 | static void |
276 | destroy_or(predexp_eval_t* bp) |
277 | { |
278 | predexp_eval_or_t* dp = (predexp_eval_or_t *) bp; |
279 | destroy_list(dp->child); |
280 | cf_free(dp); |
281 | } |
282 | |
283 | static predexp_retval_t |
284 | eval_or(predexp_eval_t* bp, predexp_args_t* argsp, wrapped_as_bin_t* wbinp) |
285 | { |
286 | predexp_eval_or_t* dp = (predexp_eval_or_t *) bp; |
287 | |
288 | // Start pessimistically. |
289 | predexp_retval_t retval = PREDEXP_FALSE; |
290 | |
291 | // Scan the children. |
292 | for (predexp_eval_t* cp = dp->child; cp != NULL; cp = cp->next) { |
293 | switch ((*cp->eval_fn)(cp, argsp, NULL)) { |
294 | case PREDEXP_TRUE: |
295 | // Shortcut, skip remaining children. |
296 | return PREDEXP_TRUE; |
297 | case PREDEXP_UNKNOWN: |
298 | // Upgrade our return value, continue scanning children. |
299 | retval = PREDEXP_UNKNOWN; |
300 | break; |
301 | case PREDEXP_FALSE: |
302 | // Continue scanning children. |
303 | break; |
304 | case PREDEXP_VALUE: |
305 | case PREDEXP_NOVALUE: |
306 | // Child can't be value node; shouldn't ever happen. |
307 | cf_crash(AS_PREDEXP, "eval_or child was value node" ); |
308 | } |
309 | } |
310 | |
311 | return retval; |
312 | } |
313 | |
314 | static bool |
315 | build_or(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp) |
316 | { |
317 | if (len != sizeof(uint16_t)) { |
318 | cf_warning(AS_PREDEXP, "predexp_or: unexpected size %d" , len); |
319 | return false; |
320 | } |
321 | uint16_t nterms = cf_swap_from_be16(* (uint16_t *) pp); |
322 | pp += sizeof(uint16_t); |
323 | |
324 | predexp_eval_or_t* dp = |
325 | (predexp_eval_or_t *) cf_malloc(sizeof(predexp_eval_or_t)); |
326 | |
327 | predexp_eval_base_init((predexp_eval_t *) dp, |
328 | destroy_or, |
329 | eval_or, |
330 | 0, |
331 | AS_PARTICLE_TYPE_NULL); |
332 | dp->child = NULL; |
333 | |
334 | for (uint16_t ndx = 0; ndx < nterms; ++ndx) { |
335 | // If there is not an available child expr cleanup and fail. |
336 | if (! *stackpp) { |
337 | cf_warning(AS_PREDEXP, "predexp_or: missing child %d" , ndx); |
338 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
339 | return false; |
340 | } |
341 | // Transfer the expr at the top of the stack to our child list. |
342 | predexp_eval_t* child; |
343 | child = *stackpp; // Child from the top of the stack. |
344 | *stackpp = child->next; // Stack points around the child. |
345 | child->next = dp->child; // Child now points to prior list head. |
346 | dp->child = child; // Child is now the top of our list. |
347 | |
348 | // Make sure the child is not a value node. |
349 | if (dp->child->flags & PREDEXP_VALUE_NODE) { |
350 | cf_warning(AS_PREDEXP, "predexp_or: child %d is value node" , ndx); |
351 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
352 | return false; |
353 | } |
354 | } |
355 | |
356 | // Success, push ourself onto the stack. |
357 | dp->base.next = *stackpp; // We point next at the old top. |
358 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
359 | |
360 | cf_debug(AS_PREDEXP, "%p: predexp_or(%d)" , stackpp, nterms); |
361 | |
362 | return true; |
363 | } |
364 | |
365 | // ---------------------------------------------------------------- |
366 | // AS_PREDEXP_NOT |
367 | // ---------------------------------------------------------------- |
368 | |
369 | typedef struct { |
370 | predexp_eval_t base; |
371 | predexp_eval_t* child; |
372 | } predexp_eval_not_t; |
373 | |
374 | static void |
375 | destroy_not(predexp_eval_t* bp) |
376 | { |
377 | predexp_eval_not_t* dp = (predexp_eval_not_t *) bp; |
378 | destroy_list(dp->child); |
379 | cf_free(dp); |
380 | } |
381 | |
382 | static predexp_retval_t |
383 | eval_not(predexp_eval_t* bp, predexp_args_t* argsp, wrapped_as_bin_t* wbinp) |
384 | { |
385 | predexp_eval_not_t* dp = (predexp_eval_not_t *) bp; |
386 | |
387 | predexp_eval_t* cp = dp->child; |
388 | |
389 | switch ((*cp->eval_fn)(cp, argsp, NULL)) { |
390 | case PREDEXP_FALSE: |
391 | return PREDEXP_TRUE; |
392 | case PREDEXP_UNKNOWN: |
393 | return PREDEXP_UNKNOWN; |
394 | case PREDEXP_TRUE: |
395 | return PREDEXP_FALSE; |
396 | case PREDEXP_VALUE: |
397 | case PREDEXP_NOVALUE: |
398 | // Child can't be value node; shouldn't ever happen. |
399 | cf_crash(AS_PREDEXP, "eval_not child was value node" ); |
400 | } |
401 | |
402 | return PREDEXP_UNKNOWN; // Can't get here, makes compiler happy. |
403 | } |
404 | |
405 | static bool |
406 | build_not(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp) |
407 | { |
408 | if (len != 0) { |
409 | cf_warning(AS_PREDEXP, "predexp_not: unexpected size %d" , len); |
410 | return false; |
411 | } |
412 | |
413 | predexp_eval_not_t* dp = |
414 | (predexp_eval_not_t *) cf_malloc(sizeof(predexp_eval_not_t)); |
415 | |
416 | predexp_eval_base_init((predexp_eval_t *) dp, |
417 | destroy_not, |
418 | eval_not, |
419 | 0, |
420 | AS_PARTICLE_TYPE_NULL); |
421 | dp->child = NULL; |
422 | |
423 | // If there is not an available child expr cleanup and fail. |
424 | if (! *stackpp) { |
425 | cf_warning(AS_PREDEXP, "predexp_not: missing child" ); |
426 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
427 | return false; |
428 | } |
429 | // Transfer the expr at the top of the stack to our child list. |
430 | predexp_eval_t* child; |
431 | child = *stackpp; // Child from the top of the stack. |
432 | *stackpp = child->next; // Stack points around the child. |
433 | child->next = dp->child; // Child now points to prior list head. |
434 | dp->child = child; // Child is now the top of our list. |
435 | |
436 | // Make sure the child is not a value node. |
437 | if (dp->child->flags & PREDEXP_VALUE_NODE) { |
438 | cf_warning(AS_PREDEXP, "predexp_not: child is value node" ); |
439 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
440 | return false; |
441 | } |
442 | |
443 | // Success, push ourself onto the stack. |
444 | dp->base.next = *stackpp; // We point next at the old top. |
445 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
446 | |
447 | cf_debug(AS_PREDEXP, "%p: predexp_not" , stackpp); |
448 | |
449 | return true; |
450 | } |
451 | |
452 | // ---------------------------------------------------------------- |
453 | // AS_PREDEXP_*_COMPARE |
454 | // ---------------------------------------------------------------- |
455 | |
456 | // GEOSPATIAL NOTES: |
457 | // |
458 | // We want to perform all possible computation on the query region |
459 | // once, prior to visiting all the points. The current value |
460 | // interface is opaque; it returns a bin particle only; there is no |
461 | // way to pass associated precomputed state. So we keep the |
462 | // precomputed region query state here in the comparison node instead. |
463 | // |
464 | // IMPROVEMENTS: |
465 | // |
466 | // We currently parse the incoming query (IMMEDIATE) region twice; |
467 | // once in the from_wire_fn and again explicitly in the build_compare |
468 | // routine, this time retaining the region. Maybe we should make an |
469 | // exposed as_geojson_from_wire which additionally returns the |
470 | // computed region; the particle geojson_from_wire could call this |
471 | // routine and then discard the region. |
472 | // |
473 | // We can improve the performance of the comparison by covering the |
474 | // region at build time and saving all of the cell min/max ranges. |
475 | // Candidate points can first be checked against the list of ranges to |
476 | // make sure they are a rough match before performing the more |
477 | // expensive strict region match. This change requires a bunch more |
478 | // state; probably we'll want a pointer to the |
479 | // predexp_eval_geojson_state_t instead of using a union at this |
480 | // point. |
481 | |
482 | typedef struct predexp_eval_geojson_state_s { |
483 | uint64_t cellid; |
484 | geo_region_t region; |
485 | } predexp_eval_geojson_state_t; |
486 | |
487 | typedef struct predexp_eval_regex_state_s { |
488 | regex_t regex; |
489 | bool iscompiled; |
490 | } predexp_eval_regex_state_t; |
491 | |
492 | typedef struct predexp_eval_compare_s { |
493 | predexp_eval_t base; |
494 | uint16_t tag; |
495 | uint8_t type; |
496 | predexp_eval_t* lchild; |
497 | predexp_eval_t* rchild; |
498 | union { |
499 | predexp_eval_geojson_state_t geojson; |
500 | predexp_eval_regex_state_t regex; |
501 | } state; |
502 | } predexp_eval_compare_t; |
503 | |
504 | static void |
505 | destroy_compare(predexp_eval_t* bp) |
506 | { |
507 | predexp_eval_compare_t* dp = (predexp_eval_compare_t *) bp; |
508 | if (dp->lchild) { |
509 | (*dp->lchild->dtor_fn)(dp->lchild); |
510 | } |
511 | if (dp->rchild) { |
512 | (*dp->rchild->dtor_fn)(dp->rchild); |
513 | } |
514 | if (dp->type == AS_PARTICLE_TYPE_GEOJSON && dp->state.geojson.region) { |
515 | geo_region_destroy(dp->state.geojson.region); |
516 | } |
517 | if (dp->tag == AS_PREDEXP_STRING_REGEX && dp->state.regex.iscompiled) { |
518 | regfree(&dp->state.regex.regex); |
519 | } |
520 | cf_free(dp); |
521 | } |
522 | |
523 | static predexp_retval_t |
524 | eval_compare(predexp_eval_t* bp, |
525 | predexp_args_t* argsp, |
526 | wrapped_as_bin_t* wbinp) |
527 | { |
528 | predexp_eval_compare_t* dp = (predexp_eval_compare_t *) bp; |
529 | |
530 | predexp_retval_t retval = PREDEXP_UNKNOWN; |
531 | |
532 | wrapped_as_bin_t lwbin; |
533 | wrapped_as_bin_t rwbin; |
534 | lwbin.must_free = false; |
535 | rwbin.must_free = false; |
536 | |
537 | // Fetch the child values. Are either of the values unknown? |
538 | // During the metadata phase this returns PREDEXP_UNKNOWN. During |
539 | // the record phase we consider a comparison with an unknown value |
540 | // to be PREDEXP_FALSE (missing bin or bin or wrong type). |
541 | |
542 | if ((*dp->lchild->eval_fn)(dp->lchild, argsp, &lwbin) == |
543 | PREDEXP_NOVALUE) { |
544 | retval = argsp->rd ? PREDEXP_FALSE : PREDEXP_UNKNOWN; |
545 | goto Cleanup; |
546 | } |
547 | |
548 | if ((*dp->rchild->eval_fn)(dp->rchild, argsp, &rwbin) == |
549 | PREDEXP_NOVALUE) { |
550 | retval = argsp->rd ? PREDEXP_FALSE : PREDEXP_UNKNOWN; |
551 | goto Cleanup; |
552 | } |
553 | |
554 | switch (dp->type) { |
555 | case AS_PARTICLE_TYPE_INTEGER: { |
556 | int64_t lval = as_bin_particle_integer_value(&lwbin.bin); |
557 | int64_t rval = as_bin_particle_integer_value(&rwbin.bin); |
558 | switch (dp->tag) { |
559 | case AS_PREDEXP_INTEGER_EQUAL: |
560 | retval = PREDEXP_RETVAL(lval == rval); |
561 | goto Cleanup; |
562 | case AS_PREDEXP_INTEGER_UNEQUAL: |
563 | retval = PREDEXP_RETVAL(lval != rval); |
564 | goto Cleanup; |
565 | case AS_PREDEXP_INTEGER_GREATER: |
566 | retval = PREDEXP_RETVAL(lval > rval); |
567 | goto Cleanup; |
568 | case AS_PREDEXP_INTEGER_GREATEREQ: |
569 | retval = PREDEXP_RETVAL(lval >= rval); |
570 | goto Cleanup; |
571 | case AS_PREDEXP_INTEGER_LESS: |
572 | retval = PREDEXP_RETVAL(lval < rval); |
573 | goto Cleanup; |
574 | case AS_PREDEXP_INTEGER_LESSEQ: |
575 | retval = PREDEXP_RETVAL(lval <= rval); |
576 | goto Cleanup; |
577 | default: |
578 | cf_crash(AS_PREDEXP, "eval_compare integer unknown tag %d" , |
579 | dp->tag); |
580 | } |
581 | } |
582 | case AS_PARTICLE_TYPE_STRING: { |
583 | // We always need to fetch the left argument. |
584 | char* lptr; |
585 | uint32_t llen = as_bin_particle_string_ptr(&lwbin.bin, &lptr); |
586 | char* rptr; |
587 | uint32_t rlen; |
588 | switch (dp->tag) { |
589 | case AS_PREDEXP_STRING_EQUAL: |
590 | case AS_PREDEXP_STRING_UNEQUAL: |
591 | // These comparisons need the right argument too. |
592 | rlen = as_bin_particle_string_ptr(&rwbin.bin, &rptr); |
593 | bool isequal = (llen == rlen) && (memcmp(lptr, rptr, llen) == 0); |
594 | switch (dp->tag) { |
595 | case AS_PREDEXP_STRING_EQUAL: |
596 | retval = isequal; |
597 | goto Cleanup; |
598 | case AS_PREDEXP_STRING_UNEQUAL: |
599 | retval = ! isequal; |
600 | goto Cleanup; |
601 | default: |
602 | cf_crash(AS_PREDEXP, "eval_compare string (eq) unknown tag %d" , |
603 | dp->tag); |
604 | } |
605 | case AS_PREDEXP_STRING_REGEX: { |
606 | char* tmpstr = cf_strndup(lptr, llen); |
607 | int rv = regexec(&dp->state.regex.regex, tmpstr, 0, NULL, 0); |
608 | cf_free(tmpstr); |
609 | retval = rv == 0; |
610 | goto Cleanup; |
611 | } |
612 | default: |
613 | cf_crash(AS_PREDEXP, "eval_compare string unknown tag %d" , dp->tag); |
614 | } |
615 | } |
616 | case AS_PARTICLE_TYPE_GEOJSON: { |
617 | // as_particle* lpart = lbinp->particle; |
618 | // as_particle* rpart = rbinp->particle; |
619 | |
620 | switch (dp->tag) { |
621 | case AS_PREDEXP_GEOJSON_WITHIN: |
622 | case AS_PREDEXP_GEOJSON_CONTAINS: { |
623 | bool isstrict = true; |
624 | bool ismatch = as_particle_geojson_match(lwbin.bin.particle, |
625 | dp->state.geojson.cellid, |
626 | dp->state.geojson.region, |
627 | isstrict); |
628 | retval = PREDEXP_RETVAL(ismatch); |
629 | goto Cleanup; |
630 | } |
631 | default: |
632 | cf_crash(AS_PREDEXP, "eval_compare geojson unknown tag %d" , |
633 | dp->tag); |
634 | } |
635 | } |
636 | default: |
637 | cf_crash(AS_PREDEXP, "eval_compare unknown type %d" , dp->type); |
638 | } |
639 | |
640 | Cleanup: |
641 | if (lwbin.must_free) { |
642 | cf_crash(AS_PREDEXP, "eval_compare need bin cleanup, didn't before" ); |
643 | } |
644 | if (rwbin.must_free) { |
645 | cf_crash(AS_PREDEXP, "eval_compare need bin cleanup, didn't before" ); |
646 | } |
647 | return retval; |
648 | } |
649 | |
650 | static bool |
651 | build_compare(predexp_eval_t** stackpp, |
652 | uint32_t len, |
653 | uint8_t* pp, |
654 | uint16_t tag) |
655 | { |
656 | predexp_eval_compare_t* dp = (predexp_eval_compare_t *) |
657 | cf_malloc(sizeof(predexp_eval_compare_t)); |
658 | |
659 | predexp_eval_base_init((predexp_eval_t *) dp, |
660 | destroy_compare, |
661 | eval_compare, |
662 | 0, |
663 | AS_PARTICLE_TYPE_NULL); |
664 | |
665 | dp->tag = tag; |
666 | dp->lchild = NULL; |
667 | dp->rchild = NULL; |
668 | |
669 | // IMPORTANT - If your state doesn't want to be initialized |
670 | // to all 0 rethink this ... |
671 | // |
672 | memset(&dp->state, 0, sizeof(dp->state)); |
673 | |
674 | switch (tag) { |
675 | case AS_PREDEXP_INTEGER_EQUAL: |
676 | case AS_PREDEXP_INTEGER_UNEQUAL: |
677 | case AS_PREDEXP_INTEGER_GREATER: |
678 | case AS_PREDEXP_INTEGER_GREATEREQ: |
679 | case AS_PREDEXP_INTEGER_LESS: |
680 | case AS_PREDEXP_INTEGER_LESSEQ: |
681 | dp->type = AS_PARTICLE_TYPE_INTEGER; |
682 | break; |
683 | case AS_PREDEXP_STRING_EQUAL: |
684 | case AS_PREDEXP_STRING_UNEQUAL: |
685 | case AS_PREDEXP_STRING_REGEX: |
686 | dp->type = AS_PARTICLE_TYPE_STRING; |
687 | break; |
688 | case AS_PREDEXP_GEOJSON_WITHIN: |
689 | case AS_PREDEXP_GEOJSON_CONTAINS: |
690 | dp->type = AS_PARTICLE_TYPE_GEOJSON; |
691 | break; |
692 | default: |
693 | cf_crash(AS_PREDEXP, "build_compare called with bogus tag: %d" , tag); |
694 | break; |
695 | } |
696 | |
697 | uint8_t* endp = pp + len; |
698 | |
699 | uint32_t regex_opts = 0; |
700 | if (tag == AS_PREDEXP_STRING_REGEX) { |
701 | // This comparison takes a uint32_t opts argument. |
702 | if (pp + sizeof(uint32_t) > endp) { |
703 | cf_warning(AS_PREDEXP, "build_compare: regex opts past end" ); |
704 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
705 | return false; |
706 | } |
707 | regex_opts = cf_swap_from_be32(* (uint32_t *) pp); |
708 | pp += sizeof(uint32_t); |
709 | } |
710 | |
711 | // No arguments. |
712 | if (pp != endp) { |
713 | cf_warning(AS_PREDEXP, "build_compare: msg unaligned" ); |
714 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
715 | return false; |
716 | } |
717 | |
718 | // ---- Pop the right child off the stack. |
719 | |
720 | if (! *stackpp) { |
721 | cf_warning(AS_PREDEXP, "predexp_compare: missing right child" ); |
722 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
723 | return false; |
724 | } |
725 | |
726 | dp->rchild = *stackpp; |
727 | *stackpp = dp->rchild->next; |
728 | dp->rchild->next = NULL; |
729 | |
730 | if ((dp->rchild->flags & PREDEXP_VALUE_NODE) == 0) { |
731 | cf_warning(AS_PREDEXP, |
732 | "predexp compare: right child is not value node" ); |
733 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
734 | return false; |
735 | } |
736 | |
737 | if (dp->rchild->type != dp->type) { |
738 | cf_warning(AS_PREDEXP, "predexp compare: right child is wrong type" ); |
739 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
740 | return false; |
741 | } |
742 | |
743 | // ---- Pop the left child off the stack. |
744 | |
745 | if (! *stackpp) { |
746 | cf_warning(AS_PREDEXP, "predexp_compare: missing left child" ); |
747 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
748 | return false; |
749 | } |
750 | |
751 | dp->lchild = *stackpp; |
752 | *stackpp = dp->lchild->next; |
753 | dp->lchild->next = NULL; |
754 | |
755 | if ((dp->lchild->flags & PREDEXP_VALUE_NODE) == 0) { |
756 | cf_warning(AS_PREDEXP, "predexp compare: left child is not value node" ); |
757 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
758 | return false; |
759 | } |
760 | |
761 | if (dp->lchild->type != dp->type) { |
762 | cf_warning(AS_PREDEXP, "predexp compare: left child is wrong type" ); |
763 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
764 | return false; |
765 | } |
766 | |
767 | switch (tag) { |
768 | case AS_PREDEXP_GEOJSON_WITHIN: |
769 | case AS_PREDEXP_GEOJSON_CONTAINS: |
770 | // The right child needs to be an immediate value. |
771 | if ((dp->rchild->flags & PREDEXP_IMMEDIATE_NODE) == 0) { |
772 | cf_warning(AS_PREDEXP, |
773 | "predexp compare: within arg not immediate GeoJSON" ); |
774 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
775 | return false; |
776 | } |
777 | |
778 | // Extract the query GeoJSON value. |
779 | predexp_args_t* argsp = NULL; // immediate values don't need args |
780 | wrapped_as_bin_t rwbin; |
781 | rwbin.must_free = false; |
782 | if ((*dp->rchild->eval_fn)(dp->rchild, argsp, &rwbin) == |
783 | PREDEXP_NOVALUE) { |
784 | cf_warning(AS_PREDEXP, |
785 | "predexp compare: within arg had unknown value" ); |
786 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
787 | return false; |
788 | } |
789 | size_t sz; |
790 | char const * ptr = as_geojson_mem_jsonstr(rwbin.bin.particle, &sz); |
791 | |
792 | // Parse the child, save the computed state. |
793 | if (!geo_parse(NULL, ptr, sz, |
794 | &dp->state.geojson.cellid, |
795 | &dp->state.geojson.region)) { |
796 | cf_warning(AS_PREDEXP, "predexp compare: failed to parse GeoJSON" ); |
797 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
798 | if (rwbin.must_free) { |
799 | cf_crash(AS_PREDEXP, |
800 | "predexp compare now needs bin destructor" ); |
801 | } |
802 | return false; |
803 | } |
804 | if (rwbin.must_free) { |
805 | cf_crash(AS_PREDEXP, "predexp compare now needs bin destructor" ); |
806 | } |
807 | break; |
808 | case AS_PREDEXP_STRING_REGEX: |
809 | // The right child needs to be an immediate value. |
810 | if ((dp->rchild->flags & PREDEXP_IMMEDIATE_NODE) == 0) { |
811 | cf_warning(AS_PREDEXP, |
812 | "predexp compare: regex arg not immediate string" ); |
813 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
814 | return false; |
815 | } |
816 | |
817 | // Extract the query regex value. |
818 | predexp_args_t* argsp2 = NULL; // immediate values don't need args |
819 | wrapped_as_bin_t rwbin2; |
820 | rwbin2.must_free = false; |
821 | if ((*dp->rchild->eval_fn)(dp->rchild, argsp2, &rwbin2) == |
822 | PREDEXP_NOVALUE) { |
823 | cf_warning(AS_PREDEXP, |
824 | "predexp compare: regex arg had unknown value" ); |
825 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
826 | return false; |
827 | } |
828 | char* rptr; |
829 | uint32_t rlen = as_bin_particle_string_ptr(&rwbin2.bin, &rptr); |
830 | char* tmpregexp = cf_strndup(rptr, rlen); |
831 | int rv = regcomp(&dp->state.regex.regex, tmpregexp, regex_opts); |
832 | cf_free(tmpregexp); |
833 | if (rv != 0) { |
834 | char errbuf[1024]; |
835 | regerror(rv, &dp->state.regex.regex, errbuf, sizeof(errbuf)); |
836 | cf_warning(AS_PREDEXP, "predexp compare: regex compile failed: %s" , |
837 | errbuf); |
838 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
839 | if (rwbin2.must_free) { |
840 | cf_crash(AS_PREDEXP, |
841 | "predexp compare now needs bin destructor" ); |
842 | } |
843 | return false; |
844 | } |
845 | dp->state.regex.iscompiled = true; |
846 | if (rwbin2.must_free) { |
847 | cf_crash(AS_PREDEXP, "predexp compare now needs bin destructor" ); |
848 | } |
849 | break; |
850 | |
851 | default: |
852 | // Don't do anything for the others ... |
853 | break; |
854 | } |
855 | |
856 | // Success, push ourself onto the stack. |
857 | dp->base.next = *stackpp; // We point next at the old top. |
858 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
859 | |
860 | switch (tag) { |
861 | case AS_PREDEXP_INTEGER_EQUAL: |
862 | cf_debug(AS_PREDEXP, "%p: predexp_integer_equal" , stackpp); |
863 | break; |
864 | case AS_PREDEXP_INTEGER_UNEQUAL: |
865 | cf_debug(AS_PREDEXP, "%p: predexp_integer_unequal" , stackpp); |
866 | break; |
867 | case AS_PREDEXP_INTEGER_GREATER: |
868 | cf_debug(AS_PREDEXP, "%p: predexp_integer_greater" , stackpp); |
869 | break; |
870 | case AS_PREDEXP_INTEGER_GREATEREQ: |
871 | cf_debug(AS_PREDEXP, "%p: predexp_integer_greatereq" , stackpp); |
872 | break; |
873 | case AS_PREDEXP_INTEGER_LESS: |
874 | cf_debug(AS_PREDEXP, "%p: predexp_integer_less" , stackpp); |
875 | break; |
876 | case AS_PREDEXP_INTEGER_LESSEQ: |
877 | cf_debug(AS_PREDEXP, "%p: predexp_integer_lesseq" , stackpp); |
878 | break; |
879 | case AS_PREDEXP_STRING_EQUAL: |
880 | cf_debug(AS_PREDEXP, "%p: predexp_string_equal" , stackpp); |
881 | break; |
882 | case AS_PREDEXP_STRING_UNEQUAL: |
883 | cf_debug(AS_PREDEXP, "%p: predexp_string_unequal" , stackpp); |
884 | break; |
885 | case AS_PREDEXP_STRING_REGEX: |
886 | cf_debug(AS_PREDEXP, "%p: predexp_string_regex(%d)" , stackpp, |
887 | regex_opts); |
888 | break; |
889 | case AS_PREDEXP_GEOJSON_WITHIN: |
890 | cf_debug(AS_PREDEXP, "%p: predexp_geojson_within" , stackpp); |
891 | break; |
892 | case AS_PREDEXP_GEOJSON_CONTAINS: |
893 | cf_debug(AS_PREDEXP, "%p: predexp_geojson_contains" , stackpp); |
894 | break; |
895 | default: |
896 | cf_crash(AS_PREDEXP, "build_compare called with bogus tag: %d" , tag); |
897 | break; |
898 | } |
899 | |
900 | return true; |
901 | } |
902 | |
903 | // ---------------------------------------------------------------- |
904 | // AS_PREDEXP_*_VALUE |
905 | // ---------------------------------------------------------------- |
906 | |
907 | typedef struct predexp_eval_value_s { |
908 | predexp_eval_t base; |
909 | as_bin bin; |
910 | uint8_t type; |
911 | } predexp_eval_value_t; |
912 | |
913 | static void |
914 | destroy_value(predexp_eval_t* bp) |
915 | { |
916 | predexp_eval_value_t* dp = (predexp_eval_value_t *) bp; |
917 | as_bin_particle_destroy(&dp->bin, true); |
918 | cf_free(dp); |
919 | } |
920 | |
921 | static predexp_retval_t |
922 | eval_value(predexp_eval_t* bp, predexp_args_t* argsp, wrapped_as_bin_t* wbinp) |
923 | { |
924 | if (wbinp == NULL) { |
925 | cf_crash(AS_PREDEXP, "eval_value called outside value context" ); |
926 | } |
927 | |
928 | predexp_eval_value_t* dp = (predexp_eval_value_t *) bp; |
929 | // We don't have a ns in this context. But the source bin doesn't |
930 | // have any name index stuff anyway ... |
931 | as_single_bin_copy(&wbinp->bin, &dp->bin); |
932 | wbinp->must_free = false; // bin is constant, destroyed after query above |
933 | return PREDEXP_VALUE; |
934 | } |
935 | |
936 | static bool |
937 | build_value(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp, uint16_t tag) |
938 | { |
939 | predexp_eval_value_t* dp = (predexp_eval_value_t *) |
940 | cf_malloc(sizeof(predexp_eval_value_t)); |
941 | |
942 | uint8_t type; |
943 | switch (tag) { |
944 | case AS_PREDEXP_INTEGER_VALUE: type = AS_PARTICLE_TYPE_INTEGER; break; |
945 | case AS_PREDEXP_STRING_VALUE: type = AS_PARTICLE_TYPE_STRING; break; |
946 | case AS_PREDEXP_GEOJSON_VALUE: type = AS_PARTICLE_TYPE_GEOJSON; break; |
947 | default: |
948 | cf_crash(AS_PREDEXP, "build_value called with bogus tag: %d" , tag); |
949 | return false; |
950 | } |
951 | |
952 | predexp_eval_base_init((predexp_eval_t *) dp, |
953 | destroy_value, |
954 | eval_value, |
955 | PREDEXP_VALUE_NODE | PREDEXP_IMMEDIATE_NODE, |
956 | type); |
957 | |
958 | as_bin_set_empty(&dp->bin); |
959 | dp->bin.particle = NULL; |
960 | |
961 | uint8_t* endp = pp + len; |
962 | |
963 | size_t vallen = len; |
964 | void* valptr = (char*) pp; |
965 | pp += vallen; |
966 | |
967 | if (pp != endp) { |
968 | cf_warning(AS_PREDEXP, "predexp value: msg unaligned" ); |
969 | goto Failed; |
970 | } |
971 | |
972 | int32_t mem_size = particle_vtable[type]->size_from_wire_fn(valptr, vallen); |
973 | |
974 | if (mem_size < 0) { |
975 | goto Failed; |
976 | } |
977 | |
978 | if (mem_size != 0) { |
979 | dp->bin.particle = cf_malloc((size_t)mem_size); |
980 | } |
981 | |
982 | int result = particle_vtable[type]->from_wire_fn(type, |
983 | valptr, |
984 | vallen, |
985 | &dp->bin.particle); |
986 | |
987 | // Set the bin's iparticle metadata. |
988 | if (result == 0) { |
989 | as_bin_state_set_from_type(&dp->bin, type); |
990 | } |
991 | else { |
992 | cf_warning(AS_PREDEXP, "failed to build predexp value with err %d" , |
993 | result); |
994 | if (mem_size != 0) { |
995 | cf_free(dp->bin.particle); |
996 | } |
997 | as_bin_set_empty(&dp->bin); |
998 | dp->bin.particle = NULL; |
999 | goto Failed; |
1000 | } |
1001 | |
1002 | // Success, push ourself onto the stack. |
1003 | dp->base.next = *stackpp; // We point next at the old top. |
1004 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
1005 | |
1006 | switch (tag) { |
1007 | case AS_PREDEXP_INTEGER_VALUE: |
1008 | cf_debug(AS_PREDEXP, "%p: predexp_integer_value(%" PRId64")" , stackpp, |
1009 | (int64_t) dp->bin.particle); |
1010 | break; |
1011 | case AS_PREDEXP_STRING_VALUE: { |
1012 | cf_debug(AS_PREDEXP, "%p: predexp_string_value(\"%s\")" , stackpp, |
1013 | CF_ZSTR1K(valptr, vallen)); |
1014 | break; |
1015 | } |
1016 | case AS_PREDEXP_GEOJSON_VALUE: { |
1017 | size_t jsonsz; |
1018 | char const * jsonptr = |
1019 | as_geojson_mem_jsonstr(dp->bin.particle, &jsonsz); |
1020 | cf_debug(AS_PREDEXP, "%p: predexp_geojson_value(%s)" , stackpp, |
1021 | CF_ZSTR1K(jsonptr, jsonsz)); |
1022 | break; |
1023 | } |
1024 | default: |
1025 | cf_crash(AS_PREDEXP, "build_value called with bogus tag: %d" , tag); |
1026 | break; |
1027 | } |
1028 | |
1029 | return true; |
1030 | |
1031 | Failed: |
1032 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1033 | return false; |
1034 | } |
1035 | |
1036 | // ---------------------------------------------------------------- |
1037 | // AS_PREDEXP_*_BIN |
1038 | // ---------------------------------------------------------------- |
1039 | |
1040 | typedef struct predexp_eval_bin_s { |
1041 | predexp_eval_t base; |
1042 | char bname[AS_BIN_NAME_MAX_SZ]; |
1043 | uint8_t type; |
1044 | } predexp_eval_bin_t; |
1045 | |
1046 | static void |
1047 | destroy_bin(predexp_eval_t* bp) |
1048 | { |
1049 | predexp_eval_bin_t* dp = (predexp_eval_bin_t *) bp; |
1050 | cf_free(dp); |
1051 | } |
1052 | |
1053 | static predexp_retval_t |
1054 | eval_bin(predexp_eval_t* bp, predexp_args_t* argsp, wrapped_as_bin_t* wbinp) |
1055 | { |
1056 | if (wbinp == NULL) { |
1057 | cf_crash(AS_PREDEXP, "eval_bin called outside value context" ); |
1058 | } |
1059 | |
1060 | predexp_eval_bin_t* dp = (predexp_eval_bin_t *) bp; |
1061 | |
1062 | // We require record data to operate. |
1063 | if (! argsp->rd) { |
1064 | return PREDEXP_NOVALUE; |
1065 | } |
1066 | |
1067 | as_bin* bb = as_bin_get(argsp->rd, dp->bname); |
1068 | if (! bb) { |
1069 | return PREDEXP_NOVALUE; |
1070 | } |
1071 | |
1072 | if (as_bin_get_particle_type(bb) != dp->type) { |
1073 | return PREDEXP_NOVALUE; |
1074 | } |
1075 | |
1076 | as_bin_copy(argsp->ns, &wbinp->bin, bb); |
1077 | wbinp->must_free = false; // bin is owned by record, in caller |
1078 | return PREDEXP_VALUE; |
1079 | } |
1080 | |
1081 | static bool |
1082 | build_bin(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp, uint16_t tag) |
1083 | { |
1084 | predexp_eval_bin_t* dp = (predexp_eval_bin_t *) |
1085 | cf_malloc(sizeof(predexp_eval_bin_t)); |
1086 | |
1087 | switch (tag) { |
1088 | case AS_PREDEXP_INTEGER_BIN: |
1089 | dp->type = AS_PARTICLE_TYPE_INTEGER; |
1090 | break; |
1091 | case AS_PREDEXP_STRING_BIN: |
1092 | dp->type = AS_PARTICLE_TYPE_STRING; |
1093 | break; |
1094 | case AS_PREDEXP_GEOJSON_BIN: |
1095 | dp->type = AS_PARTICLE_TYPE_GEOJSON; |
1096 | break; |
1097 | case AS_PREDEXP_LIST_BIN: |
1098 | dp->type = AS_PARTICLE_TYPE_LIST; |
1099 | break; |
1100 | case AS_PREDEXP_MAP_BIN: |
1101 | dp->type = AS_PARTICLE_TYPE_MAP; |
1102 | break; |
1103 | default: |
1104 | cf_crash(AS_PREDEXP, "build_bin called with bogus tag: %d" , tag); |
1105 | break; |
1106 | } |
1107 | |
1108 | predexp_eval_base_init((predexp_eval_t *) dp, |
1109 | destroy_bin, |
1110 | eval_bin, |
1111 | PREDEXP_VALUE_NODE, |
1112 | dp->type); |
1113 | |
1114 | uint8_t* endp = pp + len; |
1115 | |
1116 | if (len >= sizeof(dp->bname)) { |
1117 | cf_warning(AS_PREDEXP, "build_bin: binname too long" ); |
1118 | goto Failed; |
1119 | } |
1120 | uint8_t bnlen = (uint8_t) len; |
1121 | memcpy(dp->bname, pp, bnlen); |
1122 | dp->bname[bnlen] = '\0'; |
1123 | pp += bnlen; |
1124 | |
1125 | if (pp != endp) { |
1126 | cf_warning(AS_PREDEXP, "build_bin: msg unaligned" ); |
1127 | goto Failed; |
1128 | } |
1129 | |
1130 | // Success, push ourself onto the stack. |
1131 | dp->base.next = *stackpp; // We point next at the old top. |
1132 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
1133 | |
1134 | switch (tag) { |
1135 | case AS_PREDEXP_INTEGER_BIN: |
1136 | cf_debug(AS_PREDEXP, "%p: predexp_integer_bin(\"%s\")" , stackpp, |
1137 | dp->bname); |
1138 | break; |
1139 | case AS_PREDEXP_STRING_BIN: |
1140 | cf_debug(AS_PREDEXP, "%p: predexp_string_bin(\"%s\")" , stackpp, |
1141 | dp->bname); |
1142 | break; |
1143 | case AS_PREDEXP_GEOJSON_BIN: |
1144 | cf_debug(AS_PREDEXP, "%p: predexp_geojson_bin(\"%s\")" , stackpp, |
1145 | dp->bname); |
1146 | break; |
1147 | case AS_PREDEXP_LIST_BIN: |
1148 | cf_debug(AS_PREDEXP, "%p: predexp_list_bin(\"%s\")" , stackpp, |
1149 | dp->bname); |
1150 | break; |
1151 | case AS_PREDEXP_MAP_BIN: |
1152 | cf_debug(AS_PREDEXP, "%p: predexp_map_bin(\"%s\")" , stackpp, |
1153 | dp->bname); |
1154 | break; |
1155 | default: |
1156 | cf_crash(AS_PREDEXP, "build_bin called with bogus tag: %d" , tag); |
1157 | break; |
1158 | } |
1159 | |
1160 | return true; |
1161 | |
1162 | Failed: |
1163 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1164 | return false; |
1165 | } |
1166 | |
1167 | // ---------------------------------------------------------------- |
1168 | // AS_PREDEXP_*_VAR |
1169 | // ---------------------------------------------------------------- |
1170 | |
1171 | typedef struct predexp_eval_var_s { |
1172 | predexp_eval_t base; |
1173 | char vname[AS_BIN_NAME_MAX_SZ]; |
1174 | uint8_t type; |
1175 | } predexp_eval_var_t; |
1176 | |
1177 | static void |
1178 | destroy_var(predexp_eval_t* bp) |
1179 | { |
1180 | predexp_eval_var_t* dp = (predexp_eval_var_t *) bp; |
1181 | cf_free(dp); |
1182 | } |
1183 | |
1184 | static predexp_retval_t |
1185 | eval_var(predexp_eval_t* bp, predexp_args_t* argsp, wrapped_as_bin_t* wbinp) |
1186 | { |
1187 | if (wbinp == NULL) { |
1188 | cf_crash(AS_PREDEXP, "eval_var called outside value context" ); |
1189 | } |
1190 | |
1191 | predexp_eval_var_t* dp = (predexp_eval_var_t *) bp; |
1192 | |
1193 | for (as_predexp_var_t* vp = argsp->vl; vp != NULL; vp = vp->next) { |
1194 | if (strcmp(dp->vname, vp->vname) == 0) { |
1195 | // Is it the correct type? |
1196 | if (as_bin_get_particle_type(&vp->bin) != dp->type) { |
1197 | return PREDEXP_NOVALUE; |
1198 | } |
1199 | |
1200 | // Return it. |
1201 | as_bin_copy(argsp->ns, &wbinp->bin, &vp->bin); |
1202 | wbinp->must_free = false; // bin is owned by iterator |
1203 | return PREDEXP_VALUE; |
1204 | } |
1205 | } |
1206 | |
1207 | // If we get here we didn't find the named variable in the list. |
1208 | return PREDEXP_NOVALUE; |
1209 | } |
1210 | |
1211 | static bool |
1212 | build_var(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp, uint16_t tag) |
1213 | { |
1214 | predexp_eval_var_t* dp = (predexp_eval_var_t *) |
1215 | cf_malloc(sizeof(predexp_eval_var_t)); |
1216 | |
1217 | switch (tag) { |
1218 | case AS_PREDEXP_INTEGER_VAR: |
1219 | dp->type = AS_PARTICLE_TYPE_INTEGER; |
1220 | break; |
1221 | case AS_PREDEXP_STRING_VAR: |
1222 | dp->type = AS_PARTICLE_TYPE_STRING; |
1223 | break; |
1224 | case AS_PREDEXP_GEOJSON_VAR: |
1225 | dp->type = AS_PARTICLE_TYPE_GEOJSON; |
1226 | break; |
1227 | default: |
1228 | cf_crash(AS_PREDEXP, "build_var called with bogus tag: %d" , tag); |
1229 | break; |
1230 | } |
1231 | |
1232 | predexp_eval_base_init((predexp_eval_t *) dp, |
1233 | destroy_var, |
1234 | eval_var, |
1235 | PREDEXP_VALUE_NODE, |
1236 | dp->type); |
1237 | |
1238 | uint8_t* endp = pp + len; |
1239 | |
1240 | if (len >= sizeof(dp->vname)) { |
1241 | cf_warning(AS_PREDEXP, "build_var: varname too long" ); |
1242 | goto Failed; |
1243 | } |
1244 | uint8_t bnlen = (uint8_t) len; |
1245 | memcpy(dp->vname, pp, bnlen); |
1246 | dp->vname[bnlen] = '\0'; |
1247 | pp += bnlen; |
1248 | |
1249 | if (pp != endp) { |
1250 | cf_warning(AS_PREDEXP, "build_var: msg unaligned" ); |
1251 | goto Failed; |
1252 | } |
1253 | |
1254 | // Success, push ourself onto the stack. |
1255 | dp->base.next = *stackpp; // We point next at the old top. |
1256 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
1257 | |
1258 | switch (tag) { |
1259 | case AS_PREDEXP_INTEGER_VAR: |
1260 | cf_debug(AS_PREDEXP, "%p: predexp_integer_var(\"%s\")" , stackpp, |
1261 | dp->vname); |
1262 | break; |
1263 | case AS_PREDEXP_STRING_VAR: |
1264 | cf_debug(AS_PREDEXP, "%p: predexp_string_var(\"%s\")" , stackpp, |
1265 | dp->vname); |
1266 | break; |
1267 | case AS_PREDEXP_GEOJSON_VAR: |
1268 | cf_debug(AS_PREDEXP, "%p: predexp_geojson_var(\"%s\")" , stackpp, |
1269 | dp->vname); |
1270 | break; |
1271 | default: |
1272 | cf_crash(AS_PREDEXP, "build_var called with bogus tag: %d" , tag); |
1273 | break; |
1274 | } |
1275 | |
1276 | return true; |
1277 | |
1278 | Failed: |
1279 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1280 | return false; |
1281 | } |
1282 | |
1283 | // ---------------------------------------------------------------- |
1284 | // AS_PREDEXP_REC_DEVICE_SIZE |
1285 | // ---------------------------------------------------------------- |
1286 | |
1287 | typedef struct predexp_eval_rec_device_size_s { |
1288 | predexp_eval_t base; |
1289 | } predexp_eval_rec_device_size_t; |
1290 | |
1291 | static void |
1292 | destroy_rec_device_size(predexp_eval_t* bp) |
1293 | { |
1294 | predexp_eval_rec_device_size_t* dp = (predexp_eval_rec_device_size_t *) bp; |
1295 | cf_free(dp); |
1296 | } |
1297 | |
1298 | static predexp_retval_t |
1299 | eval_rec_device_size(predexp_eval_t* bp, |
1300 | predexp_args_t* argsp, |
1301 | wrapped_as_bin_t* wbinp) |
1302 | { |
1303 | if (wbinp == NULL) { |
1304 | cf_crash(AS_PREDEXP, |
1305 | "eval_rec_device_size called outside value context" ); |
1306 | } |
1307 | |
1308 | // predexp_eval_rec_device_size_t* dp = |
1309 | // (predexp_eval_rec_device_size_t *) bp; |
1310 | |
1311 | int64_t rec_device_size = |
1312 | (int64_t)as_storage_record_size(argsp->ns, argsp->md); |
1313 | |
1314 | as_bin_state_set_from_type(&wbinp->bin, AS_PARTICLE_TYPE_INTEGER); |
1315 | as_bin_particle_integer_set(&wbinp->bin, rec_device_size); |
1316 | return PREDEXP_VALUE; |
1317 | } |
1318 | |
1319 | static bool |
1320 | build_rec_device_size(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp) |
1321 | { |
1322 | predexp_eval_rec_device_size_t* dp = (predexp_eval_rec_device_size_t *) |
1323 | cf_malloc(sizeof(predexp_eval_rec_device_size_t)); |
1324 | |
1325 | predexp_eval_base_init((predexp_eval_t *) dp, |
1326 | destroy_rec_device_size, |
1327 | eval_rec_device_size, |
1328 | PREDEXP_VALUE_NODE, |
1329 | AS_PARTICLE_TYPE_INTEGER); |
1330 | |
1331 | uint8_t* endp = pp + len; |
1332 | |
1333 | if (pp != endp) { |
1334 | cf_warning(AS_PREDEXP, "build_rec_device_size: msg unaligned" ); |
1335 | goto Failed; |
1336 | } |
1337 | |
1338 | // Success, push ourself onto the stack. |
1339 | dp->base.next = *stackpp; // We point next at the old top. |
1340 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
1341 | |
1342 | cf_debug(AS_PREDEXP, "%p: predexp_rec_device_size()" , stackpp); |
1343 | |
1344 | return true; |
1345 | |
1346 | Failed: |
1347 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1348 | return false; |
1349 | } |
1350 | |
1351 | // ---------------------------------------------------------------- |
1352 | // AS_PREDEXP_REC_LAST_UPDATE |
1353 | // ---------------------------------------------------------------- |
1354 | |
1355 | typedef struct predexp_eval_rec_last_update_s { |
1356 | predexp_eval_t base; |
1357 | as_bin bin; |
1358 | } predexp_eval_rec_last_update_t; |
1359 | |
1360 | static void |
1361 | destroy_rec_last_update(predexp_eval_t* bp) |
1362 | { |
1363 | predexp_eval_rec_last_update_t* dp = (predexp_eval_rec_last_update_t *) bp; |
1364 | cf_free(dp); |
1365 | } |
1366 | |
1367 | static predexp_retval_t |
1368 | eval_rec_last_update(predexp_eval_t* bp, |
1369 | predexp_args_t* argsp, |
1370 | wrapped_as_bin_t* wbinp) |
1371 | { |
1372 | if (wbinp == NULL) { |
1373 | cf_crash(AS_PREDEXP, |
1374 | "eval_rec_last_update called outside value context" ); |
1375 | } |
1376 | |
1377 | // predexp_eval_rec_last_update_t* dp = |
1378 | // (predexp_eval_rec_last_update_t *) bp; |
1379 | |
1380 | int64_t rec_last_update_ns = |
1381 | (int64_t) cf_utc_ns_from_clepoch_ms(argsp->md->last_update_time); |
1382 | |
1383 | as_bin_state_set_from_type(&wbinp->bin, AS_PARTICLE_TYPE_INTEGER); |
1384 | as_bin_particle_integer_set(&wbinp->bin, rec_last_update_ns); |
1385 | return PREDEXP_VALUE; |
1386 | } |
1387 | |
1388 | static bool |
1389 | build_rec_last_update(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp) |
1390 | { |
1391 | predexp_eval_rec_last_update_t* dp = (predexp_eval_rec_last_update_t *) |
1392 | cf_malloc(sizeof(predexp_eval_rec_last_update_t)); |
1393 | |
1394 | predexp_eval_base_init((predexp_eval_t *) dp, |
1395 | destroy_rec_last_update, |
1396 | eval_rec_last_update, |
1397 | PREDEXP_VALUE_NODE, |
1398 | AS_PARTICLE_TYPE_INTEGER); |
1399 | |
1400 | uint8_t* endp = pp + len; |
1401 | |
1402 | if (pp != endp) { |
1403 | cf_warning(AS_PREDEXP, "build_rec_last_update: msg unaligned" ); |
1404 | goto Failed; |
1405 | } |
1406 | |
1407 | // Success, push ourself onto the stack. |
1408 | dp->base.next = *stackpp; // We point next at the old top. |
1409 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
1410 | |
1411 | cf_debug(AS_PREDEXP, "%p: predexp_rec_last_update()" , stackpp); |
1412 | |
1413 | return true; |
1414 | |
1415 | Failed: |
1416 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1417 | return false; |
1418 | } |
1419 | |
1420 | // ---------------------------------------------------------------- |
1421 | // AS_PREDEXP_REC_VOID_TIME |
1422 | // ---------------------------------------------------------------- |
1423 | |
1424 | typedef struct predexp_eval_rec_void_time_s { |
1425 | predexp_eval_t base; |
1426 | as_bin bin; |
1427 | } predexp_eval_rec_void_time_t; |
1428 | |
1429 | static void |
1430 | destroy_rec_void_time(predexp_eval_t* bp) |
1431 | { |
1432 | predexp_eval_rec_void_time_t* dp = (predexp_eval_rec_void_time_t *) bp; |
1433 | cf_free(dp); |
1434 | } |
1435 | |
1436 | static predexp_retval_t |
1437 | eval_rec_void_time(predexp_eval_t* bp, |
1438 | predexp_args_t* argsp, |
1439 | wrapped_as_bin_t* wbinp) |
1440 | { |
1441 | if (wbinp == NULL) { |
1442 | cf_crash(AS_PREDEXP, "eval_rec_void_time called outside value context" ); |
1443 | } |
1444 | |
1445 | // predexp_eval_rec_void_time_t* dp = (predexp_eval_rec_void_time_t *) bp; |
1446 | |
1447 | int64_t rec_void_time_ns = |
1448 | (int64_t) cf_utc_ns_from_clepoch_sec(argsp->md->void_time); |
1449 | |
1450 | // SPECIAL CASE - if the argsp->md->rec_void_time == 0 set the |
1451 | // rec_void_time_ns to 0 as well. |
1452 | // |
1453 | if (argsp->md->void_time == 0) { |
1454 | rec_void_time_ns = 0; |
1455 | } |
1456 | |
1457 | as_bin_state_set_from_type(&wbinp->bin, AS_PARTICLE_TYPE_INTEGER); |
1458 | as_bin_particle_integer_set(&wbinp->bin, rec_void_time_ns); |
1459 | return PREDEXP_VALUE; |
1460 | } |
1461 | |
1462 | static bool |
1463 | build_rec_void_time(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp) |
1464 | { |
1465 | predexp_eval_rec_void_time_t* dp = (predexp_eval_rec_void_time_t *) |
1466 | cf_malloc(sizeof(predexp_eval_rec_void_time_t)); |
1467 | |
1468 | predexp_eval_base_init((predexp_eval_t *) dp, |
1469 | destroy_rec_void_time, |
1470 | eval_rec_void_time, |
1471 | PREDEXP_VALUE_NODE, |
1472 | AS_PARTICLE_TYPE_INTEGER); |
1473 | |
1474 | uint8_t* endp = pp + len; |
1475 | |
1476 | if (pp != endp) { |
1477 | cf_warning(AS_PREDEXP, "build_rec_void_time: msg unaligned" ); |
1478 | goto Failed; |
1479 | } |
1480 | |
1481 | // Success, push ourself onto the stack. |
1482 | dp->base.next = *stackpp; // We point next at the old top. |
1483 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
1484 | |
1485 | cf_debug(AS_PREDEXP, "%p: predexp_rec_void_time()" , stackpp); |
1486 | |
1487 | return true; |
1488 | |
1489 | Failed: |
1490 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1491 | return false; |
1492 | } |
1493 | |
1494 | // ---------------------------------------------------------------- |
1495 | // AS_PREDEXP_REC_DIGEST_MODULO |
1496 | // ---------------------------------------------------------------- |
1497 | |
1498 | typedef struct predexp_eval_rec_digest_modulo_s { |
1499 | predexp_eval_t base; |
1500 | int32_t mod; |
1501 | } predexp_eval_rec_digest_modulo_t; |
1502 | |
1503 | static void |
1504 | destroy_rec_digest_modulo(predexp_eval_t* bp) |
1505 | { |
1506 | predexp_eval_rec_digest_modulo_t* dp = |
1507 | (predexp_eval_rec_digest_modulo_t *) bp; |
1508 | cf_free(dp); |
1509 | } |
1510 | |
1511 | static predexp_retval_t |
1512 | eval_rec_digest_modulo(predexp_eval_t* bp, |
1513 | predexp_args_t* argsp, |
1514 | wrapped_as_bin_t* wbinp) |
1515 | { |
1516 | if (wbinp == NULL) { |
1517 | cf_crash(AS_PREDEXP, |
1518 | "eval_rec_digest_modulo called outside value context" ); |
1519 | } |
1520 | |
1521 | predexp_eval_rec_digest_modulo_t* dp = |
1522 | (predexp_eval_rec_digest_modulo_t *) bp; |
1523 | |
1524 | // We point at the last 4 bytes of the digest. |
1525 | uint32_t* valp = (uint32_t*) &argsp->md->keyd.digest[16]; |
1526 | int64_t digest_modulo = *valp % dp->mod; |
1527 | |
1528 | as_bin_state_set_from_type(&wbinp->bin, AS_PARTICLE_TYPE_INTEGER); |
1529 | as_bin_particle_integer_set(&wbinp->bin, digest_modulo); |
1530 | return PREDEXP_VALUE; |
1531 | } |
1532 | |
1533 | static bool |
1534 | build_rec_digest_modulo(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp) |
1535 | { |
1536 | predexp_eval_rec_digest_modulo_t* dp = (predexp_eval_rec_digest_modulo_t *) |
1537 | cf_malloc(sizeof(predexp_eval_rec_digest_modulo_t)); |
1538 | |
1539 | predexp_eval_base_init((predexp_eval_t *) dp, |
1540 | destroy_rec_digest_modulo, |
1541 | eval_rec_digest_modulo, |
1542 | PREDEXP_VALUE_NODE, |
1543 | AS_PARTICLE_TYPE_INTEGER); |
1544 | |
1545 | uint8_t* endp = pp + len; |
1546 | |
1547 | if (pp + sizeof(int32_t) > endp) { |
1548 | cf_warning(AS_PREDEXP, "build_rec_digest_modulo: msg too short" ); |
1549 | goto Failed; |
1550 | } |
1551 | |
1552 | dp->mod = cf_swap_from_be32(* (int32_t*) pp); |
1553 | pp += sizeof(int32_t); |
1554 | |
1555 | if (pp != endp) { |
1556 | cf_warning(AS_PREDEXP, "build_rec_digest_modulo: msg unaligned" ); |
1557 | goto Failed; |
1558 | } |
1559 | |
1560 | if (dp->mod == 0) { |
1561 | cf_warning(AS_PREDEXP, "build_rec_digest_modulo: zero modulo invalid" ); |
1562 | goto Failed; |
1563 | } |
1564 | |
1565 | // Success, push ourself onto the stack. |
1566 | dp->base.next = *stackpp; // We point next at the old top. |
1567 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
1568 | |
1569 | cf_debug(AS_PREDEXP, "%p: predexp_rec_digest_modulo(%d)" , stackpp, dp->mod); |
1570 | |
1571 | return true; |
1572 | |
1573 | Failed: |
1574 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1575 | return false; |
1576 | } |
1577 | |
1578 | // ---------------------------------------------------------------- |
1579 | // AS_PREDEXP_*_ITERATE_* |
1580 | // ---------------------------------------------------------------- |
1581 | |
1582 | typedef struct predexp_eval_iter_s { |
1583 | predexp_eval_t base; |
1584 | uint16_t tag; |
1585 | uint8_t type; |
1586 | predexp_eval_t* lchild; // per-element expr |
1587 | predexp_eval_t* rchild; // collection |
1588 | char vname[AS_BIN_NAME_MAX_SZ]; |
1589 | } predexp_eval_iter_t; |
1590 | |
1591 | static void |
1592 | destroy_iter(predexp_eval_t* bp) |
1593 | { |
1594 | predexp_eval_iter_t* dp = (predexp_eval_iter_t *) bp; |
1595 | if (dp->lchild) { |
1596 | (*dp->lchild->dtor_fn)(dp->lchild); |
1597 | } |
1598 | if (dp->rchild) { |
1599 | (*dp->rchild->dtor_fn)(dp->rchild); |
1600 | } |
1601 | cf_free(dp); |
1602 | } |
1603 | |
1604 | static predexp_retval_t |
1605 | eval_list_iter(predexp_eval_t* bp, predexp_args_t* argsp, wrapped_as_bin_t* wbinp) |
1606 | { |
1607 | predexp_eval_iter_t* dp = (predexp_eval_iter_t *) bp; |
1608 | |
1609 | predexp_retval_t retval = PREDEXP_UNKNOWN; // init makes compiler happy |
1610 | switch (dp->tag) { |
1611 | case AS_PREDEXP_LIST_ITERATE_OR: |
1612 | // Start pessimistically. |
1613 | retval = PREDEXP_FALSE; |
1614 | break; |
1615 | case AS_PREDEXP_LIST_ITERATE_AND: |
1616 | // Start optimistically. |
1617 | retval = PREDEXP_TRUE; |
1618 | break; |
1619 | default: |
1620 | cf_crash(AS_PREDEXP, |
1621 | "eval_list_iter called with bogus tag: %d" , dp->tag); |
1622 | } |
1623 | |
1624 | wrapped_as_bin_t lwbin; |
1625 | lwbin.must_free = false; |
1626 | if ((*dp->rchild->eval_fn)(dp->rchild, argsp, &lwbin) == |
1627 | PREDEXP_NOVALUE) { |
1628 | return argsp->rd ? PREDEXP_FALSE : PREDEXP_UNKNOWN; |
1629 | } |
1630 | |
1631 | as_predexp_var_t var; |
1632 | memcpy(var.vname, dp->vname, sizeof(var.vname)); |
1633 | |
1634 | // Make sure our var starts out empty. |
1635 | as_bin_set_empty(&var.bin); |
1636 | var.bin.particle = NULL; |
1637 | |
1638 | // Prepend our var to the list. |
1639 | var.next = argsp->vl; |
1640 | argsp->vl = &var; |
1641 | |
1642 | // Traverse the collection. |
1643 | as_val* lval = as_bin_particle_to_asval(&lwbin.bin); |
1644 | as_arraylist* list = (as_arraylist*) as_list_fromval(lval); |
1645 | as_arraylist_iterator it; |
1646 | as_arraylist_iterator_init(&it, list); |
1647 | while (as_arraylist_iterator_has_next(&it)) { |
1648 | // Set our var to the element's value. |
1649 | as_val* val = (as_val*) as_arraylist_iterator_next(&it); |
1650 | int old_arena = cf_alloc_clear_ns_arena(); |
1651 | int rv = as_bin_particle_replace_from_asval(&var.bin, val); |
1652 | cf_alloc_restore_ns_arena(old_arena); |
1653 | if (rv != 0) { |
1654 | cf_warning(AS_PREDEXP, |
1655 | "eval_list_iter: particle from asval failed" ); |
1656 | continue; |
1657 | } |
1658 | |
1659 | switch (dp->tag) { |
1660 | case AS_PREDEXP_LIST_ITERATE_OR: |
1661 | switch ((*dp->lchild->eval_fn)(dp->lchild, argsp, NULL)) { |
1662 | case PREDEXP_TRUE: |
1663 | // Shortcut, skip remaining children. |
1664 | retval = PREDEXP_TRUE; |
1665 | goto Done; |
1666 | case PREDEXP_UNKNOWN: |
1667 | // Upgrade our return value, continue scanning children. |
1668 | retval = PREDEXP_UNKNOWN; |
1669 | break; |
1670 | case PREDEXP_FALSE: |
1671 | // Continue scanning children. |
1672 | break; |
1673 | case PREDEXP_VALUE: |
1674 | case PREDEXP_NOVALUE: |
1675 | // Child can't be value node; shouldn't ever happen. |
1676 | cf_crash(AS_PREDEXP, "eval_list_iter child was value node" ); |
1677 | } |
1678 | break; |
1679 | case AS_PREDEXP_LIST_ITERATE_AND: |
1680 | switch ((*dp->lchild->eval_fn)(dp->lchild, argsp, NULL)) { |
1681 | case PREDEXP_FALSE: |
1682 | // Shortcut, skip remaining children. |
1683 | retval = PREDEXP_FALSE; |
1684 | goto Done; |
1685 | case PREDEXP_UNKNOWN: |
1686 | // Downgrade our return value, continue scanning children. |
1687 | retval = PREDEXP_UNKNOWN; |
1688 | break; |
1689 | case PREDEXP_TRUE: |
1690 | // Continue scanning children. |
1691 | break; |
1692 | case PREDEXP_VALUE: |
1693 | case PREDEXP_NOVALUE: |
1694 | // Child can't be value node; shouldn't ever happen. |
1695 | cf_crash(AS_PREDEXP, "eval_list_iter child was value node" ); |
1696 | } |
1697 | break; |
1698 | default: |
1699 | cf_crash(AS_PREDEXP, "eval_list_iter called with bogus tag: %d" , |
1700 | dp->tag); |
1701 | } |
1702 | |
1703 | } |
1704 | |
1705 | Done: |
1706 | as_bin_particle_destroy(&var.bin, true); |
1707 | as_bin_set_empty(&var.bin); |
1708 | var.bin.particle = NULL; |
1709 | |
1710 | as_arraylist_iterator_destroy(&it); |
1711 | |
1712 | as_val_destroy(lval); |
1713 | |
1714 | // Remove our var from the list. |
1715 | argsp->vl = var.next; |
1716 | |
1717 | if (lwbin.must_free) { |
1718 | cf_crash(AS_PREDEXP, "eval_list_iter need bin cleanup, didn't before" ); |
1719 | } |
1720 | |
1721 | return retval; |
1722 | } |
1723 | |
1724 | static predexp_retval_t |
1725 | eval_map_iter(predexp_eval_t* bp, predexp_args_t* argsp, wrapped_as_bin_t* wbinp) |
1726 | { |
1727 | predexp_eval_iter_t* dp = (predexp_eval_iter_t *) bp; |
1728 | |
1729 | predexp_retval_t retval = PREDEXP_UNKNOWN; // init makes compiler happy |
1730 | switch (dp->tag) { |
1731 | case AS_PREDEXP_MAPKEY_ITERATE_OR: |
1732 | case AS_PREDEXP_MAPVAL_ITERATE_OR: |
1733 | // Start pessimistically. |
1734 | retval = PREDEXP_FALSE; |
1735 | break; |
1736 | case AS_PREDEXP_MAPKEY_ITERATE_AND: |
1737 | case AS_PREDEXP_MAPVAL_ITERATE_AND: |
1738 | // Start optimistically. |
1739 | retval = PREDEXP_TRUE; |
1740 | break; |
1741 | default: |
1742 | cf_crash(AS_PREDEXP, "eval_map_iter called with bogus tag: %d" , |
1743 | dp->tag); |
1744 | } |
1745 | |
1746 | wrapped_as_bin_t lwbin; |
1747 | lwbin.must_free = false; |
1748 | if ((*dp->rchild->eval_fn)(dp->rchild, argsp, &lwbin) == |
1749 | PREDEXP_NOVALUE) { |
1750 | return argsp->rd ? PREDEXP_FALSE : PREDEXP_UNKNOWN; |
1751 | } |
1752 | |
1753 | as_predexp_var_t var; |
1754 | memcpy(var.vname, dp->vname, sizeof(var.vname)); |
1755 | |
1756 | // Make sure our var starts out empty. |
1757 | as_bin_set_empty(&var.bin); |
1758 | var.bin.particle = NULL; |
1759 | |
1760 | // Prepend our var to the list. |
1761 | var.next = argsp->vl; |
1762 | argsp->vl = &var; |
1763 | |
1764 | // Traverse the collection. |
1765 | as_val* mval = as_bin_particle_to_asval(&lwbin.bin); |
1766 | as_hashmap* map = (as_hashmap*) as_map_fromval(mval); |
1767 | as_hashmap_iterator it; |
1768 | as_hashmap_iterator_init(&it, map); |
1769 | while (as_hashmap_iterator_has_next(&it)) { |
1770 | // Set our var to the element's value. |
1771 | as_pair* pair = (as_pair*) as_hashmap_iterator_next(&it); |
1772 | as_val* val = NULL; // init makes compiler happy |
1773 | switch (dp->tag) { |
1774 | case AS_PREDEXP_MAPKEY_ITERATE_OR: |
1775 | case AS_PREDEXP_MAPKEY_ITERATE_AND: |
1776 | val = as_pair_1(pair); |
1777 | break; |
1778 | case AS_PREDEXP_MAPVAL_ITERATE_OR: |
1779 | case AS_PREDEXP_MAPVAL_ITERATE_AND: |
1780 | val = as_pair_2(pair); |
1781 | break; |
1782 | default: |
1783 | cf_crash(AS_PREDEXP, "eval_map_iter called with bogus tag (2): %d" , |
1784 | dp->tag); |
1785 | } |
1786 | |
1787 | int old_arena = cf_alloc_clear_ns_arena(); |
1788 | int rv = as_bin_particle_replace_from_asval(&var.bin, val); |
1789 | cf_alloc_restore_ns_arena(old_arena); |
1790 | if (rv != 0) { |
1791 | cf_warning(AS_PREDEXP, "eval_map_iter: particle from asval failed" ); |
1792 | continue; |
1793 | } |
1794 | |
1795 | switch (dp->tag) { |
1796 | case AS_PREDEXP_MAPKEY_ITERATE_OR: |
1797 | case AS_PREDEXP_MAPVAL_ITERATE_OR: |
1798 | switch ((*dp->lchild->eval_fn)(dp->lchild, argsp, NULL)) { |
1799 | case PREDEXP_TRUE: |
1800 | // Shortcut, skip remaining children. |
1801 | retval = PREDEXP_TRUE; |
1802 | goto Done; |
1803 | case PREDEXP_UNKNOWN: |
1804 | // Upgrade our return value, continue scanning children. |
1805 | retval = PREDEXP_UNKNOWN; |
1806 | break; |
1807 | case PREDEXP_FALSE: |
1808 | // Continue scanning children. |
1809 | break; |
1810 | case PREDEXP_VALUE: |
1811 | case PREDEXP_NOVALUE: |
1812 | // Child can't be value node; shouldn't ever happen. |
1813 | cf_crash(AS_PREDEXP, "eval_map_iter child was value node" ); |
1814 | } |
1815 | break; |
1816 | case AS_PREDEXP_MAPKEY_ITERATE_AND: |
1817 | case AS_PREDEXP_MAPVAL_ITERATE_AND: |
1818 | switch ((*dp->lchild->eval_fn)(dp->lchild, argsp, NULL)) { |
1819 | case PREDEXP_FALSE: |
1820 | // Shortcut, skip remaining children. |
1821 | retval = PREDEXP_FALSE; |
1822 | goto Done; |
1823 | case PREDEXP_UNKNOWN: |
1824 | // Downgrade our return value, continue scanning children. |
1825 | retval = PREDEXP_UNKNOWN; |
1826 | break; |
1827 | case PREDEXP_TRUE: |
1828 | // Continue scanning children. |
1829 | break; |
1830 | case PREDEXP_VALUE: |
1831 | case PREDEXP_NOVALUE: |
1832 | // Child can't be value node; shouldn't ever happen. |
1833 | cf_crash(AS_PREDEXP, "eval_map_iter child was value node" ); |
1834 | } |
1835 | break; |
1836 | default: |
1837 | cf_crash(AS_PREDEXP, "eval_map_iter called with bogus tag: %d" , |
1838 | dp->tag); |
1839 | } |
1840 | |
1841 | } |
1842 | |
1843 | Done: |
1844 | as_bin_particle_destroy(&var.bin, true); |
1845 | as_bin_set_empty(&var.bin); |
1846 | var.bin.particle = NULL; |
1847 | |
1848 | as_hashmap_iterator_destroy(&it); |
1849 | |
1850 | as_val_destroy(mval); |
1851 | |
1852 | // Remove our var from the list. |
1853 | argsp->vl = var.next; |
1854 | |
1855 | if (lwbin.must_free) { |
1856 | cf_crash(AS_PREDEXP, "eval_map_iter need bin cleanup, didn't before" ); |
1857 | } |
1858 | return retval; |
1859 | } |
1860 | |
1861 | static bool |
1862 | build_iter(predexp_eval_t** stackpp, uint32_t len, uint8_t* pp, uint16_t tag) |
1863 | { |
1864 | predexp_eval_iter_t* dp = (predexp_eval_iter_t *) |
1865 | cf_malloc(sizeof(predexp_eval_iter_t)); |
1866 | |
1867 | switch (tag) { |
1868 | case AS_PREDEXP_LIST_ITERATE_OR: |
1869 | case AS_PREDEXP_LIST_ITERATE_AND: |
1870 | predexp_eval_base_init((predexp_eval_t *) dp, |
1871 | destroy_iter, |
1872 | eval_list_iter, |
1873 | 0, |
1874 | AS_PARTICLE_TYPE_NULL); |
1875 | dp->type = AS_PARTICLE_TYPE_LIST; |
1876 | break; |
1877 | case AS_PREDEXP_MAPKEY_ITERATE_OR: |
1878 | case AS_PREDEXP_MAPVAL_ITERATE_OR: |
1879 | case AS_PREDEXP_MAPKEY_ITERATE_AND: |
1880 | case AS_PREDEXP_MAPVAL_ITERATE_AND: |
1881 | predexp_eval_base_init((predexp_eval_t *) dp, |
1882 | destroy_iter, |
1883 | eval_map_iter, |
1884 | 0, |
1885 | AS_PARTICLE_TYPE_NULL); |
1886 | dp->type = AS_PARTICLE_TYPE_MAP; |
1887 | break; |
1888 | default: |
1889 | cf_crash(AS_PREDEXP, "build_iter called with bogus tag: %d" , tag); |
1890 | } |
1891 | |
1892 | dp->tag = tag; |
1893 | dp->lchild = NULL; |
1894 | dp->rchild = NULL; |
1895 | |
1896 | uint8_t* endp = pp + len; |
1897 | |
1898 | if (len >= sizeof(dp->vname)) { |
1899 | cf_warning(AS_PREDEXP, "build_iter: varname too long" ); |
1900 | goto Failed; |
1901 | } |
1902 | uint8_t vnlen = (uint8_t) len; |
1903 | memcpy(dp->vname, pp, vnlen); |
1904 | dp->vname[vnlen] = '\0'; |
1905 | pp += vnlen; |
1906 | |
1907 | // ---- Pop the right child (collection) off the stack. |
1908 | |
1909 | if (! *stackpp) { |
1910 | cf_warning(AS_PREDEXP, "predexp_iterate: missing right child" ); |
1911 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1912 | return false; |
1913 | } |
1914 | |
1915 | dp->rchild = *stackpp; |
1916 | *stackpp = dp->rchild->next; |
1917 | dp->rchild->next = NULL; |
1918 | |
1919 | if ((dp->rchild->flags & PREDEXP_VALUE_NODE) == 0) { |
1920 | cf_warning(AS_PREDEXP, |
1921 | "predexp iterate: right child is not value node" ); |
1922 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1923 | return false; |
1924 | } |
1925 | |
1926 | if (dp->rchild->type != dp->type) { |
1927 | cf_warning(AS_PREDEXP, "predexp iterate: right child is wrong type" ); |
1928 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1929 | return false; |
1930 | } |
1931 | |
1932 | // ---- Pop the left child (per-element expr) off the stack. |
1933 | |
1934 | if (! *stackpp) { |
1935 | cf_warning(AS_PREDEXP, "predexp_iterate: missing left child" ); |
1936 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1937 | return false; |
1938 | } |
1939 | |
1940 | dp->lchild = *stackpp; |
1941 | *stackpp = dp->lchild->next; |
1942 | dp->lchild->next = NULL; |
1943 | |
1944 | if ((dp->lchild->flags & PREDEXP_VALUE_NODE) == 1) { |
1945 | cf_warning(AS_PREDEXP, "predexp iterate: left child is value node" ); |
1946 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1947 | return false; |
1948 | } |
1949 | |
1950 | if (dp->lchild->type != AS_PARTICLE_TYPE_NULL) { |
1951 | cf_warning(AS_PREDEXP, "predexp iterate: left child is wrong type" ); |
1952 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1953 | return false; |
1954 | } |
1955 | |
1956 | if (pp != endp) { |
1957 | cf_warning(AS_PREDEXP, "build_iter: msg unaligned" ); |
1958 | goto Failed; |
1959 | } |
1960 | |
1961 | // Success, push ourself onto the stack. |
1962 | dp->base.next = *stackpp; // We point next at the old top. |
1963 | *stackpp = (predexp_eval_t *) dp; // We're the new top |
1964 | |
1965 | switch (tag) { |
1966 | case AS_PREDEXP_LIST_ITERATE_OR: |
1967 | cf_debug(AS_PREDEXP, "%p: predexp_list_iterate_or()" , stackpp); |
1968 | break; |
1969 | case AS_PREDEXP_LIST_ITERATE_AND: |
1970 | cf_debug(AS_PREDEXP, "%p: predexp_list_iterate_and()" , stackpp); |
1971 | break; |
1972 | case AS_PREDEXP_MAPKEY_ITERATE_OR: |
1973 | cf_debug(AS_PREDEXP, "%p: predexp_mapkey_iterate_or()" , stackpp); |
1974 | break; |
1975 | case AS_PREDEXP_MAPVAL_ITERATE_OR: |
1976 | cf_debug(AS_PREDEXP, "%p: predexp_mapval_iterate_or()" , stackpp); |
1977 | break; |
1978 | case AS_PREDEXP_MAPKEY_ITERATE_AND: |
1979 | cf_debug(AS_PREDEXP, "%p: predexp_mapkey_iterate_and()" , stackpp); |
1980 | break; |
1981 | case AS_PREDEXP_MAPVAL_ITERATE_AND: |
1982 | cf_debug(AS_PREDEXP, "%p: predexp_mapval_iterate_and()" , stackpp); |
1983 | break; |
1984 | default: |
1985 | cf_crash(AS_PREDEXP, "build_iter called with bogus tag: %d" , tag); |
1986 | } |
1987 | |
1988 | return true; |
1989 | |
1990 | Failed: |
1991 | (*dp->base.dtor_fn)((predexp_eval_t *) dp); |
1992 | return false; |
1993 | } |
1994 | |
1995 | // ---------------------------------------------------------------- |
1996 | // External Interface |
1997 | // ---------------------------------------------------------------- |
1998 | |
1999 | |
2000 | static bool |
2001 | build(predexp_eval_t** stackpp, uint16_t tag, uint32_t len, uint8_t* pp) |
2002 | { |
2003 | switch (tag) { |
2004 | case AS_PREDEXP_AND: |
2005 | return build_and(stackpp, len, pp); |
2006 | case AS_PREDEXP_OR: |
2007 | return build_or(stackpp, len, pp); |
2008 | case AS_PREDEXP_NOT: |
2009 | return build_not(stackpp, len, pp); |
2010 | case AS_PREDEXP_INTEGER_EQUAL: |
2011 | case AS_PREDEXP_INTEGER_UNEQUAL: |
2012 | case AS_PREDEXP_INTEGER_GREATER: |
2013 | case AS_PREDEXP_INTEGER_GREATEREQ: |
2014 | case AS_PREDEXP_INTEGER_LESS: |
2015 | case AS_PREDEXP_INTEGER_LESSEQ: |
2016 | case AS_PREDEXP_STRING_EQUAL: |
2017 | case AS_PREDEXP_STRING_UNEQUAL: |
2018 | case AS_PREDEXP_STRING_REGEX: |
2019 | case AS_PREDEXP_GEOJSON_WITHIN: |
2020 | case AS_PREDEXP_GEOJSON_CONTAINS: |
2021 | return build_compare(stackpp, len, pp, tag); |
2022 | case AS_PREDEXP_INTEGER_VALUE: |
2023 | case AS_PREDEXP_STRING_VALUE: |
2024 | case AS_PREDEXP_GEOJSON_VALUE: |
2025 | return build_value(stackpp, len, pp, tag); |
2026 | case AS_PREDEXP_INTEGER_BIN: |
2027 | case AS_PREDEXP_STRING_BIN: |
2028 | case AS_PREDEXP_GEOJSON_BIN: |
2029 | case AS_PREDEXP_LIST_BIN: |
2030 | case AS_PREDEXP_MAP_BIN: |
2031 | return build_bin(stackpp, len, pp, tag); |
2032 | case AS_PREDEXP_INTEGER_VAR: |
2033 | case AS_PREDEXP_STRING_VAR: |
2034 | case AS_PREDEXP_GEOJSON_VAR: |
2035 | return build_var(stackpp, len, pp, tag); |
2036 | case AS_PREDEXP_REC_DEVICE_SIZE: |
2037 | return build_rec_device_size(stackpp, len, pp); |
2038 | case AS_PREDEXP_REC_LAST_UPDATE: |
2039 | return build_rec_last_update(stackpp, len, pp); |
2040 | case AS_PREDEXP_REC_VOID_TIME: |
2041 | return build_rec_void_time(stackpp, len, pp); |
2042 | case AS_PREDEXP_REC_DIGEST_MODULO: |
2043 | return build_rec_digest_modulo(stackpp, len, pp); |
2044 | case AS_PREDEXP_LIST_ITERATE_OR: |
2045 | case AS_PREDEXP_LIST_ITERATE_AND: |
2046 | case AS_PREDEXP_MAPKEY_ITERATE_OR: |
2047 | case AS_PREDEXP_MAPKEY_ITERATE_AND: |
2048 | case AS_PREDEXP_MAPVAL_ITERATE_OR: |
2049 | case AS_PREDEXP_MAPVAL_ITERATE_AND: |
2050 | return build_iter(stackpp, len, pp, tag); |
2051 | default: |
2052 | cf_warning(AS_PREDEXP, "unexpected predexp tag: %d" , tag); |
2053 | return false; |
2054 | } |
2055 | } |
2056 | |
2057 | predexp_eval_t* |
2058 | predexp_build(as_msg_field* pfp) |
2059 | { |
2060 | predexp_eval_t* stackp = NULL; |
2061 | |
2062 | cf_debug(AS_PREDEXP, "%p: predexp_build starting" , &stackp); |
2063 | |
2064 | uint8_t* pp = pfp->data; |
2065 | uint32_t pdsize = as_msg_field_get_value_sz(pfp); |
2066 | uint8_t* endp = pp + pdsize; |
2067 | |
2068 | // Minumum possible TLV token is 6 bytes. |
2069 | while (pp + 6 <= endp) { |
2070 | uint16_t tag = cf_swap_from_be16(* (uint16_t *) pp); |
2071 | pp += sizeof(uint16_t); |
2072 | |
2073 | uint32_t len = cf_swap_from_be32(* (uint32_t *) pp); |
2074 | pp += sizeof(uint32_t); |
2075 | |
2076 | if (pp + len > endp) { |
2077 | cf_warning(AS_PREDEXP, "malformed predexp field" ); |
2078 | goto FAILED; |
2079 | } |
2080 | |
2081 | if (!build(&stackp, tag, len, pp)) { |
2082 | // Warning should already have happened |
2083 | goto FAILED; |
2084 | } |
2085 | pp += len; |
2086 | } |
2087 | |
2088 | // The cursor needs to neatly point at the end pointer. |
2089 | if (pp != endp) { |
2090 | cf_warning(AS_PREDEXP, "malformed predexp field" ); |
2091 | goto FAILED; |
2092 | } |
2093 | |
2094 | // We'd better have exactly one node on the stack now. |
2095 | if (!stackp) { |
2096 | cf_warning(AS_PREDEXP, "no top level predexp" ); |
2097 | goto FAILED; |
2098 | } |
2099 | if (stackp->next) { |
2100 | cf_warning(AS_PREDEXP, "multiple top-level predexp" ); |
2101 | goto FAILED; |
2102 | } |
2103 | |
2104 | // The top node needs to be a matching node, not a value node. |
2105 | if (stackp->flags & PREDEXP_VALUE_NODE) { |
2106 | cf_warning(AS_PREDEXP, "top-level predexp is value node" ); |
2107 | goto FAILED; |
2108 | } |
2109 | |
2110 | cf_debug(AS_PREDEXP, "%p: predexp_build finished" , &stackp); |
2111 | |
2112 | // Return the root of the predicate expression tree. |
2113 | return stackp; |
2114 | |
2115 | FAILED: |
2116 | cf_debug(AS_PREDEXP, "%p: predexp_build failed" , &stackp); |
2117 | destroy_list(stackp); |
2118 | return NULL; |
2119 | } |
2120 | |
2121 | predexp_retval_t |
2122 | predexp_matches_metadata(predexp_eval_t* bp, predexp_args_t* argsp) |
2123 | { |
2124 | if (bp == NULL) { |
2125 | return PREDEXP_TRUE; |
2126 | } |
2127 | |
2128 | predexp_retval_t result = (*bp->eval_fn)(bp, argsp, NULL); |
2129 | |
2130 | switch (result) { |
2131 | case PREDEXP_TRUE: |
2132 | case PREDEXP_FALSE: |
2133 | case PREDEXP_UNKNOWN: |
2134 | return result; |
2135 | default: |
2136 | cf_crash(AS_PREDEXP, "predexp_matches_metadata returned other than true/false/unknown" ); |
2137 | return result; // makes compiler happy |
2138 | } |
2139 | } |
2140 | |
2141 | bool |
2142 | predexp_matches_record(predexp_eval_t* bp, predexp_args_t* argsp) |
2143 | { |
2144 | if (bp == NULL) { |
2145 | return true; |
2146 | } |
2147 | |
2148 | switch ((*bp->eval_fn)(bp, argsp, NULL)) { |
2149 | case PREDEXP_TRUE: |
2150 | return true; |
2151 | case PREDEXP_FALSE: |
2152 | return false; |
2153 | default: |
2154 | cf_crash(AS_PREDEXP, "predexp_matches_record returned other than true/false with record data present" ); |
2155 | return false; // makes compiler happy |
2156 | } |
2157 | } |
2158 | |
2159 | void |
2160 | predexp_destroy(predexp_eval_t* bp) |
2161 | { |
2162 | if (bp == NULL) { |
2163 | return; |
2164 | } |
2165 | |
2166 | (*bp->dtor_fn)(bp); |
2167 | } |
2168 | |