Commit 7f187041 authored by Geoff Simmons's avatar Geoff Simmons

Add methods .hasprefix(), .nmatches(), .matched() and .which().

parent c517ccfb
......@@ -26,7 +26,11 @@ CONTENTS
* :ref:`obj_set`
* :ref:`func_set.add`
* :ref:`func_set.debug`
* :ref:`func_set.hasprefix`
* :ref:`func_set.match`
* :ref:`func_set.matched`
* :ref:`func_set.nmatches`
* :ref:`func_set.which`
* :ref:`func_version`
......@@ -107,6 +111,68 @@ Example::
}
.. _func_set.hasprefix:
BOOL xset.hasprefix(STRING)
---------------------------
Returns ``true`` if and only if ...
Example::
if (myset.hasprefix(req.url)) {
call do_if_prefix_matched;
}
.. _func_set.nmatches:
INT xset.nmatches()
-------------------
Returns ...
Example::
if (myset.hasprefix(req.url)) {
# ...
}
.. _func_set.matched:
BOOL xset.matched(INT)
----------------------
Returns ...
Example::
if (myset.hasprefix(req.url)) {
# ...
}
.. _func_set.which:
INT xset.which(ENUM select)
---------------------------
::
INT xset.which(
ENUM {UNIQUE, FIRST, LAST, SHORTEST, LONGEST} select=UNIQUE
)
Returns ...
Example::
if (myset.hasprefix(req.url)) {
# ...
}
.. _func_set.debug:
STRING xset.debug()
......
......@@ -256,6 +256,64 @@ PT_Lookup(const struct pt_y * const restrict root,
return (UINT_MAX);
}
static int
pt_search(const struct pt_y * const restrict y,
char * const restrict * const restrict strings,
const unsigned char * restrict c,
struct match_data * const restrict match)
{
const unsigned char * restrict s;
unsigned short i;
if (y == NULL)
return (0);
CHECK_OBJ(y, PT_Y_MAGIC);
s = (unsigned char *)strings[y->idx] + y->off;
for (i = 0; *c != '\0' && i < y->len && s[i] == *c; i++)
c++;
if (s[i] == '\0') {
if (match->n == match->limit)
return (-1);
match->indices[match->n] = y->idx;
match->n++;
if (y->idx < match->min)
match->min = y->idx;
if (y->idx > match->max)
match->max = y->idx;
}
if (i < y->len)
return (0);
if (pt_search(y->leaf[0], strings, c, match) != 0)
return (-1);
if (pt_search(y->leaf[1], strings, c, match) != 0)
return (-1);
return (0);
}
int
PT_Prefixes(const struct pt_y * const restrict root,
char * const restrict * const restrict strings,
const char * const restrict subject,
struct match_data * const restrict match)
{
CHECK_OBJ_NOTNULL(match, MATCH_DATA_MAGIC);
AN(match->indices);
AN(match->limit);
AN(strings);
AN(subject);
match->n = 0;
match->min = UINT_MAX;
match->max = 0;
return (pt_search(root, strings, (unsigned char *)subject, match));
}
void
PT_Free(struct pt_y *y)
{
......
......@@ -34,16 +34,15 @@
struct pt_y;
#if 0
struct t_stats {
struct match_data {
unsigned magic;
#define T_STATS_MAGIC 0xf92e6603
double d_mean;
unsigned d_max;
unsigned n_nodes;
unsigned n_leaves;
}
#endif
#define MATCH_DATA_MAGIC 0x0d9a845e
unsigned *indices;
unsigned limit;
unsigned n;
unsigned min;
unsigned max;
};
void PT_Init(void);
int PT_Inited(void);
......@@ -52,5 +51,9 @@ int PT_Insert(struct pt_y * * restrict root, unsigned idx,
unsigned PT_Lookup(const struct pt_y * const restrict root,
char * const restrict * const restrict strings,
const char * const restrict subject);
int PT_Prefixes(const struct pt_y * const restrict root,
char * const restrict * const restrict strings,
const char * const restrict subject,
struct match_data * const restrict match);
void PT_Free(struct pt_y *y);
struct vsb * PT_Dump(struct pt_y *root, char **strings);
This diff is collapsed.
......@@ -39,6 +39,7 @@
#include "vre.h"
#include "cache/cache_director.h"
#include "vcc_if.h"
#include "patricia.h"
#define VFAIL(ctx, fmt, ...) \
......@@ -48,6 +49,9 @@
VSLb((ctx)->vsl, SLT_VCL_Error, "vmod selector error: " fmt, \
__VA_ARGS__)
#define VERRNOMEM(ctx, fmt, ...) \
VERR((ctx), "out of space: " fmt, __VA_ARGS__)
struct entry {
unsigned magic;
#define VMOD_SELECTOR_ENTRY_MAGIC 0x733dbe63
......@@ -205,11 +209,42 @@ vmod_set_add(VRT_CTX, struct vmod_selector_set *set, VCL_STRING member,
set->table[n - 1] = entry;
}
static struct match_data *
get_match_data(VRT_CTX, struct vmod_selector_set *set, const char *method)
{
struct vmod_priv *task;
struct match_data *match;
task = VRT_priv_task(ctx, set);
AN(task);
if (task->priv == NULL) {
if ((task->priv = WS_Alloc(ctx->ws, sizeof(*match))) == NULL) {
VERRNOMEM(ctx, "Allocating match data in %s.%s()",
set->vcl_name, method);
return (NULL);
}
task->len = sizeof(*match);
task->free = NULL;
match = (struct match_data *)task->priv;
match->magic = MATCH_DATA_MAGIC;
}
else {
WS_Assert_Allocated(ctx->ws, task->priv, sizeof(*match));
CAST_OBJ(match, task->priv, MATCH_DATA_MAGIC);
}
return match;
}
VCL_BOOL
vmod_set_match(VRT_CTX, struct vmod_selector_set *set, VCL_STRING subject)
{
unsigned idx;
struct match_data *match;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
AN(set->origo);
AN(set->members);
if (subject == NULL) {
VERR(ctx, "%s.match(): subject string is NULL", set->vcl_name);
......@@ -220,9 +255,162 @@ vmod_set_match(VRT_CTX, struct vmod_selector_set *set, VCL_STRING subject)
return (0);
}
match = get_match_data(ctx, set, "match");
if ((idx = PT_Lookup(set->origo, set->members, subject)) == UINT_MAX) {
match->n = 0;
return (0);
}
if ((match->indices = WS_Alloc(ctx->ws, sizeof(unsigned))) == NULL) {
VERRNOMEM(ctx, "Reserving space for index in "
"%s.match(\"%.40s\")", set->vcl_name, subject);
return (0);
}
*match->indices = idx;
match->n = 1;
return (1);
}
VCL_BOOL
vmod_set_hasprefix(VRT_CTX, struct vmod_selector_set *set, VCL_STRING subject)
{
struct match_data *match;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
AN(set->origo);
AN(set->members);
return (PT_Lookup(set->origo, set->members, subject) != UINT_MAX);
if (subject == NULL) {
VERR(ctx, "%s.hasprefix(): subject string is NULL",
set->vcl_name);
return (0);
}
if (set->nmembers == 0) {
VERR(ctx, "%s.hasprefix(): no entries were added",
set->vcl_name);
return (0);
}
match = get_match_data(ctx, set, "hasprefix");
if ((match->limit = WS_ReserveLumps(ctx->ws, sizeof(unsigned))) == 0) {
VERRNOMEM(ctx, "Reserving index array in "
"%s.hasprefix(\"%.40s\")", set->vcl_name, subject);
return (0);
}
match->indices = (unsigned *)WS_Front(ctx->ws);
if (PT_Prefixes(set->origo, set->members, subject, match) != 0) {
VERRNOMEM(ctx, "Adding indices in %s.hasprefix(\"%.40s\")",
set->vcl_name, subject);
WS_Release(ctx->ws, 0);
return (0);
}
WS_Release(ctx->ws, match->n * sizeof(unsigned));
return (match->n > 0);
}
static struct match_data *
get_existing_match_data(VRT_CTX,
const struct vmod_selector_set * const restrict set,
const char * const restrict method)
{
struct vmod_priv *task;
struct match_data *match;
task = VRT_priv_task(ctx, set);
AN(task);
if (task->priv == NULL) {
VERR(ctx, "%s.%s() called without prior match", set->vcl_name,
method);
return (NULL);
}
WS_Assert_Allocated(ctx->ws, task->priv, sizeof(*match));
CAST_OBJ(match, task->priv, MATCH_DATA_MAGIC);
return match;
}
VCL_INT
vmod_set_nmatches(VRT_CTX, struct vmod_selector_set *set)
{
struct match_data *match;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
match = get_existing_match_data(ctx, set, "nmatches");
if (match == NULL)
return (0);
return (match->n);
}
VCL_BOOL
vmod_set_matched(VRT_CTX, struct vmod_selector_set *set, VCL_INT idx)
{
struct match_data *match;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
if (idx < 1 || idx > set->nmembers) {
VERR(ctx, "%s.matched(%ld) out of range (%d members)",
set->vcl_name, idx, set->nmembers);
return (0);
}
match = get_existing_match_data(ctx, set, "matched");
if (match == NULL || match->n == 0)
return (0);
AN(match->indices);
WS_Assert_Allocated(ctx->ws, match->indices,
match->n * sizeof(unsigned));
/* XXX search algorithm? */
idx--;
for (unsigned i = 0; i < match->n; i++)
if (match->indices[i] == idx)
return (1);
return (0);
}
static unsigned
select(VRT_CTX, const struct match_data * const restrict match,
const char * const restrict obj, VCL_ENUM const restrict selects,
const char * const restrict method)
{
if (selects == vmod_enum_UNIQUE) {
if (match->n != 1) {
VERR(ctx, "%s.%s(select=UNIQUE): "
"%d elements were matched", obj, method, match->n);
return (UINT_MAX);
}
return match->indices[0];
}
if (selects == vmod_enum_FIRST)
return match->min;
if (selects == vmod_enum_LAST)
return match->max;
if (selects == vmod_enum_SHORTEST)
return match->indices[0];
if (selects == vmod_enum_LONGEST)
return match->indices[match->n - 1];
WRONG("illegal select enum");
return (UINT_MAX);
}
VCL_INT
vmod_set_which(VRT_CTX, struct vmod_selector_set *set, VCL_ENUM selects)
{
struct match_data *match;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
match = get_existing_match_data(ctx, set, "which");
if (match == NULL || match->n == 0)
return (0);
return (select(ctx, match, set->vcl_name, selects, "which") + 1);
}
VCL_STRING
......
......@@ -64,6 +64,46 @@ Example::
call do_on_match;
}
$Method BOOL .hasprefix(STRING)
Returns ``true`` if and only if ...
Example::
if (myset.hasprefix(req.url)) {
call do_if_prefix_matched;
}
$Method INT .nmatches()
Returns ...
Example::
if (myset.hasprefix(req.url)) {
# ...
}
$Method BOOL .matched(INT)
Returns ...
Example::
if (myset.hasprefix(req.url)) {
# ...
}
$Method INT .which(ENUM {UNIQUE, FIRST, LAST, SHORTEST, LONGEST} select=UNIQUE)
Returns ...
Example::
if (myset.hasprefix(req.url)) {
# ...
}
$Method STRING .debug()
Intentionally not documented.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment