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
44typedef 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.
50typedef void (*predexp_eval_dtor_fn)(predexp_eval_t* bp);
51
52typedef 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
62struct 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
70struct 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
78extern const as_particle_vtable *particle_vtable[];
79
80#if 0
81static void predexp_eval_base_dtor(predexp_eval_t* bp)
82{
83 cf_free(bp);
84}
85#endif
86
87static 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
104static void
105destroy_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
168typedef struct {
169 predexp_eval_t base;
170 predexp_eval_t* child;
171} predexp_eval_and_t;
172
173static void
174destroy_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
181static predexp_retval_t
182eval_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
213static bool
214build_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
270typedef struct {
271 predexp_eval_t base;
272 predexp_eval_t* child;
273} predexp_eval_or_t;
274
275static void
276destroy_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
283static predexp_retval_t
284eval_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
314static bool
315build_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
369typedef struct {
370 predexp_eval_t base;
371 predexp_eval_t* child;
372} predexp_eval_not_t;
373
374static void
375destroy_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
382static predexp_retval_t
383eval_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
405static bool
406build_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
482typedef struct predexp_eval_geojson_state_s {
483 uint64_t cellid;
484 geo_region_t region;
485} predexp_eval_geojson_state_t;
486
487typedef struct predexp_eval_regex_state_s {
488 regex_t regex;
489 bool iscompiled;
490} predexp_eval_regex_state_t;
491
492typedef 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
504static void
505destroy_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
523static predexp_retval_t
524eval_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
650static bool
651build_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
907typedef struct predexp_eval_value_s {
908 predexp_eval_t base;
909 as_bin bin;
910 uint8_t type;
911} predexp_eval_value_t;
912
913static void
914destroy_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
921static predexp_retval_t
922eval_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
936static bool
937build_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
1040typedef 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
1046static void
1047destroy_bin(predexp_eval_t* bp)
1048{
1049 predexp_eval_bin_t* dp = (predexp_eval_bin_t *) bp;
1050 cf_free(dp);
1051}
1052
1053static predexp_retval_t
1054eval_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
1081static bool
1082build_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
1171typedef 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
1177static void
1178destroy_var(predexp_eval_t* bp)
1179{
1180 predexp_eval_var_t* dp = (predexp_eval_var_t *) bp;
1181 cf_free(dp);
1182}
1183
1184static predexp_retval_t
1185eval_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
1211static bool
1212build_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
1287typedef struct predexp_eval_rec_device_size_s {
1288 predexp_eval_t base;
1289} predexp_eval_rec_device_size_t;
1290
1291static void
1292destroy_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
1298static predexp_retval_t
1299eval_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
1319static bool
1320build_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
1355typedef struct predexp_eval_rec_last_update_s {
1356 predexp_eval_t base;
1357 as_bin bin;
1358} predexp_eval_rec_last_update_t;
1359
1360static void
1361destroy_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
1367static predexp_retval_t
1368eval_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
1388static bool
1389build_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
1424typedef struct predexp_eval_rec_void_time_s {
1425 predexp_eval_t base;
1426 as_bin bin;
1427} predexp_eval_rec_void_time_t;
1428
1429static void
1430destroy_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
1436static predexp_retval_t
1437eval_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
1462static bool
1463build_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
1498typedef struct predexp_eval_rec_digest_modulo_s {
1499 predexp_eval_t base;
1500 int32_t mod;
1501} predexp_eval_rec_digest_modulo_t;
1502
1503static void
1504destroy_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
1511static predexp_retval_t
1512eval_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
1533static bool
1534build_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
1582typedef 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
1591static void
1592destroy_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
1604static predexp_retval_t
1605eval_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
1724static predexp_retval_t
1725eval_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
1861static bool
1862build_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
2000static bool
2001build(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
2057predexp_eval_t*
2058predexp_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
2121predexp_retval_t
2122predexp_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
2141bool
2142predexp_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
2159void
2160predexp_destroy(predexp_eval_t* bp)
2161{
2162 if (bp == NULL) {
2163 return;
2164 }
2165
2166 (*bp->dtor_fn)(bp);
2167}
2168