#include <algorithm>
#include "ac_slow.hpp"
#include "ac_fast.hpp"
#include <cstddef>
uint32
AC_Converter::Calc_State_Sz(const ACS_State* s) const {
AC_State dummy;
uint32 sz = offsetof(AC_State, input_vect);
sz += s->Get_GotoNum() * sizeof(dummy.input_vect[0]);
if (sz < sizeof(AC_State))
sz = sizeof(AC_State);
uint32 align = __alignof(dummy);
sz = (sz + align - 1) & ~(align - 1);
return sz;
}
AC_Buffer*
AC_Converter::Alloc_Buffer() {
const vector<ACS_State*>& all_states = _acs.Get_All_States();
const ACS_State* root_state = _acs.Get_Root_State();
uint32 root_fanout = root_state->Get_GotoNum();
AC_Ofst root_goto_ofst, states_ofst_ofst, first_state_ofst;
uint32 sz = root_goto_ofst = sizeof(AC_Buffer);
if (likely(root_fanout != 255))
sz += 256;
else
root_goto_ofst = 0;
unsigned align = __alignof(AC_Ofst);
sz = (sz + align - 1) & ~(align - 1);
states_ofst_ofst = sz;
sz += sizeof(AC_Ofst) * all_states.size();
align = __alignof(AC_State);
sz = (sz + align - 1) & ~(align - 1);
first_state_ofst = sz;
uint32 state_sz = 0;
for (vector<ACS_State*>::const_iterator i = all_states.begin(),
e = all_states.end(); i != e; i++) {
state_sz += Calc_State_Sz(*i);
}
state_sz -= Calc_State_Sz(root_state);
sz += state_sz;
AC_Buffer* buf = _buf_alloc.alloc(sz);
buf->hdr.magic_num = AC_MAGIC_NUM;
buf->hdr.impl_variant = IMPL_FAST_VARIANT;
buf->buf_len = sz;
buf->root_goto_ofst = root_goto_ofst;
buf->states_ofst_ofst = states_ofst_ofst;
buf->first_state_ofst = first_state_ofst;
buf->root_goto_num = root_fanout;
buf->state_num = _acs.Get_State_Num();
return buf;
}
void
AC_Converter::Populate_Root_Goto_Func(AC_Buffer* buf,
GotoVect& goto_vect) {
unsigned char *buf_base = (unsigned char*)(buf);
InputTy* root_gotos = (InputTy*)(buf_base + buf->root_goto_ofst);
const ACS_State* root_state = _acs.Get_Root_State();
root_state->Get_Sorted_Gotos(goto_vect);
uint32 new_id = 1;
bool full_fantout = (goto_vect.size() == 255);
if (likely(!full_fantout))
memset(root_gotos, 0, 256 * sizeof(InputTy));
for (GotoVect::iterator i = goto_vect.begin(), e = goto_vect.end();
i != e; i++, new_id++) {
InputTy c = i->first;
ACS_State* s = i->second;
_id_map[s->Get_ID()] = new_id;
if (likely(!full_fantout))
root_gotos[c] = new_id;
}
}
AC_Buffer*
AC_Converter::Convert() {
GotoVect gotovect;
_id_map.clear();
_ofst_map.clear();
_id_map.resize(_acs.Get_Next_Node_Id());
_ofst_map.resize(_acs.Get_Next_Node_Id());
AC_Buffer* buf = Alloc_Buffer();
unsigned char* buf_base = (unsigned char*)buf;
Populate_Root_Goto_Func(buf, gotovect);
buf->root_goto_num = gotovect.size();
_id_map[_acs.Get_Root_State()->Get_ID()] = 0;
vector<const ACS_State*> wl;
State_ID id = 1;
for (GotoVect::iterator i = gotovect.begin(), e = gotovect.end();
i != e; i++, id++) {
ACS_State* s = i->second;
wl.push_back(s);
_id_map[s->Get_ID()] = id;
}
AC_Ofst* state_ofst_vect = (AC_Ofst*)(buf_base + buf->states_ofst_ofst);
AC_Ofst ofst = buf->first_state_ofst;
for (uint32 idx = 0; idx < wl.size(); idx++) {
const ACS_State* old_s = wl[idx];
AC_State* new_s = (AC_State*)(buf_base + ofst);
State_ID state_id = idx + 1;
ASSERT(_id_map[old_s->Get_ID()] == state_id);
state_ofst_vect[state_id] = ofst;
new_s->first_kid = wl.size() + 1;
new_s->depth = old_s->Get_Depth();
new_s->is_term = old_s->is_Terminal() ?
old_s->get_Pattern_Idx() + 1 : 0;
uint32 gotonum = old_s->Get_GotoNum();
new_s->goto_num = gotonum;
old_s->Get_Sorted_Gotos(gotovect);
uint32 input_idx = 0;
uint32 id = wl.size() + 1;
InputTy* input_vect = new_s->input_vect;
for (GotoVect::iterator i = gotovect.begin(), e = gotovect.end();
i != e; i++, id++, input_idx++) {
input_vect[input_idx] = i->first;
ACS_State* kid = i->second;
_id_map[kid->Get_ID()] = id;
wl.push_back(kid);
}
_ofst_map[old_s->Get_ID()] = ofst;
ofst += Calc_State_Sz(old_s);
}
ASSERT(ofst == buf->buf_len);
for (vector<const ACS_State*>::iterator i = wl.begin(), e = wl.end();
i != e; i++) {
const ACS_State* slow_s = *i;
State_ID fast_s_id = _id_map[slow_s->Get_ID()];
AC_State* fast_s = (AC_State*)(buf_base + state_ofst_vect[fast_s_id]);
if (const ACS_State* fl = slow_s->Get_FailLink()) {
State_ID id = _id_map[fl->Get_ID()];
fast_s->fail_link = id;
} else
fast_s->fail_link = 0;
}
#ifdef DEBUG
#endif
return buf;
}
static inline AC_State*
Get_State_Addr(unsigned char* buf_base, AC_Ofst* StateOfstVect, uint32 state_id) {
ASSERT(state_id != 0 && "root node is handled in speical way");
ASSERT(state_id < ((AC_Buffer*)buf_base)->state_num);
return (AC_State*)(buf_base + StateOfstVect[state_id]);
}
#if !defined(BS_MULTI_VER)
#if defined(_MSC_VER) && !__INTEL_COMPILER && _MSC_VER >= 1310
#define INLINE_FLAG inline __forceinline
#else
#define INLINE_FLAG inline __attribute__((always_inline))
#endif
static bool INLINE_FLAG
Binary_Search_Input(InputTy* input_vect, int vect_len, InputTy input, int& idx) {
if (vect_len <= 8) {
for (int i = 0; i < vect_len; i++) {
if (input_vect[i] == input) {
idx = i;
return true;
}
}
return false;
}
int low = 0, high = vect_len - 1;
while (low <= high) {
int mid = (low + high) >> 1;
InputTy mid_c = input_vect[mid];
if (input < mid_c)
high = mid - 1;
else if (input > mid_c)
low = mid + 1;
else {
idx = mid;
return true;
}
}
return false;
}
#else
static inline bool
Binary_Search_Input(InputTy* input_vect, int vect_len, InputTy input, int& idx) {
int low = 0, high = vect_len - 1;
while (low <= high) {
int mid = (low + high) >> 1;
InputTy mid_c = input_vect[mid];
if (input < mid_c)
high = mid - 1;
else if (input > mid_c)
low = mid + 1;
else {
idx = mid;
return true;
}
}
return false;
}
#endif
typedef enum {
MV_FIRST_MATCH,
MV_LEFT_LONGEST,
MV_RIGHT_LONGEST,
MV_ALL_MATCHES,
} MATCH_VARIANT;
template<MATCH_VARIANT variant> static ac_result_t
Match_Tmpl(AC_Buffer* buf, const char* str, uint32 len) {
unsigned char* buf_base = (unsigned char*)(buf);
unsigned char* root_goto = buf_base + buf->root_goto_ofst;
AC_Ofst* states_ofst_vect = (AC_Ofst* )(buf_base + buf->states_ofst_ofst);
AC_State* state = 0;
uint32 idx = 0;
if (likely(buf->root_goto_num != 255)) {
while(idx < len) {
unsigned char c = str[idx++];
if (unsigned char kid_id = root_goto[c]) {
state = Get_State_Addr(buf_base, states_ofst_vect, kid_id);
break;
}
}
} else {
idx = 1;
state = Get_State_Addr(buf_base, states_ofst_vect, *str);
}
ac_result_t r = {-1, -1, 0};
if (likely(state != 0)) {
if (unlikely(state->is_term)) {
r.match_begin = idx - state->depth;
r.match_end = idx - 1;
r.pattern_idx = state->is_term - 1;
if (variant == MV_FIRST_MATCH) {
return r;
}
}
}
while (idx < len) {
unsigned char c = str[idx];
int res;
bool found;
found = Binary_Search_Input(state->input_vect, state->goto_num, c, res);
if (found) {
uint32 kid = state->first_kid + res;
state = Get_State_Addr(buf_base, states_ofst_vect, kid);
idx++;
} else {
State_ID fl = state->fail_link;
if (fl == 0) {
while(idx < len) {
InputTy c = str[idx++];
if (unsigned char kid_id = root_goto[c]) {
state =
Get_State_Addr(buf_base, states_ofst_vect, kid_id);
break;
}
}
} else {
state = Get_State_Addr(buf_base, states_ofst_vect, fl);
}
}
if (state->is_term) {
if (variant == MV_FIRST_MATCH) {
ac_result_t r;
r.match_begin = idx - state->depth;
r.match_end = idx - 1;
r.pattern_idx = state->is_term - 1;
return r;
}
if (variant == MV_LEFT_LONGEST) {
int match_begin = idx - state->depth;
int match_end = idx - 1;
if (r.match_begin == -1 ||
match_end - match_begin > r.match_end - r.match_begin) {
r.match_begin = match_begin;
r.match_end = match_end;
r.pattern_idx = state->is_term - 1;
}
continue;
}
ASSERT(false && "NYI");
}
}
return r;
}
ac_result_t
Match(AC_Buffer* buf, const char* str, uint32 len) {
return Match_Tmpl<MV_FIRST_MATCH>(buf, str, len);
}
ac_result_t
Match_Longest_L(AC_Buffer* buf, const char* str, uint32 len) {
return Match_Tmpl<MV_LEFT_LONGEST>(buf, str, len);
}
#ifdef DEBUG
void
AC_Converter::dump_buffer(AC_Buffer* buf, FILE* f) {
vector<AC_Ofst> state_ofst;
state_ofst.resize(_id_map.size());
fprintf(f, "Id maps between old/slow and new/fast graphs\n");
int old_id = 0;
for (vector<uint32>::iterator i = _id_map.begin(), e = _id_map.end();
i != e; i++, old_id++) {
State_ID new_id = *i;
if (new_id != 0) {
fprintf(f, "%d -> %d, ", old_id, new_id);
}
}
fprintf(f, "\n");
int idx = 0;
for (vector<uint32>::iterator i = _id_map.begin(), e = _id_map.end();
i != e; i++, idx++) {
uint32 id = *i;
if (id == 0) continue;
state_ofst[id] = _ofst_map[idx];
}
unsigned char* buf_base = (unsigned char*)buf;
fprintf(f, "root, fanout:%d goto {", buf->root_goto_num);
if (buf->root_goto_num != 255) {
unsigned char* root_goto = buf_base + buf->root_goto_ofst;
for (uint32 i = 0; i < 255; i++) {
if (root_goto[i] != 0)
fprintf(f, "%c->S:%d, ", (unsigned char)i, root_goto[i]);
}
} else {
fprintf(f, "full fanout\n");
}
fprintf(f, "}\n");
AC_Ofst* state_ofst_vect = (AC_Ofst*)(buf_base + buf->states_ofst_ofst);
for (uint32 i = 1, e = buf->state_num; i < e; i++) {
AC_Ofst ofst = state_ofst_vect[i];
ASSERT(ofst == state_ofst[i]);
fprintf(f, "S:%d, ofst:%d, goto={", i, ofst);
AC_State* s = (AC_State*)(buf_base + ofst);
State_ID kid = s->first_kid;
for (uint32 k = 0, ke = s->goto_num; k < ke; k++, kid++)
fprintf(f, "%c->S:%d, ", s->input_vect[k], kid);
fprintf(f, "}, fail-link = S:%d, %s\n", s->fail_link,
s->is_term ? "terminal" : "");
}
}
#endif