*
* regis.c
* Fast regex subset
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* src/backend/tsearch/regis.c
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "tsearch/dicts/regis.h"
#include "tsearch/ts_locale.h"
#define RS_IN_ONEOF 1
#define RS_IN_ONEOF_IN 2
#define RS_IN_NONEOF 3
#define RS_IN_WAIT 4
* Test whether a regex is of the subset supported here.
* Keep this in sync with RS_compile!
*/
bool RS_isRegis(const char* str)
{
int state = RS_IN_WAIT;
const char* c = str;
while (*c) {
if (state == RS_IN_WAIT) {
if (t_isalpha(c)) {
;
} else if (t_iseq(c, '[')) {
state = RS_IN_ONEOF;
} else {
return false;
}
} else if (state == RS_IN_ONEOF) {
if (t_iseq(c, '^')) {
state = RS_IN_NONEOF;
} else if (t_isalpha(c)) {
state = RS_IN_ONEOF_IN;
} else {
return false;
}
} else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF) {
if (t_isalpha(c)) {
;
} else if (t_iseq(c, ']')) {
state = RS_IN_WAIT;
} else {
return false;
}
} else {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("internal error in RS_isRegis: state %d", state)));
}
c += pg_mblen(c);
}
return (state == RS_IN_WAIT);
}
static RegisNode* newRegisNode(RegisNode* prev, int len)
{
RegisNode* ptr = NULL;
ptr = (RegisNode*)palloc0(RNHDRSZ + len + 1);
if (prev != NULL) {
prev->next = ptr;
}
return ptr;
}
void RS_compile(Regis* r, bool issuffix, const char* str)
{
int len = strlen(str);
int state = RS_IN_WAIT;
const char* c = str;
RegisNode* ptr = NULL;
errno_t errorno = EOK;
errorno = memset_s(r, sizeof(Regis), 0, sizeof(Regis));
securec_check(errorno, "\0", "\0");
r->issuffix = (issuffix) ? 1 : 0;
while (*c) {
if (state == RS_IN_WAIT) {
if (t_isalpha(c)) {
if (ptr != NULL) {
ptr = newRegisNode(ptr, len);
} else {
ptr = r->node = newRegisNode(NULL, len);
}
COPYCHAR(ptr->data, c);
ptr->type = RSF_ONEOF;
ptr->len = pg_mblen(c);
} else if (t_iseq(c, '[')) {
if (ptr != NULL) {
ptr = newRegisNode(ptr, len);
} else {
ptr = r->node = newRegisNode(NULL, len);
}
ptr->type = RSF_ONEOF;
state = RS_IN_ONEOF;
} else {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("invalid regis pattern: \"%s\"", str)));
}
} else if (state == RS_IN_ONEOF) {
if (t_iseq(c, '^')) {
ptr->type = RSF_NONEOF;
state = RS_IN_NONEOF;
} else if (t_isalpha(c)) {
COPYCHAR(ptr->data, c);
ptr->len = pg_mblen(c);
state = RS_IN_ONEOF_IN;
} else {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("invalid regis pattern: \"%s\"", str)));
}
} else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF) {
if (t_isalpha(c)) {
COPYCHAR(ptr->data + ptr->len, c);
ptr->len += pg_mblen(c);
} else if (t_iseq(c, ']')) {
state = RS_IN_WAIT;
} else {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("invalid regis pattern: \"%s\"", str)));
}
} else {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("internal error in RS_compile: state %d", state)));
}
c += pg_mblen(c);
}
if (state != RS_IN_WAIT) {
ereport(
ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("invalid regis pattern: \"%s\"", str)));
}
ptr = r->node;
while (ptr != NULL) {
r->nchar++;
ptr = ptr->next;
}
}
void RS_free(Regis* r)
{
RegisNode *ptr = r->node;
RegisNode *tmp = NULL;
while (ptr != NULL) {
tmp = ptr->next;
pfree_ext(ptr);
ptr = tmp;
}
r->node = NULL;
}
#ifdef USE_WIDE_UPPER_LOWER
static bool mb_strchr(char* str, const char* c)
{
int clen, plen, i;
char* ptr = str;
bool res = false;
clen = pg_mblen(c);
while (*ptr && !res) {
plen = pg_mblen(ptr);
if (plen == clen) {
i = plen;
res = true;
while (i--) {
if (*(ptr + i) != *(c + i)) {
res = false;
break;
}
}
}
ptr += plen;
}
return res;
}
#else
#define mb_strchr(s, c) ((strchr((s), *(c)) == NULL) ? false : true)
#endif
bool RS_execute(Regis* r, char* str)
{
RegisNode* ptr = r->node;
char* c = str;
int len = 0;
while (*c) {
len++;
c += pg_mblen(c);
}
if (len < r->nchar) {
return 0;
}
c = str;
if (r->issuffix) {
len -= r->nchar;
while (len-- > 0) {
c += pg_mblen(c);
}
}
while (ptr != NULL) {
switch (ptr->type) {
case RSF_ONEOF:
if (!mb_strchr((char*)ptr->data, c)) {
return false;
}
break;
case RSF_NONEOF:
if (mb_strchr((char*)ptr->data, c)) {
return false;
}
break;
default:
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized regis node type: %u", ptr->type)));
}
ptr = ptr->next;
c += pg_mblen(c);
}
return true;
}