mirror of
https://github.com/VectorCamp/vectorscan.git
synced 2025-06-28 16:41:01 +03:00
366 lines
12 KiB
C
366 lines
12 KiB
C
/*
|
|
* Copyright (c) 2015, Intel Corporation
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef FDR_STREAMING_RUNTIME_H
|
|
#define FDR_STREAMING_RUNTIME_H
|
|
|
|
#include "fdr_streaming_internal.h"
|
|
#include "util/partial_store.h"
|
|
|
|
static really_inline
|
|
const struct FDRSTableHeader * getSHDR(const struct FDR * fdr) {
|
|
const u8 * linkPtr = ((const u8 *)fdr) + fdr->link;
|
|
// test if it's not really a engineID, but a 'pseudo engine id'
|
|
assert(*(const u32 *)linkPtr == 0xffffffff);
|
|
assert(linkPtr);
|
|
return (const struct FDRSTableHeader *)linkPtr;
|
|
}
|
|
|
|
// Reads from stream state and unpacks values into stream state table.
|
|
static really_inline
|
|
void getStreamStates(const struct FDRSTableHeader * streamingTable,
|
|
const u8 * stream_state, u32 * table) {
|
|
assert(streamingTable);
|
|
assert(stream_state);
|
|
assert(table);
|
|
|
|
u8 ss_bytes = streamingTable->streamStateBytes;
|
|
u8 ssb = streamingTable->streamStateBits[CASEFUL];
|
|
UNUSED u8 ssb_nc = streamingTable->streamStateBits[CASELESS];
|
|
assert(ss_bytes == (ssb + ssb_nc + 7) / 8);
|
|
|
|
#if defined(ARCH_32_BIT)
|
|
// On 32-bit hosts, we may be able to avoid having to do any u64a
|
|
// manipulation at all.
|
|
if (ss_bytes <= 4) {
|
|
u32 ssb_mask = (1U << ssb) - 1;
|
|
u32 streamVal = partial_load_u32(stream_state, ss_bytes);
|
|
table[CASEFUL] = (u32)(streamVal & ssb_mask);
|
|
table[CASELESS] = (u32)(streamVal >> ssb);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
u64a ssb_mask = (1ULL << ssb) - 1;
|
|
u64a streamVal = partial_load_u64a(stream_state, ss_bytes);
|
|
table[CASEFUL] = (u32)(streamVal & ssb_mask);
|
|
table[CASELESS] = (u32)(streamVal >> (u64a)ssb);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
// Defensive checking (used in assert) that these table values don't overflow
|
|
// outside the range available.
|
|
static really_inline UNUSED
|
|
u32 streamingTableOverflow(u32 * table, u8 ssb, u8 ssb_nc) {
|
|
u32 ssb_mask = (1ULL << (ssb)) - 1;
|
|
if (table[CASEFUL] & ~ssb_mask) {
|
|
return 1;
|
|
}
|
|
u32 ssb_nc_mask = (1ULL << (ssb_nc)) - 1;
|
|
if (table[CASELESS] & ~ssb_nc_mask) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
// Reads from stream state table and packs values into stream state.
|
|
static really_inline
|
|
void setStreamStates(const struct FDRSTableHeader * streamingTable,
|
|
u8 * stream_state, u32 * table) {
|
|
assert(streamingTable);
|
|
assert(stream_state);
|
|
assert(table);
|
|
|
|
u8 ss_bytes = streamingTable->streamStateBytes;
|
|
u8 ssb = streamingTable->streamStateBits[CASEFUL];
|
|
UNUSED u8 ssb_nc = streamingTable->streamStateBits[CASELESS];
|
|
assert(ss_bytes == (ssb + ssb_nc + 7) / 8);
|
|
assert(!streamingTableOverflow(table, ssb, ssb_nc));
|
|
|
|
#if defined(ARCH_32_BIT)
|
|
// On 32-bit hosts, we may be able to avoid having to do any u64a
|
|
// manipulation at all.
|
|
if (ss_bytes <= 4) {
|
|
u32 stagingStreamState = table[CASEFUL];
|
|
stagingStreamState |= (table[CASELESS] << ssb);
|
|
|
|
partial_store_u32(stream_state, stagingStreamState, ss_bytes);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
u64a stagingStreamState = (u64a)table[CASEFUL];
|
|
stagingStreamState |= (u64a)table[CASELESS] << ((u64a)ssb);
|
|
partial_store_u64a(stream_state, stagingStreamState, ss_bytes);
|
|
}
|
|
|
|
u32 fdrStreamStateActive(const struct FDR * fdr, const u8 * stream_state) {
|
|
if (!stream_state) {
|
|
return 0;
|
|
}
|
|
const struct FDRSTableHeader * streamingTable = getSHDR(fdr);
|
|
u8 ss_bytes = streamingTable->streamStateBytes;
|
|
|
|
// We just care if there are any bits set, and the test below is faster
|
|
// than a partial_load_u64a (especially on 32-bit hosts).
|
|
for (u32 i = 0; i < ss_bytes; i++) {
|
|
if (*stream_state) {
|
|
return 1;
|
|
}
|
|
++stream_state;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// binary search for the literal index that contains the current state
|
|
static really_inline
|
|
u32 findLitTabEntry(const struct FDRSTableHeader * streamingTable,
|
|
u32 stateValue, MODES m) {
|
|
const struct FDRSLiteral * litTab = getLitTab(streamingTable);
|
|
u32 lo = get_start_lit_idx(streamingTable, m);
|
|
u32 hi = get_end_lit_idx(streamingTable, m);
|
|
|
|
// Now move stateValue back by one so that we're looking for the
|
|
// litTab entry that includes it the string, not the one 'one past' it
|
|
stateValue -= 1;
|
|
assert(lo != hi);
|
|
assert(litTab[lo].offset <= stateValue);
|
|
assert(litTab[hi].offset > stateValue);
|
|
|
|
// binary search to find the entry e such that:
|
|
// litTab[e].offsetToLiteral <= stateValue < litTab[e+1].offsetToLiteral
|
|
while (lo + 1 < hi) {
|
|
u32 mid = (lo + hi) / 2;
|
|
if (litTab[mid].offset <= stateValue) {
|
|
lo = mid;
|
|
} else { //(litTab[mid].offset > stateValue) {
|
|
hi = mid;
|
|
}
|
|
}
|
|
assert(litTab[lo].offset <= stateValue);
|
|
assert(litTab[hi].offset > stateValue);
|
|
return lo;
|
|
}
|
|
|
|
static really_inline
|
|
void fdrUnpackStateMode(struct FDR_Runtime_Args *a,
|
|
const struct FDRSTableHeader *streamingTable,
|
|
const struct FDRSLiteral * litTab,
|
|
const u32 *state_table,
|
|
const MODES m) {
|
|
if (!state_table[m]) {
|
|
return;
|
|
}
|
|
|
|
u32 stateValue = unpackStateVal(streamingTable, m, state_table[m]);
|
|
u32 idx = findLitTabEntry(streamingTable, stateValue, m);
|
|
size_t found_offset = litTab[idx].offset;
|
|
const u8 * found_buf = found_offset + (const u8 *)streamingTable;
|
|
size_t found_sz = stateValue - found_offset;
|
|
if (m == CASEFUL) {
|
|
a->buf_history = found_buf;
|
|
a->len_history = found_sz;
|
|
} else {
|
|
a->buf_history_nocase = found_buf;
|
|
a->len_history_nocase = found_sz;
|
|
}
|
|
}
|
|
|
|
static really_inline
|
|
void fdrUnpackState(const struct FDR * fdr, struct FDR_Runtime_Args * a,
|
|
const u8 * stream_state) {
|
|
// nothing to do if there's no stream state for the case
|
|
if (!stream_state) {
|
|
return;
|
|
}
|
|
|
|
const struct FDRSTableHeader * streamingTable = getSHDR(fdr);
|
|
const struct FDRSLiteral * litTab = getLitTab(streamingTable);
|
|
|
|
u32 state_table[MAX_MODES];
|
|
getStreamStates(streamingTable, stream_state, state_table);
|
|
|
|
fdrUnpackStateMode(a, streamingTable, litTab, state_table, CASEFUL);
|
|
fdrUnpackStateMode(a, streamingTable, litTab, state_table, CASELESS);
|
|
}
|
|
|
|
static really_inline
|
|
u32 do_single_confirm(const struct FDRSTableHeader * streamingTable,
|
|
const struct FDR_Runtime_Args * a, u32 hashState, MODES m) {
|
|
const struct FDRSLiteral * litTab = getLitTab(streamingTable);
|
|
u32 idx = findLitTabEntry(streamingTable, hashState, m);
|
|
size_t found_offset = litTab[idx].offset;
|
|
const u8 * s1 = found_offset + (const u8 *)streamingTable;
|
|
assert(hashState > found_offset);
|
|
size_t l1 = hashState - found_offset;
|
|
const u8 * buf = a->buf;
|
|
size_t len = a->len;
|
|
const char nocase = m != CASEFUL;
|
|
|
|
if (l1 > len) {
|
|
const u8 * hist = nocase ? a->buf_history_nocase : a->buf_history;
|
|
size_t hist_len = nocase ? a->len_history_nocase : a->len_history;
|
|
|
|
if (l1 > len+hist_len) {
|
|
return 0; // Break out - not enough total history
|
|
}
|
|
|
|
size_t overhang = l1 - len;
|
|
assert(overhang <= hist_len);
|
|
|
|
if (cmpForward(hist + hist_len - overhang, s1, overhang, nocase)) {
|
|
return 0;
|
|
}
|
|
s1 += overhang;
|
|
l1 -= overhang;
|
|
}
|
|
// if we got here, we don't need history or we compared ok out of history
|
|
assert(l1 <= len);
|
|
|
|
if (cmpForward(buf + len - l1, s1, l1, nocase)) {
|
|
return 0;
|
|
}
|
|
return hashState; // our new state
|
|
}
|
|
|
|
static really_inline
|
|
void fdrFindStreamingHash(const struct FDR_Runtime_Args *a,
|
|
const struct FDRSTableHeader *streamingTable,
|
|
u8 hash_len, u32 *hashes) {
|
|
u8 tempbuf[128];
|
|
const u8 *base;
|
|
if (hash_len > a->len) {
|
|
assert(hash_len <= 128);
|
|
size_t overhang = hash_len - a->len;
|
|
assert(overhang <= a->len_history);
|
|
memcpy(tempbuf, a->buf_history + a->len_history - overhang, overhang);
|
|
memcpy(tempbuf + overhang, a->buf, a->len);
|
|
base = tempbuf;
|
|
} else {
|
|
assert(hash_len <= a->len);
|
|
base = a->buf + a->len - hash_len;
|
|
}
|
|
|
|
if (streamingTable->hashNBits[CASEFUL]) {
|
|
hashes[CASEFUL] = streaming_hash(base, hash_len, CASEFUL);
|
|
}
|
|
if (streamingTable->hashNBits[CASELESS]) {
|
|
hashes[CASELESS] = streaming_hash(base, hash_len, CASELESS);
|
|
}
|
|
}
|
|
|
|
static really_inline
|
|
const struct FDRSHashEntry *getEnt(const struct FDRSTableHeader *streamingTable,
|
|
u32 h, const MODES m) {
|
|
u32 nbits = streamingTable->hashNBits[m];
|
|
if (!nbits) {
|
|
return NULL;
|
|
}
|
|
|
|
u32 h_ent = h & ((1 << nbits) - 1);
|
|
u32 h_low = (h >> nbits) & 63;
|
|
|
|
const struct FDRSHashEntry *tab =
|
|
(const struct FDRSHashEntry *)((const u8 *)streamingTable
|
|
+ streamingTable->hashOffset[m]);
|
|
const struct FDRSHashEntry *ent = tab + h_ent;
|
|
|
|
if (!has_bit(ent, h_low)) {
|
|
return NULL;
|
|
}
|
|
|
|
return ent;
|
|
}
|
|
|
|
static really_inline
|
|
void fdrPackStateMode(u32 *state_table, const struct FDR_Runtime_Args *a,
|
|
const struct FDRSTableHeader *streamingTable,
|
|
const struct FDRSHashEntry *ent, const MODES m) {
|
|
assert(ent);
|
|
assert(streamingTable->hashNBits[m]);
|
|
|
|
const struct FDRSHashEntry *tab =
|
|
(const struct FDRSHashEntry *)((const u8 *)streamingTable
|
|
+ streamingTable->hashOffset[m]);
|
|
|
|
while (1) {
|
|
u32 tmp = 0;
|
|
if ((tmp = do_single_confirm(streamingTable, a, ent->state, m))) {
|
|
state_table[m] = packStateVal(streamingTable, m, tmp);
|
|
break;
|
|
}
|
|
if (ent->link == LINK_INVALID) {
|
|
break;
|
|
}
|
|
ent = tab + ent->link;
|
|
}
|
|
}
|
|
|
|
static really_inline
|
|
void fdrPackState(const struct FDR *fdr, const struct FDR_Runtime_Args *a,
|
|
u8 *stream_state) {
|
|
// nothing to do if there's no stream state for the case
|
|
if (!stream_state) {
|
|
return;
|
|
}
|
|
|
|
// get pointers to the streamer FDR and the tertiary structure
|
|
const struct FDRSTableHeader *streamingTable = getSHDR(fdr);
|
|
|
|
assert(streamingTable->N);
|
|
|
|
u32 state_table[MAX_MODES] = {0, 0};
|
|
|
|
// if we don't have enough history, we don't need to do anything
|
|
if (streamingTable->N <= a->len + a->len_history) {
|
|
u32 hashes[MAX_MODES] = {0, 0};
|
|
|
|
fdrFindStreamingHash(a, streamingTable, streamingTable->N, hashes);
|
|
|
|
const struct FDRSHashEntry *ent_ful = getEnt(streamingTable,
|
|
hashes[CASEFUL], CASEFUL);
|
|
const struct FDRSHashEntry *ent_less = getEnt(streamingTable,
|
|
hashes[CASELESS], CASELESS);
|
|
|
|
if (ent_ful) {
|
|
fdrPackStateMode(state_table, a, streamingTable, ent_ful,
|
|
CASEFUL);
|
|
}
|
|
|
|
if (ent_less) {
|
|
fdrPackStateMode(state_table, a, streamingTable, ent_less,
|
|
CASELESS);
|
|
}
|
|
}
|
|
|
|
setStreamStates(streamingTable, stream_state, state_table);
|
|
}
|
|
|
|
#endif
|