- Added coloring option to graph saving.
[oweals/gnunet.git] / src / regex / regex.c
index a1619ef28bd9ca8bff8ec29eacbf7343b1e25c65..c8b8ad3faedb680268ff3256c91ea8b868a9a449 100644 (file)
@@ -32,7 +32,7 @@
 /**
  * Constant for how many bits the initial string regex should have.
  */
-#define INITIAL_BITS 10
+#define INITIAL_BITS 8
 
 
 /**
@@ -287,7 +287,8 @@ state_compare (const void *a, const void *b)
  * Get all edges leaving state 's'.
  *
  * @param s state.
- * @param edges all edges leaving 's'.
+ * @param edges all edges leaving 's', expected to be allocated and have enough
+ *        space for s->transitions_count elements.
  *
  * @return number of edges.
  */
@@ -765,7 +766,8 @@ number_states (void *cls, unsigned int count, struct GNUNET_REGEX_State *s)
   struct GNUNET_REGEX_State **states = cls;
 
   s->proof_id = count;
-  states[count] = s;
+  if (NULL != states)
+    states[count] = s;
 }
 
 
@@ -1419,7 +1421,7 @@ dfa_remove_unreachable_states (struct GNUNET_REGEX_Automaton *a)
 
 /**
  * Remove all dead states from the DFA 'a'. Dead states are those states that do
- * not transition to any other state but themselfes.
+ * not transition to any other state but themselves.
  *
  * @param a DFA automaton
  */
@@ -1599,7 +1601,7 @@ nfa_fragment_create (struct GNUNET_REGEX_State *start,
   n->start = NULL;
   n->end = NULL;
 
-  if (NULL == start && NULL == end)
+  if (NULL == start || NULL == end)
     return n;
 
   automaton_add_state (n, end);
@@ -1812,26 +1814,28 @@ nfa_add_concatenation (struct GNUNET_REGEX_Context *ctx)
 {
   struct GNUNET_REGEX_Automaton *a;
   struct GNUNET_REGEX_Automaton *b;
-  struct GNUNET_REGEX_Automaton *new;
+  struct GNUNET_REGEX_Automaton *new_nfa;
 
   b = ctx->stack_tail;
+  GNUNET_assert (NULL != b);
   GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, b);
   a = ctx->stack_tail;
+  GNUNET_assert (NULL != a);
   GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
 
   state_add_transition (ctx, a->end, 0, b->start);
   a->end->accepting = 0;
   b->end->accepting = 1;
 
-  new = nfa_fragment_create (NULL, NULL);
-  nfa_add_states (new, a->states_head, a->states_tail);
-  nfa_add_states (new, b->states_head, b->states_tail);
-  new->start = a->start;
-  new->end = b->end;
+  new_nfa = nfa_fragment_create (NULL, NULL);
+  nfa_add_states (new_nfa, a->states_head, a->states_tail);
+  nfa_add_states (new_nfa, b->states_head, b->states_tail);
+  new_nfa->start = a->start;
+  new_nfa->end = b->end;
   automaton_fragment_clear (a);
   automaton_fragment_clear (b);
 
-  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new);
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa);
 }
 
 
@@ -1844,12 +1848,11 @@ static void
 nfa_add_star_op (struct GNUNET_REGEX_Context *ctx)
 {
   struct GNUNET_REGEX_Automaton *a;
-  struct GNUNET_REGEX_Automaton *new;
+  struct GNUNET_REGEX_Automaton *new_nfa;
   struct GNUNET_REGEX_State *start;
   struct GNUNET_REGEX_State *end;
 
   a = ctx->stack_tail;
-  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
 
   if (NULL == a)
   {
@@ -1858,6 +1861,8 @@ nfa_add_star_op (struct GNUNET_REGEX_Context *ctx)
     return;
   }
 
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
   start = nfa_state_create (ctx, 0);
   end = nfa_state_create (ctx, 1);
 
@@ -1869,11 +1874,11 @@ nfa_add_star_op (struct GNUNET_REGEX_Context *ctx)
   a->end->accepting = 0;
   end->accepting = 1;
 
-  new = nfa_fragment_create (start, end);
-  nfa_add_states (new, a->states_head, a->states_tail);
+  new_nfa = nfa_fragment_create (start, end);
+  nfa_add_states (new_nfa, a->states_head, a->states_tail);
   automaton_fragment_clear (a);
 
-  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new);
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa);
 }
 
 
@@ -1905,12 +1910,11 @@ static void
 nfa_add_question_op (struct GNUNET_REGEX_Context *ctx)
 {
   struct GNUNET_REGEX_Automaton *a;
-  struct GNUNET_REGEX_Automaton *new;
+  struct GNUNET_REGEX_Automaton *new_nfa;
   struct GNUNET_REGEX_State *start;
   struct GNUNET_REGEX_State *end;
 
   a = ctx->stack_tail;
-  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
 
   if (NULL == a)
   {
@@ -1919,6 +1923,8 @@ nfa_add_question_op (struct GNUNET_REGEX_Context *ctx)
     return;
   }
 
+  GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
+
   start = nfa_state_create (ctx, 0);
   end = nfa_state_create (ctx, 1);
 
@@ -1928,11 +1934,11 @@ nfa_add_question_op (struct GNUNET_REGEX_Context *ctx)
 
   a->end->accepting = 0;
 
-  new = nfa_fragment_create (start, end);
-  nfa_add_states (new, a->states_head, a->states_tail);
+  new_nfa = nfa_fragment_create (start, end);
+  nfa_add_states (new_nfa, a->states_head, a->states_tail);
   automaton_fragment_clear (a);
 
-  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new);
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa);
 }
 
 
@@ -1947,13 +1953,15 @@ nfa_add_alternation (struct GNUNET_REGEX_Context *ctx)
 {
   struct GNUNET_REGEX_Automaton *a;
   struct GNUNET_REGEX_Automaton *b;
-  struct GNUNET_REGEX_Automaton *new;
+  struct GNUNET_REGEX_Automaton *new_nfa;
   struct GNUNET_REGEX_State *start;
   struct GNUNET_REGEX_State *end;
 
   b = ctx->stack_tail;
+  GNUNET_assert (NULL != b);
   GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, b);
   a = ctx->stack_tail;
+  GNUNET_assert (NULL != a);
   GNUNET_CONTAINER_DLL_remove (ctx->stack_head, ctx->stack_tail, a);
 
   start = nfa_state_create (ctx, 0);
@@ -1968,13 +1976,13 @@ nfa_add_alternation (struct GNUNET_REGEX_Context *ctx)
   b->end->accepting = 0;
   end->accepting = 1;
 
-  new = nfa_fragment_create (start, end);
-  nfa_add_states (new, a->states_head, a->states_tail);
-  nfa_add_states (new, b->states_head, b->states_tail);
+  new_nfa = nfa_fragment_create (start, end);
+  nfa_add_states (new_nfa, a->states_head, a->states_tail);
+  nfa_add_states (new_nfa, b->states_head, b->states_tail);
   automaton_fragment_clear (a);
   automaton_fragment_clear (b);
 
-  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new);
+  GNUNET_CONTAINER_DLL_insert_tail (ctx->stack_head, ctx->stack_tail, new_nfa);
 }
 
 
@@ -2132,6 +2140,7 @@ GNUNET_REGEX_construct_nfa (const char *regex, const size_t len)
     case 92:                   /* escape: \ */
       regexp++;
       count++;
