static u32 depth_to_u32(const depth &d) { assert(d.is_reachable()); if (d.is_infinite()) { return REPEAT_INF; } u32 d_val = d; assert(d_val < REPEAT_INF); return d_val; }
enum RepeatType chooseRepeatType(const depth &repeatMin, const depth &repeatMax, u32 minPeriod, bool is_reset) { if (repeatMax.is_infinite()) { return REPEAT_FIRST; } if (repeatMin == depth(0) || is_reset) { return REPEAT_LAST; } // Cases with max < 64 can be handled with either bitmap or trailer. We use // whichever has smaller packed state. if (repeatMax < depth(64)) { u32 bitmap_len = packedSize(REPEAT_BITMAP, repeatMin, repeatMax, minPeriod); u32 trailer_len = packedSize(REPEAT_TRAILER, repeatMin, repeatMax, minPeriod); return bitmap_len <= trailer_len ? REPEAT_BITMAP : REPEAT_TRAILER; } if (repeatMin <= depth(64)) { return REPEAT_TRAILER; } u32 range_len = ~0U; if (repeatMax > repeatMin && numRangeSlots(repeatMin, repeatMax) <= REPEAT_RANGE_MAX_SLOTS) { assert(numRangeSlots(repeatMin, repeatMax) < 256); // stored in u8 range_len = streamStateSize(REPEAT_RANGE, repeatMin, repeatMax, minPeriod); } assert(repeatMax.is_finite()); u32 sparse_len = ~0U; if (minPeriod > 6) { sparse_len = streamStateSize(REPEAT_SPARSE_OPTIMAL_P, repeatMin, repeatMax, minPeriod); } if (range_len != ~0U || sparse_len != ~0U) { return range_len < sparse_len ? REPEAT_RANGE : REPEAT_SPARSE_OPTIMAL_P; } return REPEAT_RING; }
/** If the pattern is unanchored, has a max_offset and has not asked for SOM, * we can use that knowledge to anchor it which will limit its lifespan. Note * that we can't use this transformation if there's a min_length, as it's * currently handled using "sly SOM". * * Note that it is possible to handle graphs that have a combination of * anchored and unanchored paths, but it's too tricky for the moment. */ static bool anchorPatternWithBoundedRepeat(NGWrapper &g, const depth &minWidth, const depth &maxWidth) { assert(!g.som); assert(g.max_offset != MAX_OFFSET); assert(minWidth <= maxWidth); assert(maxWidth.is_reachable()); DEBUG_PRINTF("widths=[%s,%s], min/max offsets=[%llu,%llu]\n", minWidth.str().c_str(), maxWidth.str().c_str(), g.min_offset, g.max_offset); if (g.max_offset > MAX_MAXOFFSET_TO_ANCHOR) { return false; } if (g.max_offset < minWidth) { assert(0); return false; } // If the pattern has virtual starts, we probably don't want to touch it. if (hasVirtualStarts(g)) { DEBUG_PRINTF("virtual starts, bailing\n"); return false; } // Similarly, bail if the pattern is vacuous. TODO: this could be done, we // would just need to be a little careful with reports. if (isVacuous(g)) { DEBUG_PRINTF("vacuous, bailing\n"); return false; } u32 min_bound, max_bound; if (maxWidth.is_infinite()) { min_bound = 0; max_bound = g.max_offset - minWidth; } else { min_bound = g.min_offset > maxWidth ? g.min_offset - maxWidth : 0; max_bound = g.max_offset - minWidth; } DEBUG_PRINTF("prepending ^.{%u,%u}\n", min_bound, max_bound); vector<NFAVertex> initials; for (auto v : adjacent_vertices_range(g.startDs, g)) { if (v == g.startDs) { continue; } initials.push_back(v); } if (initials.empty()) { DEBUG_PRINTF("no initial vertices\n"); return false; } // Wire up 'min_offset' mandatory dots from anchored start. NFAVertex u = g.start; for (u32 i = 0; i < min_bound; i++) { NFAVertex v = add_vertex(g); g[v].char_reach.setall(); add_edge(u, v, g); u = v; } NFAVertex head = u; // Wire up optional dots for (max_offset - min_offset). for (u32 i = 0; i < max_bound - min_bound; i++) { NFAVertex v = add_vertex(g); g[v].char_reach.setall(); if (head != u) { add_edge(head, v, g); } add_edge(u, v, g); u = v; } // Remove edges from starts and wire both head and u to our initials. for (auto v : initials) { remove_edge(g.startDs, v, g); remove_edge(g.start, v, g); if (head != u) { add_edge(head, v, g); } add_edge(u, v, g); } g.renumberVertices(); g.renumberEdges(); return true; }