#define INITIAL_FN JOIN(moNfaInitial, SIZE)
#define TOP_FN JOIN(moNfaTop, SIZE)
#define TOPN_FN JOIN(moNfaTopN, SIZE)
+#define PROCESS_ACCEPTS_IMPL_FN JOIN(moProcessAcceptsImpl, SIZE)
#define PROCESS_ACCEPTS_FN JOIN(moProcessAccepts, SIZE)
#define PROCESS_ACCEPTS_NOSQUASH_FN JOIN(moProcessAcceptsNoSquash, SIZE)
#define CONTEXT_T JOIN(NFAContext, SIZE)
#define SQUASH_UNTUG_BR_FN JOIN(lazyTug, SIZE)
#define GET_NFA_REPEAT_INFO_FN JOIN(getNfaRepeatInfo, SIZE)
+#if defined(ARCH_64_BIT) && (SIZE >= 64)
+#define CHUNK_T u64a
+#define FIND_AND_CLEAR_FN findAndClearLSB_64
+#define POPCOUNT_FN popcount64
+#define RANK_IN_MASK_FN rank_in_mask64
+#else
+#define CHUNK_T u32
+#define FIND_AND_CLEAR_FN findAndClearLSB_32
+#define POPCOUNT_FN popcount32
+#define RANK_IN_MASK_FN rank_in_mask32
+#endif
+
+#define NUM_STATE_CHUNKS (sizeof(STATE_T) / sizeof(CHUNK_T))
+
static really_inline
void SQUASH_UNTUG_BR_FN(const IMPL_NFA_T *limex,
const union RepeatControl *repeat_ctrl,
}
}
-static never_inline
-char PROCESS_ACCEPTS_FN(const IMPL_NFA_T *limex, STATE_T *s,
- const struct NFAAccept *acceptTable, u32 acceptCount,
- u64a offset, NfaCallback callback, void *context) {
+static really_inline
+char PROCESS_ACCEPTS_IMPL_FN(const IMPL_NFA_T *limex, const STATE_T *s,
+ STATE_T *squash, const ENG_STATE_T *squashMasks,
+ const STATE_T *acceptMask,
+ const struct NFAAccept *acceptTable, u64a offset,
+ NfaCallback callback, void *context) {
assert(s);
assert(limex);
assert(callback);
- assert(acceptCount);
-
- // We have squash masks we might have to apply after firing reports.
- STATE_T squash = ONES_STATE;
- const ENG_STATE_T *squashMasks = (const ENG_STATE_T *)
- ((const char *)limex + limex->squashOffset);
- for (u32 i = 0; i < acceptCount; i++) {
- const struct NFAAccept *a = &acceptTable[i];
- if (TESTBIT_STATE(*s, a->state)) {
- DEBUG_PRINTF("state %u is on, firing report id=%u, offset=%llu\n",
- a->state, a->externalId, offset);
- int rv = callback(0, offset, a->externalId, context);
+ const STATE_T accept_mask = *acceptMask;
+ STATE_T accepts = AND_STATE(*s, accept_mask);
+
+ // Caller must ensure that we have at least one accept state on.
+ assert(ISNONZERO_STATE(accepts));
+
+ CHUNK_T chunks[NUM_STATE_CHUNKS];
+ memcpy(chunks, &accepts, sizeof(accepts));
+
+ CHUNK_T mask_chunks[NUM_STATE_CHUNKS];
+ memcpy(mask_chunks, &accept_mask, sizeof(accept_mask));
+
+ u32 base_index = 0; // Cumulative sum of mask popcount up to current chunk.
+ for (u32 i = 0; i < NUM_STATE_CHUNKS; i++) {
+ CHUNK_T chunk = chunks[i];
+ while (chunk != 0) {
+ u32 bit = FIND_AND_CLEAR_FN(&chunk);
+ u32 local_idx = RANK_IN_MASK_FN(mask_chunks[i], bit);
+ u32 idx = local_idx + base_index;
+ const struct NFAAccept *a = &acceptTable[idx];
+ DEBUG_PRINTF("state %u: firing report list=%u, offset=%llu\n",
+ bit + i * (u32)sizeof(chunk) * 8, a->reports, offset);
+ int rv = limexRunAccept((const char *)limex, a, callback, context,
+ offset);
if (unlikely(rv == MO_HALT_MATCHING)) {
return 1;
}
- if (a->squash != MO_INVALID_IDX) {
+ if (squash != NULL && a->squash != MO_INVALID_IDX) {
+ assert(squashMasks);
assert(a->squash < limex->squashCount);
const ENG_STATE_T *sq = &squashMasks[a->squash];
DEBUG_PRINTF("squash mask %u @ %p\n", a->squash, sq);
- squash = AND_STATE(squash, LOAD_FROM_ENG(sq));
+ *squash = AND_STATE(*squash, LOAD_FROM_ENG(sq));
}
}
+ base_index += POPCOUNT_FN(mask_chunks[i]);
}
- *s = AND_STATE(*s, squash);
return 0;
}
static never_inline
-char PROCESS_ACCEPTS_NOSQUASH_FN(const STATE_T *s,
+char PROCESS_ACCEPTS_FN(const IMPL_NFA_T *limex, STATE_T *s,
+ const STATE_T *acceptMask,
+ const struct NFAAccept *acceptTable, u64a offset,
+ NfaCallback callback, void *context) {
+ // We have squash masks we might have to apply after firing reports.
+ STATE_T squash = ONES_STATE;
+ const ENG_STATE_T *squashMasks = (const ENG_STATE_T *)
+ ((const char *)limex + limex->squashOffset);
+
+ return PROCESS_ACCEPTS_IMPL_FN(limex, s, &squash, squashMasks, acceptMask,
+ acceptTable, offset, callback, context);
+
+ *s = AND_STATE(*s, squash);
+}
+
+static never_inline
+char PROCESS_ACCEPTS_NOSQUASH_FN(const IMPL_NFA_T *limex, const STATE_T *s,
+ const STATE_T *acceptMask,
const struct NFAAccept *acceptTable,
- u32 acceptCount, u64a offset,
- NfaCallback callback, void *context) {
- assert(s);
- assert(callback);
- assert(acceptCount);
-
- for (u32 i = 0; i < acceptCount; i++) {
- const struct NFAAccept *a = &acceptTable[i];
- if (TESTBIT_STATE(*s, a->state)) {
- DEBUG_PRINTF("state %u is on, firing report id=%u, offset=%llu\n",
- a->state, a->externalId, offset);
- int rv = callback(0, offset, a->externalId, context);
- if (unlikely(rv == MO_HALT_MATCHING)) {
- return 1;
- }
- }
- }
- return 0;
+ u64a offset, NfaCallback callback,
+ void *context) {
+ STATE_T *squash = NULL;
+ const ENG_STATE_T *squashMasks = NULL;
+
+ return PROCESS_ACCEPTS_IMPL_FN(limex, s, squash, squashMasks, acceptMask,
+ acceptTable, offset, callback, context);
}
// Run EOD accepts. Note that repeat_ctrl and repeat_state may be NULL if this
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
const struct NFAAccept *acceptEodTable = getAcceptEodTable(limex);
- if (PROCESS_ACCEPTS_NOSQUASH_FN(&foundAccepts, acceptEodTable,
- limex->acceptEodCount, offset, callback,
+ if (PROCESS_ACCEPTS_NOSQUASH_FN(limex, &foundAccepts, &acceptEodMask,
+ acceptEodTable, offset, callback,
context)) {
return MO_HALT_MATCHING;
}
const struct NFAAccept *acceptTable = getAcceptTable(limex);
u64a offset = q_cur_offset(q);
- if (PROCESS_ACCEPTS_NOSQUASH_FN(&foundAccepts, acceptTable,
- limex->acceptCount, offset, q->cb,
+ if (PROCESS_ACCEPTS_NOSQUASH_FN(limex, &foundAccepts, &acceptMask,
+ acceptTable, offset, q->cb,
q->context)) {
return MO_HALT_MATCHING;
}
u64a offset, ReportID report) {
assert(limex);
- const STATE_T acceptMask = LOAD_FROM_ENG(&limex->accept);
- STATE_T accstate = AND_STATE(state, acceptMask);
+ const STATE_T accept_mask = LOAD_FROM_ENG(&limex->accept);
+ STATE_T accepts = AND_STATE(state, accept_mask);
// Are we in an accept state?
- if (ISZERO_STATE(accstate)) {
+ if (ISZERO_STATE(accepts)) {
DEBUG_PRINTF("no accept states are on\n");
return 0;
}
- SQUASH_UNTUG_BR_FN(limex, repeat_ctrl, repeat_state, offset, &accstate);
+ SQUASH_UNTUG_BR_FN(limex, repeat_ctrl, repeat_state, offset, &accepts);
DEBUG_PRINTF("looking for report %u\n", report);
-#ifdef DEBUG
- DEBUG_PRINTF("accept states that are on: ");
- for (u32 i = 0; i < sizeof(STATE_T) * 8; i++) {
- if (TESTBIT_STATE(accstate, i)) printf("%u ", i);
- }
- printf("\n");
-#endif
-
- // Does one of our states match the given report ID?
const struct NFAAccept *acceptTable = getAcceptTable(limex);
- for (u32 i = 0; i < limex->acceptCount; i++) {
- const struct NFAAccept *a = &acceptTable[i];
- DEBUG_PRINTF("checking idx=%u, externalId=%u\n", a->state,
- a->externalId);
- if (a->externalId == report && TESTBIT_STATE(accstate, a->state)) {
- DEBUG_PRINTF("report is on!\n");
- return 1;
+
+ CHUNK_T chunks[NUM_STATE_CHUNKS];
+ memcpy(chunks, &accepts, sizeof(accepts));
+
+ CHUNK_T mask_chunks[NUM_STATE_CHUNKS];
+ memcpy(mask_chunks, &accept_mask, sizeof(accept_mask));
+
+ u32 base_index = 0; // Cumulative sum of mask popcount up to current chunk.
+ for (u32 i = 0; i < NUM_STATE_CHUNKS; i++) {
+ CHUNK_T chunk = chunks[i];
+ while (chunk != 0) {
+ u32 bit = FIND_AND_CLEAR_FN(&chunk);
+ u32 local_idx = RANK_IN_MASK_FN(mask_chunks[i], bit);
+ u32 idx = local_idx + base_index;
+ assert(idx < limex->acceptCount);
+ const struct NFAAccept *a = &acceptTable[idx];
+ DEBUG_PRINTF("state %u is on, report list at %u\n",
+ bit + i * (u32)sizeof(chunk) * 8, a->reports);
+
+ if (limexAcceptHasReport((const char *)limex, a, report)) {
+ DEBUG_PRINTF("report %u is on\n", report);
+ return 1;
+ }
}
+ base_index += POPCOUNT_FN(mask_chunks[i]);
}
return 0;
#undef TESTBIT_STATE
#undef ISNONZERO_STATE
#undef ISZERO_STATE
+#undef PROCESS_ACCEPTS_IMPL_FN
#undef PROCESS_ACCEPTS_FN
#undef PROCESS_ACCEPTS_NOSQUASH_FN
#undef SQUASH_UNTUG_BR_FN
#undef GET_NFA_REPEAT_INFO_FN
+
+#undef CHUNK_T
+#undef FIND_AND_CLEAR_FN
+#undef POPCOUNT_FN
+#undef RANK_IN_MASK_FN
+#undef NUM_STATE_CHUNKS
}
static
-void buildAccepts(const build_info &args, NFAStateSet &acceptMask,
- NFAStateSet &acceptEodMask, vector<NFAAccept> &accepts,
- vector<NFAAccept> &acceptsEod, vector<NFAStateSet> &squash) {
+u32 addSquashMask(const build_info &args, const NFAVertex &v,
+ vector<NFAStateSet> &squash) {
+ auto sit = args.reportSquashMap.find(v);
+ if (sit == args.reportSquashMap.end()) {
+ return MO_INVALID_IDX;
+ }
+
+ // This state has a squash mask. Paw through the existing vector to
+ // see if we've already seen it, otherwise add a new one.
+ auto it = find(squash.begin(), squash.end(), sit->second);
+ if (it != squash.end()) {
+ return verify_u32(distance(squash.begin(), it));
+ }
+ u32 idx = verify_u32(squash.size());
+ squash.push_back(sit->second);
+ return idx;
+}
+
+static
+u32 addReports(const flat_set<ReportID> &r, vector<ReportID> &reports,
+ unordered_map<vector<ReportID>, u32> &reportListCache) {
+ assert(!r.empty());
+
+ vector<ReportID> my_reports(begin(r), end(r));
+ my_reports.push_back(MO_INVALID_IDX); // sentinel
+
+ auto cache_it = reportListCache.find(my_reports);
+ if (cache_it != end(reportListCache)) {
+ u32 offset = cache_it->second;
+ DEBUG_PRINTF("reusing cached report list at %u\n", offset);
+ return offset;
+ }
+
+ auto it = search(begin(reports), end(reports), begin(my_reports),
+ end(my_reports));
+ if (it != end(reports)) {
+ u32 offset = verify_u32(distance(begin(reports), it));
+ DEBUG_PRINTF("reusing found report list at %u\n", offset);
+ return offset;
+ }
+
+ u32 offset = verify_u32(reports.size());
+ insert(&reports, reports.end(), my_reports);
+ reportListCache.emplace(move(my_reports), offset);
+ return offset;
+}
+
+static
+void buildAcceptsList(const build_info &args,
+ unordered_map<vector<ReportID>, u32> &reports_cache,
+ vector<NFAVertex> &verts, vector<NFAAccept> &accepts,
+ vector<ReportID> &reports, vector<NFAStateSet> &squash) {
+ if (verts.empty()) {
+ return;
+ }
+
+ DEBUG_PRINTF("building accept lists for %zu states\n", verts.size());
+
+ auto cmp_state_id = [&args](NFAVertex a, NFAVertex b) {
+ u32 a_state = args.state_ids.at(a);
+ u32 b_state = args.state_ids.at(b);
+ assert(a_state != b_state || a == b);
+ return a_state < b_state;
+ };
+
+ sort(begin(verts), end(verts), cmp_state_id);
+
+ const NGHolder &h = args.h;
+ for (const auto &v : verts) {
+ DEBUG_PRINTF("state=%u, reports: [%s]\n", args.state_ids.at(v),
+ as_string_list(h[v].reports).c_str());
+ NFAAccept a;
+ memset(&a, 0, sizeof(a));
+ assert(!h[v].reports.empty());
+ if (h[v].reports.size() == 1) {
+ a.single_report = 1;
+ a.reports = *h[v].reports.begin();
+ } else {
+ a.single_report = 0;
+ a.reports = addReports(h[v].reports, reports, reports_cache);
+ }
+ a.squash = addSquashMask(args, v, squash);
+ accepts.push_back(move(a));
+ }
+}
+
+static
+void buildAccepts(const build_info &args,
+ unordered_map<vector<ReportID>, u32> &reports_cache,
+ NFAStateSet &acceptMask, NFAStateSet &acceptEodMask,
+ vector<NFAAccept> &accepts, vector<NFAAccept> &acceptsEod,
+ vector<ReportID> &reports, vector<NFAStateSet> &squash) {
const NGHolder &h = args.h;
acceptMask.resize(args.num_states);
acceptEodMask.resize(args.num_states);
+ vector<NFAVertex> verts_accept, verts_accept_eod;
+
for (auto v : vertices_range(h)) {
u32 state_id = args.state_ids.at(v);
continue;
}
- u32 squashMaskOffset = MO_INVALID_IDX;
- auto sit = args.reportSquashMap.find(v);
- if (sit != args.reportSquashMap.end()) {
- // This state has a squash mask. Paw through the existing vector to
- // see if we've already seen it, otherwise add a new one.
- auto it = find(squash.begin(), squash.end(), sit->second);
- if (it != squash.end()) {
- squashMaskOffset = verify_u32(distance(squash.begin(), it));
- } else {
- squashMaskOffset = verify_u32(squash.size());
- squash.push_back(sit->second);
- }
- }
-
- // Add an accept (or acceptEod) per report ID.
-
- vector<NFAAccept> *accepts_out;
if (edge(v, h.accept, h).second) {
acceptMask.set(state_id);
- accepts_out = &accepts;
+ verts_accept.push_back(v);
} else {
assert(edge(v, h.acceptEod, h).second);
acceptEodMask.set(state_id);
- accepts_out = &acceptsEod;
- }
-
- for (auto report : h[v].reports) {
- accepts_out->push_back(NFAAccept());
- NFAAccept &a = accepts_out->back();
- a.state = state_id;
- a.externalId = report;
- a.squash = squashMaskOffset;
- DEBUG_PRINTF("Accept: state=%u, externalId=%u\n", state_id, report);
+ verts_accept_eod.push_back(v);
}
}
+
+ buildAcceptsList(args, reports_cache, verts_accept, accepts, reports,
+ squash);
+ buildAcceptsList(args, reports_cache, verts_accept_eod, acceptsEod, reports,
+ squash);
}
static
}
};
-static
-u32 getReportListIndex(const flat_set<ReportID> &reports,
- vector<ReportID> &exceptionReports,
- map<vector<ReportID>, u32> &reportListCache) {
- if (reports.empty()) {
- return MO_INVALID_IDX;
- }
-
- const vector<ReportID> r(reports.begin(), reports.end());
-
- auto it = reportListCache.find(r);
- if (it != reportListCache.end()) {
- u32 idx = it->second;
- assert(idx < exceptionReports.size());
- assert(equal(r.begin(), r.end(), exceptionReports.begin() + idx));
- return idx;
- }
-
- u32 idx = verify_u32(exceptionReports.size());
- reportListCache[r] = idx;
- exceptionReports.insert(exceptionReports.end(), r.begin(), r.end());
- exceptionReports.push_back(MO_INVALID_IDX); // terminator
- return idx;
-}
-
static
u32 buildExceptionMap(const build_info &args,
+ unordered_map<vector<ReportID>, u32> &reports_cache,
const ue2::unordered_set<NFAEdge> &exceptional,
- map<ExceptionProto, vector<u32> > &exceptionMap,
- vector<ReportID> &exceptionReports) {
+ map<ExceptionProto, vector<u32>> &exceptionMap,
+ vector<ReportID> &reportList) {
const NGHolder &h = args.h;
const u32 num_states = args.num_states;
u32 exceptionCount = 0;
}
}
- // We track report lists that have already been written into the global
- // list in case we can reuse them.
- map<vector<ReportID>, u32> reportListCache;
-
for (auto v : vertices_range(h)) {
const u32 i = args.state_ids.at(v);
DEBUG_PRINTF("state %u is exceptional due to accept "
"(%zu reports)\n", i, reports.size());
- e.reports_index =
- getReportListIndex(reports, exceptionReports, reportListCache);
+ if (reports.empty()) {
+ e.reports_index = MO_INVALID_IDX;
+ } else {
+ e.reports_index =
+ addReports(reports, reportList, reports_cache);
+ }
// We may be applying a report squash too.
auto mi = args.reportSquashMap.find(v);
}
static
- void writeExceptions(const map<ExceptionProto, vector<u32> > &exceptionMap,
- const vector<u32> &repeatOffsets,
- implNFA_t *limex, const u32 exceptionsOffset) {
+ void writeExceptions(const map<ExceptionProto, vector<u32>> &exceptionMap,
+ const vector<u32> &repeatOffsets, implNFA_t *limex,
+ const u32 exceptionsOffset,
+ const u32 reportListOffset) {
DEBUG_PRINTF("exceptionsOffset=%u\n", exceptionsOffset);
exception_t *etable = (exception_t *)((char *)limex + exceptionsOffset);
exception_t &e = etable[ecount];
maskSetBits(e.squash, proto.squash_states);
maskSetBits(e.successors, proto.succ_states);
- e.reports = proto.reports_index;
+ if (proto.reports_index == MO_INVALID_IDX) {
+ e.reports = MO_INVALID_IDX;
+ } else {
+ e.reports = reportListOffset +
+ proto.reports_index * sizeof(ReportID);
+ }
e.hasSquash = verify_u8(proto.squash);
e.trigger = verify_u8(proto.trigger);
u32 repeat_offset = proto.repeat_index == MO_INVALID_IDX
const vector<NFAAccept> &acceptsEod,
const vector<NFAStateSet> &squash, implNFA_t *limex,
const u32 acceptsOffset, const u32 acceptsEodOffset,
- const u32 squashOffset) {
+ const u32 squashOffset, const u32 reportListOffset) {
+ char *limex_base = (char *)limex;
+
DEBUG_PRINTF("acceptsOffset=%u, acceptsEodOffset=%u, squashOffset=%u\n",
acceptsOffset, acceptsEodOffset, squashOffset);
maskSetBits(limex->accept, acceptMask);
maskSetBits(limex->acceptAtEOD, acceptEodMask);
+ // Transforms the index into the report list into an offset relative to
+ // the base of the limex.
+ auto report_offset_fn = [&](NFAAccept a) {
+ if (!a.single_report) {
+ a.reports = reportListOffset + a.reports * sizeof(ReportID);
+ }
+ return a;
+ };
+
// Write accept table.
limex->acceptOffset = acceptsOffset;
limex->acceptCount = verify_u32(accepts.size());
DEBUG_PRINTF("NFA has %zu accepts\n", accepts.size());
- NFAAccept *acceptsTable = (NFAAccept *)((char *)limex + acceptsOffset);
+ NFAAccept *acceptsTable = (NFAAccept *)(limex_base + acceptsOffset);
assert(ISALIGNED(acceptsTable));
- copy(accepts.begin(), accepts.end(), acceptsTable);
+ transform(accepts.begin(), accepts.end(), acceptsTable,
+ report_offset_fn);
// Write eod accept table.
limex->acceptEodOffset = acceptsEodOffset;
limex->acceptEodCount = verify_u32(acceptsEod.size());
DEBUG_PRINTF("NFA has %zu EOD accepts\n", acceptsEod.size());
- NFAAccept *acceptsEodTable = (NFAAccept *)((char *)limex + acceptsEodOffset);
+ NFAAccept *acceptsEodTable = (NFAAccept *)(limex_base + acceptsEodOffset);
assert(ISALIGNED(acceptsEodTable));
- copy(acceptsEod.begin(), acceptsEod.end(), acceptsEodTable);
+ transform(acceptsEod.begin(), acceptsEod.end(), acceptsEodTable,
+ report_offset_fn);
// Write squash mask table.
limex->squashCount = verify_u32(squash.size());
limex->squashOffset = squashOffset;
DEBUG_PRINTF("NFA has %zu report squash masks\n", squash.size());
- tableRow_t *mask = (tableRow_t *)((char *)limex + squashOffset);
+ tableRow_t *mask = (tableRow_t *)(limex_base + squashOffset);
assert(ISALIGNED(mask));
for (size_t i = 0, end = squash.size(); i < end; i++) {
maskSetBits(mask[i], squash[i]);
}
static
- void writeExceptionReports(const vector<ReportID> &reports,
- implNFA_t *limex,
- const u32 exceptionReportsOffset) {
- DEBUG_PRINTF("exceptionReportsOffset=%u\n", exceptionReportsOffset);
-
- limex->exReportOffset = exceptionReportsOffset;
- assert(ISALIGNED_N((char *)limex + exceptionReportsOffset,
+ void writeReportList(const vector<ReportID> &reports, implNFA_t *limex,
+ const u32 reportListOffset) {
+ DEBUG_PRINTF("reportListOffset=%u\n", reportListOffset);
+ assert(ISALIGNED_N((char *)limex + reportListOffset,
alignof(ReportID)));
- copy_bytes((char *)limex + exceptionReportsOffset, reports);
+ copy_bytes((char *)limex + reportListOffset, reports);
}
static
repeatSize += repeats[i].second;
}
+ // We track report lists that have already been written into the global
+ // list in case we can reuse them.
+ unordered_map<vector<ReportID>, u32> reports_cache;
+
ue2::unordered_set<NFAEdge> exceptional;
u32 shiftCount = findBestNumOfVarShifts(args);
assert(shiftCount);
findExceptionalTransitions(args, exceptional, maxShift);
map<ExceptionProto, vector<u32> > exceptionMap;
- vector<ReportID> exceptionReports;
- u32 exceptionCount = buildExceptionMap(args, exceptional, exceptionMap,
- exceptionReports);
+ vector<ReportID> reportList;
+
+ u32 exceptionCount = buildExceptionMap(args, reports_cache, exceptional,
+ exceptionMap, reportList);
assert(exceptionCount <= args.num_states);
NFAStateSet acceptMask, acceptEodMask;
vector<NFAAccept> accepts, acceptsEod;
vector<NFAStateSet> squash;
- buildAccepts(args, acceptMask, acceptEodMask, accepts, acceptsEod,
- squash);
+ buildAccepts(args, reports_cache, acceptMask, acceptEodMask, accepts,
+ acceptsEod, reportList, squash);
// Build all our accel info.
NFAStateSet accelMask, accelFriendsMask;
const u32 exceptionsOffset = offset;
offset += sizeof(exception_t) * exceptionCount;
- const u32 exceptionReportsOffset = offset;
- offset += sizeof(ReportID) * exceptionReports.size();
+ const u32 reportListOffset = offset;
+ offset += sizeof(ReportID) * reportList.size();
const u32 repeatOffsetsOffset = offset;
offset += sizeof(u32) * args.repeats.size();
limex, accelTableOffset, accelAuxOffset);
writeAccepts(acceptMask, acceptEodMask, accepts, acceptsEod, squash,
- limex, acceptsOffset, acceptsEodOffset, squashOffset);
+ limex, acceptsOffset, acceptsEodOffset, squashOffset,
+ reportListOffset);
limex->shiftCount = shiftCount;
writeShiftMasks(args, limex);
// Determine the state required for our state vector.
findStateSize(args, limex);
- writeExceptionReports(exceptionReports, limex, exceptionReportsOffset);
+ writeReportList(reportList, limex, reportListOffset);
// Repeat structures and offset table.
vector<u32> repeatOffsets;
writeRepeats(repeats, repeatOffsets, limex, repeatOffsetsOffset,
repeatsOffset);
- writeExceptions(exceptionMap, repeatOffsets, limex, exceptionsOffset);
+ writeExceptions(exceptionMap, repeatOffsets, limex, exceptionsOffset,
+ reportListOffset);
writeLimexMasks(args, limex);
}
}
+static
+void dumpAcceptList(const char *limex_base, const struct NFAAccept *accepts,
+ u32 acceptCount, FILE *f) {
+ for (u32 i = 0; i < acceptCount; i++) {
+ const NFAAccept &a = accepts[i];
+ if (a.single_report) {
+ fprintf(f, " idx %u fires single report %u\n", i, a.reports);
+ continue;
+ }
+ fprintf(f, " idx %u fires report list %u:", i, a.reports);
+ const ReportID *report = (const ReportID *)(limex_base + a.reports);
+ for (; *report != MO_INVALID_IDX; report++) {
+ fprintf(f, " %u", *report);
+ }
+ fprintf(f, "\n");
+ }
+}
+
template<typename limex_type>
static
void dumpAccepts(const limex_type *limex, FILE *f) {
- u32 acceptCount = limex->acceptCount;
- u32 acceptEodCount = limex->acceptEodCount;
+ const char *limex_base = (const char *)limex;
+
+ const u32 acceptCount = limex->acceptCount;
+ const u32 acceptEodCount = limex->acceptEodCount;
fprintf(f, "\n%u accepts.\n", acceptCount);
- const struct NFAAccept *accepts
- = (const struct NFAAccept *)((const char *)limex + limex->acceptOffset);
- for (u32 i = 0; i < acceptCount; i++) {
- fprintf(f, " state %u fires report %u\n", accepts[i].state,
- accepts[i].externalId);
- }
+ const auto *accepts =
+ (const struct NFAAccept *)(limex_base + limex->acceptOffset);
+ dumpAcceptList(limex_base, accepts, acceptCount, f);
fprintf(f, "\n%u accepts at EOD.\n", acceptEodCount);
- accepts = (const struct NFAAccept *)((const char *)limex
- + limex->acceptEodOffset);
- for (u32 i = 0; i < acceptEodCount; i++) {
- fprintf(f, " state %u fires report %u\n", accepts[i].state,
- accepts[i].externalId);
- }
+ const auto *accepts_eod =
+ (const struct NFAAccept *)(limex_base + limex->acceptEodOffset);
+ dumpAcceptList(limex_base, accepts_eod, acceptEodCount, f);
fprintf(f, "\n");
}
((const char *)limex + limex->exceptionOffset);
}
-template<typename limex_type>
-static
-const ReportID *getReportList(const limex_type *limex) {
- return (const ReportID *)((const char *)limex + limex->exReportOffset);
-}
-
template<typename limex_type>
static
void dumpLimexExceptions(const limex_type *limex, FILE *f) {
const typename limex_traits<limex_type>::exception_type *e =
getExceptionTable(limex);
- const ReportID *reports = getReportList(limex);
const u32 size = limex_traits<limex_type>::size;
+ const char *limex_base = (const char *)limex;
+
fprintf(f, "\n");
for (u32 i = 0; i < limex->exceptionCount; i++) {
fprintf(f, "exception %u: hasSquash=%u, reports offset=%u\n",
if (e[i].reports == MO_INVALID_IDX) {
fprintf(f, " <none>\n");
} else {
- const ReportID *r = reports + e[i].reports;
+ const ReportID *r = (const ReportID *)(limex_base + e[i].reports);
while (*r != MO_INVALID_IDX) {
fprintf(f, " %u", *r++);
}
STATE_T *local_succ,
#endif
const struct IMPL_NFA_T *limex,
- const ReportID *exReports,
u64a offset,
struct CONTEXT_T *ctx,
struct proto_cache *new_cache,
// Some exceptions fire accepts.
if (e->reports != MO_INVALID_IDX) {
if (flags & CALLBACK_OUTPUT) {
- const ReportID *reports = exReports + e->reports;
+ const ReportID *reports =
+ (const ReportID *)((const char *)limex + e->reports);
if (unlikely(limexRunReports(reports, ctx->callback,
ctx->context, offset)
== MO_HALT_MATCHING)) {
static really_inline
int PE_FN(STATE_ARG, ESTATE_ARG, u32 diffmask, STATE_T *succ,
const struct IMPL_NFA_T *limex, const EXCEPTION_T *exceptions,
- const ReportID *exReports, u64a offset, struct CONTEXT_T *ctx,
- char in_rev, char flags) {
+ u64a offset, struct CONTEXT_T *ctx, char in_rev, char flags) {
assert(diffmask > 0); // guaranteed by caller macro
if (EQ_STATE(estate, ctx->cached_estate)) {
#ifndef BIG_MODEL
&local_succ,
#endif
- limex, exReports, offset, ctx, &new_cache,
- &cacheable, in_rev, flags)) {
+ limex, offset, ctx, &new_cache, &cacheable,
+ in_rev, flags)) {
return PE_RV_HALT;
}
} while (word);
#undef STATE_ARG_NAME
#undef STATE_ARG_P
+#undef IMPL_NFA_T
+
#undef CHUNK_T
#undef FIND_AND_CLEAR_FN
-#undef IMPL_NFA_T
-#undef GET_NFA_REPEAT_INFO_FN
+#undef POPCOUNT_FN
+#undef RANK_IN_MASK_FN
u32 acceptEodOffset; /* rel. to start of LimExNFA */ \
u32 exceptionCount; \
u32 exceptionOffset; /* rel. to start of LimExNFA */ \
- u32 exReportOffset; /* rel. to start of LimExNFA */ \
u32 repeatCount; \
u32 repeatOffset; \
u32 squashOffset; /* rel. to start of LimExNFA; for accept squashing */ \
};
struct NFAAccept {
- u32 state; //!< state ID of triggering state
- ReportID externalId; //!< report ID to raise
- u32 squash; //!< offset into masks, or MO_INVALID_IDX
+ u8 single_report; //!< If true, 'reports' is report id.
+
+ /**
+ * \brief If single report is true, this is the report id to fire.
+ * Otherwise, it is the offset (relative to the start of the LimExNFA
+ * structure) of a list of reports, terminated with MO_INVALID_IDX.
+ */
+ u32 reports;
+
+ u32 squash; //!< Offset into squash masks, or MO_INVALID_IDX.
};
#endif
static really_inline
int processExceptional32(u32 s, u32 estate, UNUSED u32 diffmask, u32 *succ,
const struct LimExNFA32 *limex,
- const struct NFAException32 *exceptions,
- const ReportID *exReports, u64a offset,
+ const struct NFAException32 *exceptions, u64a offset,
struct NFAContext32 *ctx, char in_rev, char flags) {
assert(estate != 0); // guaranteed by calling macro
u32 bit = findAndClearLSB_32(&estate);
u32 idx = rank_in_mask32(limex->exceptionMask, bit);
const struct NFAException32 *e = &exceptions[idx];
- if (!runException32(e, s, succ, &local_succ, limex, exReports, offset,
- ctx, &new_cache, &cacheable, in_rev, flags)) {
+ if (!runException32(e, s, succ, &local_succ, limex, offset, ctx,
+ &new_cache, &cacheable, in_rev, flags)) {
return PE_RV_HALT;
}
} while (estate != 0);
return MO_CONTINUE_MATCHING; // continue
}
+static really_inline
+int limexRunAccept(const char *limex_base, const struct NFAAccept *accept,
+ NfaCallback callback, void *context, u64a offset) {
+ if (accept->single_report) {
+ const ReportID report = accept->reports;
+ DEBUG_PRINTF("firing single report for id %u at offset %llu\n", report,
+ offset);
+ return callback(0, offset, report, context);
+ }
+ const ReportID *reports = (const ReportID *)(limex_base + accept->reports);
+ return limexRunReports(reports, callback, context, offset);
+}
+
+static really_inline
+int limexAcceptHasReport(const char *limex_base, const struct NFAAccept *accept,
+ ReportID report) {
+ if (accept->single_report) {
+ return accept->reports == report;
+ }
+
+ const ReportID *reports = (const ReportID *)(limex_base + accept->reports);
+ assert(*reports != MO_INVALID_IDX);
+ do {
+ if (*reports == report) {
+ return 1;
+ }
+ reports++;
+ } while (*reports != MO_INVALID_IDX);
+
+ return 0;
+}
+
/** \brief Return a (correctly typed) pointer to the exception table. */
#define getExceptionTable(exc_type, lim) \
((const exc_type *)((const char *)(lim) + (lim)->exceptionOffset))
-/** \brief Return a pointer to the exceptional reports list. */
-#define getExReports(lim) \
- ((const ReportID *)((const char *)(lim) + (lim)->exReportOffset))
-
/** \brief Return a pointer to the ordinary accepts table. */
#define getAcceptTable(lim) \
((const struct NFAAccept *)((const char *)(lim) + (lim)->acceptOffset))
// continue, 1 if an accept was fired and the user instructed us to halt.
static really_inline
char RUN_EXCEPTIONS_FN(const IMPL_NFA_T *limex, const EXCEPTION_T *exceptions,
- const ReportID *exReports, STATE_T s,
- const STATE_T emask, size_t i, u64a offset,
+ STATE_T s, const STATE_T emask, size_t i, u64a offset,
STATE_T *succ, u64a *final_loc, struct CONTEXT_T *ctx,
const char flags, const char in_rev,
const char first_match) {
char localflags = (!i && !in_rev) ? NO_OUTPUT | FIRST_BYTE : flags;
int rv = JOIN(processExceptional, SIZE)(
- pass_state, pass_estate, diffmask, succ, limex, exceptions, exReports,
+ pass_state, pass_estate, diffmask, succ, limex, exceptions,
callback_offset, ctx, in_rev, localflags);
if (rv == PE_RV_HALT) {
return 1; // Halt matching.
const union AccelAux *accelAux =
(const union AccelAux *)((const char *)limex + limex->accelAuxOffset);
const EXCEPTION_T *exceptions = getExceptionTable(EXCEPTION_T, limex);
- const ReportID *exReports = getExReports(limex);
STATE_T s = ctx->s;
/* assert(ISALIGNED_16(exceptions)); */
STATE_T succ;
NFA_EXEC_GET_LIM_SUCC(limex, s, succ);
- if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, s, EXCEPTION_MASK,
- i, offset, &succ, final_loc, ctx, flags, 0,
- first_match)) {
+ if (RUN_EXCEPTIONS_FN(limex, exceptions, s, EXCEPTION_MASK, i, offset,
+ &succ, final_loc, ctx, flags, 0, first_match)) {
return MO_HALT_MATCHING;
}
STATE_T succ;
NFA_EXEC_GET_LIM_SUCC(limex, s, succ);
- if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, s, EXCEPTION_MASK,
- i, offset, &succ, final_loc, ctx, flags, 0,
- first_match)) {
+ if (RUN_EXCEPTIONS_FN(limex, exceptions, s, EXCEPTION_MASK, i, offset,
+ &succ, final_loc, ctx, flags, 0, first_match)) {
return MO_HALT_MATCHING;
}
if ((first_match || (flags & CALLBACK_OUTPUT)) && limex->acceptCount) {
STATE_T acceptMask = LOAD_FROM_ENG(&limex->accept);
const struct NFAAccept *acceptTable = getAcceptTable(limex);
- const u32 acceptCount = limex->acceptCount;
-
STATE_T foundAccepts = AND_STATE(s, acceptMask);
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
if (first_match) {
assert(final_loc);
*final_loc = length;
return MO_HALT_MATCHING;
- } else if (PROCESS_ACCEPTS_FN(limex, &ctx->s, acceptTable,
- acceptCount, offset + length,
+ } else if (PROCESS_ACCEPTS_FN(limex, &ctx->s, &acceptMask,
+ acceptTable, offset + length,
ctx->callback, ctx->context)) {
return MO_HALT_MATCHING;
}
const STATE_T exceptionMask = LOAD_FROM_ENG(&limex->exceptionMask);
#endif
const EXCEPTION_T *exceptions = getExceptionTable(EXCEPTION_T, limex);
- const ReportID *exReports = getExReports(limex);
STATE_T s = ctx->s;
/* assert(ISALIGNED_16(exceptions)); */
STATE_T succ;
NFA_EXEC_GET_LIM_SUCC(limex, s, succ);
- if (RUN_EXCEPTIONS_FN(limex, exceptions, exReports, s,
- EXCEPTION_MASK, i, offset, &succ, final_loc, ctx,
- flags, 1, 0)) {
+ if (RUN_EXCEPTIONS_FN(limex, exceptions, s, EXCEPTION_MASK, i, offset,
+ &succ, final_loc, ctx, flags, 1, 0)) {
return MO_HALT_MATCHING;
}
if (acceptCount) {
STATE_T foundAccepts = AND_STATE(s, acceptMask);
if (unlikely(ISNONZERO_STATE(foundAccepts))) {
- if (PROCESS_ACCEPTS_NOSQUASH_FN(&ctx->s, acceptTable, acceptCount,
- offset, ctx->callback,
+ if (PROCESS_ACCEPTS_NOSQUASH_FN(limex, &ctx->s, &acceptMask,
+ acceptTable, offset, ctx->callback,
ctx->context)) {
return MO_HALT_MATCHING;
}