+      /* fall through! */
     default:
       if (atomcount > 1)
       {
@@ -2166,6 +2175,9 @@ GNUNET_REGEX_construct_nfa (const char *regex, const size_t len)
 
   nfa->regex = GNUNET_strdup (regex);
 
+  /* create depth-first numbering of the states for pretty printing */
+  GNUNET_REGEX_automaton_traverse (nfa, &number_states, NULL);
+
   return nfa;
 
 error:
@@ -2390,7 +2402,6 @@ evaluate_nfa (struct GNUNET_REGEX_Automaton *a, const char *string)
     return 0;
 
   result = 1;
-  strp = string;
   sset = nfa_closure_create (a, a->start, 0);
 
   for (strp = string; NULL != strp && *strp; strp++)
@@ -2471,7 +2482,7 @@ GNUNET_REGEX_get_canonical_regex (struct GNUNET_REGEX_Automaton *a)
 
 /**
  * Get the first key for the given 'input_string'. This hashes the first x bits
- * of the 'input_strings'.
+ * of the 'input_string'.
  *
  * @param input_string string.
  * @param string_len length of the 'input_string'.
@@ -2480,9 +2491,9 @@ GNUNET_REGEX_get_canonical_regex (struct GNUNET_REGEX_Automaton *a)
  * @return number of bits of 'input_string' that have been consumed
  *         to construct the key
  */
-unsigned int
-GNUNET_REGEX_get_first_key (const char *input_string, unsigned int string_len,
-                            struct GNUNET_HashCode *key)
+size_t
+GNUNET_REGEX_get_first_key (const char *input_string, size_t string_len,
+                            struct GNUNET_HashCode * key)
 {
   unsigned int size;
 
@@ -2503,15 +2514,135 @@ GNUNET_REGEX_get_first_key (const char *input_string, unsigned int string_len,
 /**
  * Check if the given 'proof' matches the given 'key'.
  *
- * @param proof partial regex
- * @param key hash
+ * @param proof partial regex of a state.
+ * @param key hash of a state.
  *
- * @return GNUNET_OK if the proof is valid for the given key
+ * @return GNUNET_OK if the proof is valid for the given key.
  */
 int
 GNUNET_REGEX_check_proof (const char *proof, const struct GNUNET_HashCode *key)
 {
-  return GNUNET_OK;
+  struct GNUNET_HashCode key_check;
+
+  GNUNET_CRYPTO_hash (proof, strlen (proof), &key_check);
+  return (0 ==
+          GNUNET_CRYPTO_hash_cmp (key, &key_check)) ? GNUNET_OK : GNUNET_NO;
+}
+
+
+/**
+ * Recursive helper function for iterate_initial_edges. Will call iterator
+ * function for each initial state.
+ *
+ * @param min_len minimum length of the path in the graph.
+ * @param max_len maximum length of the path in the graph.
+ * @param cur_len current length of the path already traversed.
+ * @param consumed_string string consumed by traversing the graph till this state.
+ * @param state current state of the automaton.
+ * @param iterator iterator function called for each edge.
+ * @param iterator_cls closure for the iterator function.
+ */
+static void
+iterate_initial_edge (const unsigned int min_len, const unsigned int max_len,
+                      unsigned int cur_len, char *consumed_string,
+                      struct GNUNET_REGEX_State *state,
+                      GNUNET_REGEX_KeyIterator iterator, void *iterator_cls)
+{
+  unsigned int i;
+  char label[state->transition_count][2];
+  char *temp;
+  struct GNUNET_REGEX_Transition *t;
+  unsigned int num_edges = state->transition_count;
+  struct GNUNET_REGEX_Edge edges[num_edges];
+  struct GNUNET_HashCode hash;
+
+  if (cur_len > min_len && NULL != consumed_string && cur_len <= max_len)
+  {
+    for (i = 0, t = state->transitions_head; NULL != t; t = t->next, i++)
+    {
+      label[i][0] = t->label;
+      label[i][1] = '\0';
+      edges[i].label = label[i];
+      edges[i].destination = t->to_state->hash;
+    }
+
+    GNUNET_CRYPTO_hash (consumed_string, strlen (consumed_string), &hash);
+    iterator (iterator_cls, &hash, consumed_string, state->accepting, num_edges,
+              edges);
+  }
+
+  if (cur_len < max_len)
+  {
+    cur_len++;
+    for (t = state->transitions_head; NULL != t; t = t->next)
+    {
+      if (NULL != consumed_string)
+        GNUNET_asprintf (&temp, "%s%c", consumed_string, t->label);
+      else
+        GNUNET_asprintf (&temp, "%c", t->label);
+
+      iterate_initial_edge (min_len, max_len, cur_len, temp, t->to_state,
+                            iterator, iterator_cls);
+      GNUNET_free (temp);
+    }
+  }
+}
+
+
+/**
+ * Iterate over all initial edges that aren't actually part of the automaton.
+ * This is needed to find the initial states returned by
+ * GNUNET_REGEX_get_first_key. Iteration will start at the first branch state (a
+ * state that has more than one outgoing edge, can be the first state), because
+ * all previous states will have the same proof and be iterated over in
+ * iterate_all_edges.
+ *
+ * @param a the automaton for which the initial states should be computed.
+ * @param initial_len length of the initial state string.
+ * @param iterator iterator function called for each edge.
+ * @param iterator_cls closure for the iterator function.
+ */
+void
+iterate_initial_edges (struct GNUNET_REGEX_Automaton *a,
+                       const unsigned int initial_len,
+                       GNUNET_REGEX_KeyIterator iterator, void *iterator_cls)
+{
+  char *consumed_string;
+  char *temp;
+  struct GNUNET_REGEX_State *s;
+  unsigned int cur_len;
+
+  if (1 > initial_len)
+    return;
+
+  consumed_string = NULL;
+  s = a->start;
+  cur_len = 0;
+
+  if (1 == s->transition_count)
+  {
+    do
+    {
+      if (NULL != consumed_string)
+      {
+        temp = consumed_string;
+        GNUNET_asprintf (&consumed_string, "%s%c", consumed_string,
+                         s->transitions_head->label);
+        GNUNET_free (temp);
+      }
+      else
+        GNUNET_asprintf (&consumed_string, "%c", s->transitions_head->label);
+
+      s = s->transitions_head->to_state;
+      cur_len++;
+    }
+    while (cur_len < initial_len && 1 == s->transition_count);
+  }
+
+  iterate_initial_edge (cur_len, initial_len, cur_len, consumed_string, s,
+                        iterator, iterator_cls);
+
+  GNUNET_free_non_null (consumed_string);
 }
 
 
@@ -2537,7 +2668,9 @@ iterate_edge (struct GNUNET_REGEX_State *s, GNUNET_REGEX_KeyIterator iterator,
 
     num_edges = state_get_edges (s, edges);
 
-    iterator (iterator_cls, &s->hash, s->proof, s->accepting, num_edges, edges);
+    if (0 < strlen (s->proof) || s->accepting)
+      iterator (iterator_cls, &s->hash, s->proof, s->accepting, num_edges,
+                edges);
 
     for (t = s->transitions_head; NULL != t; t = t->next)
       iterate_edge (t->to_state, iterator, iterator_cls);
@@ -2563,5 +2696,6 @@ GNUNET_REGEX_iterate_all_edges (struct GNUNET_REGEX_Automaton *a,
   for (s = a->states_head; NULL != s; s = s->next)
     s->marked = GNUNET_NO;
 
+  iterate_initial_edges (a, INITIAL_BITS, iterator, iterator_cls);
   iterate_edge (a->start, iterator, iterator_cls);
 }