/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2010-2012, RedJack, LLC. * All rights reserved. * * Please see the LICENSE.txt file in this distribution for license * details. * ---------------------------------------------------------------------- */ #include #include #include "ipset/bdd/nodes.h" #include "ipset/bits.h" #include "ipset/logging.h" static void initialize(struct ipset_expanded_assignment *exp, const struct ipset_assignment *assignment, ipset_variable var_count) { /* First loop through all of the variables in the assignment vector, * making sure not to go further than the caller requested. */ ipset_variable last_assignment = cork_array_size(&assignment->values); if (var_count < last_assignment) { last_assignment = var_count; } ipset_variable var; for (var = 0; var < last_assignment; var++) { enum ipset_tribool curr_value = cork_array_at(&assignment->values, var); if (curr_value == IPSET_EITHER) { /* If this variable is EITHER, start it off as FALSE, and * add it to the eithers list. */ DEBUG("Variable %u is EITHER", var); IPSET_BIT_SET(exp->values.buf, var, false); cork_array_append(&exp->eithers, var); } else { /* Otherwise set the variable to the same value in the * expanded assignment as it is in the non-expanded one. */ DEBUG("Variable %u is %s", var, curr_value? "true": "false"); IPSET_BIT_SET(exp->values.buf, var, curr_value); } } /* If the caller requested more variables than there are in the * assignment vector, add them to the eithers list. */ for (var = last_assignment; var < var_count; var++) { DEBUG("Variable %u is implicitly EITHER", var); cork_array_append(&exp->eithers, var); } } struct ipset_expanded_assignment * ipset_assignment_expand(const struct ipset_assignment *assignment, ipset_variable var_count) { /* First allocate the iterator itself, and all of its contained * fields. */ struct ipset_expanded_assignment *exp; unsigned int values_size = (var_count / 8) + ((var_count % 8) != 0); exp = cork_new(struct ipset_expanded_assignment); exp->finished = false; cork_buffer_init(&exp->values); cork_buffer_ensure_size(&exp->values, values_size); memset(exp->values.buf, 0, values_size); cork_array_init(&exp->eithers); /* Then initialize the values and eithers fields. */ initialize(exp, assignment, var_count); return exp; } void ipset_expanded_assignment_free(struct ipset_expanded_assignment *exp) { if (exp == NULL) { return; } cork_buffer_done(&exp->values); cork_array_done(&exp->eithers); free(exp); } void ipset_expanded_assignment_advance(struct ipset_expanded_assignment *exp) { /* If we're already at the end of the iterator, don't do anything. */ if (CORK_UNLIKELY(exp->finished)) { return; } DEBUG("Advancing iterator"); /* Look at the last EITHER bit in the assignment. If it's 0, then * set it to 1 and return. Otherwise we set it to 0 and carry up to * the previous indeterminate bit. */ size_t i; for (i = cork_array_size(&exp->eithers); i > 0; i--) { size_t idx = i - 1; ipset_variable either_var = cork_array_at(&exp->eithers, idx); DEBUG("Checking EITHER variable %u", either_var); if (IPSET_BIT_GET(exp->values.buf, either_var)) { /* This variable is currently true, so set it back to false * and carry. */ DEBUG(" Variable %u is true, changing to false and carrying", either_var); IPSET_BIT_SET(exp->values.buf, either_var, false); } else { /* This variable is currently false, so set it to true and * return. */ DEBUG(" Variable %u is false, changing to true", either_var); IPSET_BIT_SET(exp->values.buf, either_var, true); return; } } /* If we fall through then we've made it through all of the expanded * assignments. */ exp->finished = true; }