forked from Mirrors/privacore-open-source-search-engine
384 lines
8.9 KiB
C++
384 lines
8.9 KiB
C++
#include "gb-include.h"
|
|
|
|
#include "Bits.h"
|
|
#include "StopWords.h"
|
|
#include "fctypes.h"
|
|
#include "Abbreviations.h"
|
|
#include "Mem.h"
|
|
#include "Sections.h"
|
|
#include "Process.h"
|
|
|
|
|
|
Bits::Bits() {
|
|
m_bits = NULL;
|
|
m_swbits = NULL;
|
|
memset(m_localBuf, 0, sizeof(m_localBuf));
|
|
reset();
|
|
}
|
|
|
|
Bits::~Bits() {
|
|
reset();
|
|
}
|
|
|
|
void Bits::reset() {
|
|
if ( m_bits && m_needsFree ) // (char *)m_bits != m_localBuf )
|
|
mfree ( m_bits , m_bitsSize , "Bits" );
|
|
if ( m_swbits && m_needsFree )
|
|
mfree ( m_swbits , m_swbitsSize , "Bits" );
|
|
m_bits = NULL;
|
|
m_swbits = NULL;
|
|
m_inLinkBitsSet = false;
|
|
m_inUrlBitsSet = false;
|
|
|
|
// Coverity
|
|
m_bitsSize = 0;
|
|
m_swbitsSize = 0;
|
|
m_words = NULL;
|
|
m_needsFree = false;
|
|
}
|
|
|
|
// . set bits for each word
|
|
// . these bits are used for phrasing and by spam detector
|
|
// . returns false and sets errno on error
|
|
bool Bits::set(const Words *words) {
|
|
reset();
|
|
|
|
// save words so printBits works
|
|
m_words = words;
|
|
// how many words?
|
|
int32_t numBits = words->getNumWords();
|
|
// how much space do we need?
|
|
int32_t need = numBits * sizeof(wbit_t);
|
|
// assume no malloc
|
|
m_needsFree = false;
|
|
|
|
// use local buf?
|
|
if ( need < BITS_LOCALBUFSIZE ) {
|
|
m_bits = (wbit_t *) m_localBuf;
|
|
} else {
|
|
m_bitsSize = need;
|
|
m_bits = (wbit_t *)mmalloc ( need , "Bits1" );
|
|
m_needsFree = true;
|
|
}
|
|
if ( ! m_bits ) {
|
|
log("build: Could not allocate Bits table used to parse words: %s", mstrerror(g_errno));
|
|
return false;
|
|
}
|
|
|
|
const nodeid_t *tagIds = words->getTagIds();
|
|
const char *const*w = words->getWordPtrs();
|
|
|
|
int32_t brcount = 0;
|
|
|
|
wbit_t bits;
|
|
|
|
for ( int32_t i = 0 ; i < numBits ; i++ ) {
|
|
if ( tagIds && tagIds[i] ) {
|
|
nodeid_t tid = tagIds[i] & BACKBITCOMP;
|
|
// count the <br>s, we can't pair across more than 1
|
|
if ( g_nodes[tid].m_isBreaking ) {
|
|
bits = 0;
|
|
} else if ( tid == TAG_BR ) {
|
|
// can only pair across one <br> tag, not two
|
|
if ( brcount > 0 ) {
|
|
bits = 0;
|
|
} else {
|
|
brcount++;
|
|
bits = D_CAN_PAIR_ACROSS;
|
|
}
|
|
} else {
|
|
bits = D_CAN_PAIR_ACROSS;
|
|
}
|
|
}
|
|
else if ( is_alnum_utf8 ( w[i]+0 )) {
|
|
bits = getAlnumBits( i );
|
|
brcount = 0;
|
|
} else {
|
|
// . just allow anything now!
|
|
// . the curved quote in utf8 is 3 bytes long and with
|
|
// a space before it, was causing issues here!
|
|
bits= D_CAN_PAIR_ACROSS;
|
|
}
|
|
|
|
// remember our bits.
|
|
m_bits [ i ] = bits;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Bits::setInLinkBits ( Sections *ss ) {
|
|
|
|
if ( m_inLinkBitsSet ) return;
|
|
m_inLinkBitsSet = true;
|
|
if ( ss->m_numSections == 0 ) return;
|
|
// sets bits for Bits.cpp for D_IN_LINK for each ALNUM word
|
|
for ( Section *si = ss->m_rootSection ; si ; si = si->m_next ) {
|
|
// skip if not a href section
|
|
if ( si->m_baseHash != TAG_A ) continue;
|
|
// set boundaries
|
|
int32_t a = si->m_a;
|
|
int32_t b = si->m_b;
|
|
for ( int32_t i = a ; i < b ; i++ )
|
|
m_bits[i] |= D_IN_LINK;
|
|
}
|
|
}
|
|
|
|
void Bits::setInUrlBits () {
|
|
if ( m_inUrlBitsSet ) return;
|
|
m_inUrlBitsSet = true;
|
|
const nodeid_t *tids = m_words->getTagIds();
|
|
const int64_t *wids = m_words->getWordIds();
|
|
const char *const*wptrs = m_words->getWordPtrs();
|
|
int32_t nw = m_words->getNumWords();
|
|
for ( int32_t i = 0 ; i < nw; i++ ) {
|
|
// look for protocol
|
|
if ( wids[i] ) continue;
|
|
if ( tids[i] ) continue;
|
|
if ( wptrs[i][0] != ':' ) continue;
|
|
if ( wptrs[i][1] != '/' ) continue;
|
|
if ( wptrs[i][2] != '/' ) continue;
|
|
// set them up
|
|
if ( i<= 0 ) continue;
|
|
|
|
// scan for end of it. stop at tag or space
|
|
int32_t j = i - 1;
|
|
for ( ; j < nw; j++ ) {
|
|
// check if end
|
|
if ( m_words->hasSpace( j ) ) {
|
|
break;
|
|
}
|
|
|
|
// or tag
|
|
if ( tids[j] ) {
|
|
break;
|
|
}
|
|
// include it
|
|
m_bits[j] |= D_IS_IN_URL;
|
|
}
|
|
|
|
// avoid inifinite loop with this if conditional statement
|
|
if ( j > i ) {
|
|
i = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
// . if we're a stop word and previous word was an apostrophe
|
|
// then set D_CAN_APOSTROPHE_PRECEED to true and PERIOD_PRECEED to false
|
|
wbit_t Bits::getAlnumBits( int32_t i ) const {
|
|
// this is not case sensitive -- all non-stop words can start phrases
|
|
if ( ! ::isStopWord ( m_words->getWord( i ) , m_words->getWordLen( i ) , m_words->getWordId( i ) ) )
|
|
return (D_CAN_BE_IN_PHRASE) ;
|
|
|
|
return (D_CAN_BE_IN_PHRASE | D_CAN_PAIR_ACROSS | D_IS_STOPWORD);
|
|
}
|
|
|
|
//
|
|
// Summary.cpp sets its own bits.
|
|
//
|
|
|
|
// this table maps a tagId to a #define'd bit from Bits.h which describes
|
|
// the format of the following text in the page. like bold or italics, etc.
|
|
static nodeid_t s_bt [ 512 ];
|
|
static bool s_init = false;
|
|
|
|
// . set bits for each word
|
|
// . these bits are used for phrasing and by spam detector
|
|
// . returns false and sets errno on error
|
|
bool Bits::setForSummary ( const Words *words ) {
|
|
// clear the mem
|
|
reset();
|
|
|
|
// set our s_bt[] table
|
|
if ( ! s_init ) {
|
|
// only do this once
|
|
s_init = true;
|
|
|
|
// clear table
|
|
if ( getNumXmlNodes() > 512 ) {
|
|
g_process.shutdownAbort(true);
|
|
}
|
|
memset ( s_bt, 0, sizeof(s_bt) );
|
|
// set just those that have bits #defined in Bits.h
|
|
s_bt [ TAG_TITLE ] = D_IN_TITLE;
|
|
s_bt [ TAG_A ] = D_IN_HYPERLINK;
|
|
s_bt [ TAG_B ] = D_IN_BOLDORITALICS;
|
|
s_bt [ TAG_I ] = D_IN_BOLDORITALICS;
|
|
s_bt [ TAG_LI ] = D_IN_LIST;
|
|
s_bt [ TAG_SUP ] = D_IN_SUP;
|
|
s_bt [ TAG_P ] = D_IN_PARAGRAPH;
|
|
s_bt [ TAG_BLOCKQUOTE ] = D_IN_BLOCKQUOTE;
|
|
}
|
|
|
|
// save words so printBits works
|
|
m_words = words;
|
|
|
|
// how many words?
|
|
int32_t numBits = words->getNumWords();
|
|
|
|
// how much space do we need?
|
|
int32_t need = sizeof(swbit_t) * numBits;
|
|
|
|
// assume no malloc
|
|
m_needsFree = false;
|
|
|
|
// use local buf?
|
|
if ( need < BITS_LOCALBUFSIZE ) {
|
|
m_swbits = (swbit_t *)m_localBuf;
|
|
} else {
|
|
// i guess need to malloc
|
|
m_swbitsSize = need;
|
|
m_swbits = (swbit_t *)mmalloc( need, "BitsW" );
|
|
m_needsFree = true;
|
|
}
|
|
|
|
if ( !m_swbits ) {
|
|
log( LOG_WARN, "build: Could not allocate Bits table used to parse words: %s", mstrerror( g_errno ) );
|
|
return false;
|
|
}
|
|
|
|
// set
|
|
// D_STRONG_CONNECTOR
|
|
// D_STARTS_SENTENCE
|
|
// D_STARTS_FRAGMENT
|
|
|
|
const nodeid_t *tagIds = words->getTagIds();
|
|
const char *const*w = words->getWordPtrs();
|
|
const int32_t *wlens = words->getWordLens();
|
|
const int64_t *wids = words->getWordIds();
|
|
|
|
bool startSent = true;
|
|
bool startFrag = true;
|
|
bool inQuote = false;
|
|
bool inParens = false;
|
|
|
|
// the ongoing accumulation flag we apply to each word
|
|
swbit_t flags = 0;
|
|
|
|
for ( int32_t i = 0 ; i < numBits ; i++ ) {
|
|
// assume none are set
|
|
m_swbits[i] = 0;
|
|
|
|
// if a breaking tag, next guy can "start a sentence"
|
|
if ( tagIds && tagIds[i] ) {
|
|
// get the tag id minus the high "back bit"
|
|
int32_t tid = tagIds[i] & BACKBITCOMP;
|
|
|
|
// is it a "breaking tag"?
|
|
if ( g_nodes[tid].m_isBreaking ) {
|
|
startSent = true;
|
|
inQuote = false;
|
|
}
|
|
|
|
// adjust flags if we should
|
|
if ( s_bt[tid] ) {
|
|
if ( tid != tagIds[i] ) {
|
|
flags &= ~s_bt[tid];
|
|
} else {
|
|
flags |= s_bt[tid];
|
|
}
|
|
}
|
|
|
|
// apply flag
|
|
m_swbits[i] |= flags;
|
|
continue;
|
|
}
|
|
|
|
// if alnum, might start sentence or fragment
|
|
if ( wids[i] ) {
|
|
if ( startFrag ) {
|
|
m_swbits[i] |= D_STARTS_FRAG;
|
|
startFrag = false;
|
|
}
|
|
|
|
if ( startSent ) {
|
|
m_swbits[i] |= D_STARTS_SENTENCE;
|
|
startSent = false;
|
|
}
|
|
|
|
if ( inQuote ) {
|
|
m_swbits[i] |= D_IN_QUOTES;
|
|
inQuote = false;
|
|
}
|
|
|
|
if ( inParens ) {
|
|
m_swbits[i] |= D_IN_PARENS;
|
|
}
|
|
|
|
// apply any other flags we got
|
|
m_swbits[i] |= flags;
|
|
continue;
|
|
}
|
|
|
|
// fast ptrs
|
|
int32_t wlen = wlens[i];
|
|
const char *wp = w[i];
|
|
|
|
// this is not 100%
|
|
if ( words->hasChar( i, '(' ) ) {
|
|
flags |= D_IN_PARENS;
|
|
} else if ( words->hasChar( i, ')' ) ) {
|
|
flags &= ~D_IN_PARENS;
|
|
}
|
|
|
|
// apply curent flags
|
|
m_swbits[i] |= flags;
|
|
|
|
// does it END in a quote?
|
|
if ( wp[wlen - 1] == '\"' ) {
|
|
inQuote = true;
|
|
} else if ( wlen >= 6 && strncmp( wp, """, 6 ) == 0 ) {
|
|
inQuote = true;
|
|
}
|
|
|
|
// . but double spaces are not starters
|
|
// . MDW: we kinda force ourselves to only use ascii spaces here
|
|
if ( wlen == 2 && is_wspace_a( *wp ) && is_wspace_a( wp[1] ) ) {
|
|
continue;
|
|
}
|
|
|
|
// it can start a fragment if not a single space char
|
|
if ( wlen != 1 || !is_wspace_utf8( wp ) ) {
|
|
startFrag = true;
|
|
}
|
|
|
|
// ". " denotes end of sentence
|
|
if ( wlen >= 2 && wp[0] == '.' && is_wspace_utf8( wp + 1 ) ) {
|
|
// but not if preceeded by an initial
|
|
if ( i > 0 && wlens[i - 1] == 1 && wids[i - 1] ) {
|
|
continue;
|
|
}
|
|
|
|
// ok, really the end of a sentence
|
|
startSent = true;
|
|
}
|
|
|
|
// are we a "strong connector", meaning that
|
|
// Summary.cpp should not split on us if possible
|
|
|
|
// apostrophe html encoded?
|
|
if ( wlen == 6 && strncmp( wp, "’", 6 ) == 0 ) {
|
|
m_swbits[i] |= D_IS_STRONG_CONNECTOR;
|
|
continue;
|
|
}
|
|
if ( wlen == 7 && strncmp( wp, "’", 7 ) == 0 ) {
|
|
m_swbits[i] |= D_IS_STRONG_CONNECTOR;
|
|
continue;
|
|
}
|
|
|
|
// otherwise, strong connectors must be single char
|
|
if ( wlen != 1 ) {
|
|
continue;
|
|
}
|
|
|
|
// is it apostrophe? - & . * (M*A*S*H)
|
|
char c = wp[0];
|
|
if ( c == '\'' || c == '-' || c == '&' || c == '.' || c == '*' || c == '/' ) {
|
|
m_swbits[i] |= D_IS_STRONG_CONNECTOR;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|