Commit 79668197 authored by Geoff Simmons's avatar Geoff Simmons

Add the allow_overlaps flag.

parent 11ac404d
...@@ -321,8 +321,15 @@ or the longest match, and so on:: ...@@ -321,8 +321,15 @@ or the longest match, and so on::
.. _selector.set(): .. _selector.set():
new xset = selector.set(BOOL case_sensitive=1) new xset = selector.set(BOOL case_sensitive, BOOL allow_overlaps)
---------------------------------------------- -----------------------------------------------------------------
::
new xset = selector.set(
BOOL case_sensitive=1,
BOOL allow_overlaps=1
)
Create a set object. When ``case_sensitive`` is ``false``, matches Create a set object. When ``case_sensitive`` is ``false``, matches
using the ``.match()`` and ``.hasprefix()`` methods are using the ``.match()`` and ``.hasprefix()`` methods are
......
...@@ -159,7 +159,8 @@ getidx(const struct qp_y * const restrict y, uint16_t bitmap) ...@@ -159,7 +159,8 @@ getidx(const struct qp_y * const restrict y, uint16_t bitmap)
int int
QP_Insert(struct qp_y * * restrict root, unsigned idx, QP_Insert(struct qp_y * * restrict root, unsigned idx,
char * const restrict * const restrict strings) char * const restrict * const restrict strings,
unsigned allow_overlaps)
{ {
struct qp_y *y; struct qp_y *y;
unsigned char *c, *b; unsigned char *c, *b;
...@@ -205,6 +206,16 @@ QP_Insert(struct qp_y * * restrict root, unsigned idx, ...@@ -205,6 +206,16 @@ QP_Insert(struct qp_y * * restrict root, unsigned idx,
return (-1); return (-1);
} }
if (!allow_overlaps && i == y->len && y->term) {
/*
* The current node is terminating and has a
* prefix in common with the current string,
* reject if overlaps are not permitted.
*/
errno = EPERM;
return (-1);
}
if (i == y->len && y->branch != NULL) { if (i == y->len && y->branch != NULL) {
/* /*
* The string to be inserted has a prefix that is * The string to be inserted has a prefix that is
......
...@@ -61,7 +61,8 @@ struct qp_stats { ...@@ -61,7 +61,8 @@ struct qp_stats {
}; };
int QP_Insert(struct qp_y * * restrict root, unsigned idx, int QP_Insert(struct qp_y * * restrict root, unsigned idx,
char * const restrict * const restrict strings); char * const restrict * const restrict strings,
unsigned allow_overlaps);
unsigned QP_Lookup(const struct qp_y * const restrict root, unsigned QP_Lookup(const struct qp_y * const restrict root,
char * const restrict * const restrict strings, char * const restrict * const restrict strings,
const char * const restrict subject); const char * const restrict subject);
......
...@@ -98,7 +98,7 @@ void ...@@ -98,7 +98,7 @@ void
usage(const char *argv, int status) usage(const char *argv, int status)
{ {
fprintf(stderr, fprintf(stderr,
"Usage: %s [-hs] [-c csvfile] [-d dumpfile] [-i inputfile]\n" "Usage: %s [-hos] [-c csvfile] [-d dumpfile] [-i inputfile]\n"
" [-m m|p] [-n iterations] [file]\n", argv); " [-m m|p] [-n iterations] [file]\n", argv);
exit(status); exit(status);
} }
...@@ -121,10 +121,11 @@ main(int argc, char *argv[]) ...@@ -121,10 +121,11 @@ main(int argc, char *argv[])
struct timespec before, after, start, finish; struct timespec before, after, start, finish;
uint64_t ns = 0, iters, matches, exacts, misses; uint64_t ns = 0, iters, matches, exacts, misses;
struct qp_stats stats = { .magic = QP_STATS_MAGIC }; struct qp_stats stats = { .magic = QP_STATS_MAGIC };
int opt, do_shuf = 0, do_iters = ITERATIONS, do_match = 1, do_prefix = 1; int opt, do_shuf = 0, do_iters = ITERATIONS, do_match = 1
, do_prefix = 1, allow_overlaps = 1;
struct rusage rusage; struct rusage rusage;
while ((opt = getopt(argc, argv, "hsc:d:i:m:n:")) != -1) { while ((opt = getopt(argc, argv, "hosc:d:i:m:n:")) != -1) {
switch (opt) { switch (opt) {
case 'c': case 'c':
csvf = optarg; csvf = optarg;
...@@ -152,6 +153,9 @@ main(int argc, char *argv[]) ...@@ -152,6 +153,9 @@ main(int argc, char *argv[])
case 's': case 's':
do_shuf = 1; do_shuf = 1;
break; break;
case 'o':
allow_overlaps = 0;
break;
default: default:
usage(argv[0], EXIT_FAILURE); usage(argv[0], EXIT_FAILURE);
} }
...@@ -272,7 +276,7 @@ main(int argc, char *argv[]) ...@@ -272,7 +276,7 @@ main(int argc, char *argv[])
errno = 0; errno = 0;
(void)clock_gettime(CLOCK, &before); (void)clock_gettime(CLOCK, &before);
ret = QP_Insert(&origo, i, strings); ret = QP_Insert(&origo, i, strings, allow_overlaps);
(void)clock_gettime(CLOCK, &after); (void)clock_gettime(CLOCK, &after);
if (ret != 0) { if (ret != 0) {
......
# looks like -*- vcl -*-
varnishtest "allow_overlaps flag"
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub vcl_init {
new s = selector.set();
s.add("foo");
s.add("bar");
s.add("baz");
s.add("quux");
s.compile();
}
sub vcl_recv {
return (synth(200));
}
sub vcl_synth {
set resp.http.Match = s.hasprefix(req.http.Word);
set resp.http.N = s.nmatches();
set resp.http.Which = s.which();
set resp.http.Which-Unique = s.which(select=UNIQUE);
return (deliver);
}
} -start
client c1 {
txreq -hdr "Word: foobar"
rxresp
expect resp.status == 200
expect resp.http.Match == "true"
expect resp.http.N == "1"
expect resp.http.Which == "1"
expect resp.http.Which-Unique == resp.http.Which
txreq -hdr "Word: barbaz"
rxresp
expect resp.status == 200
expect resp.http.Match == "true"
expect resp.http.N == "1"
expect resp.http.Which == "2"
expect resp.http.Which-Unique == resp.http.Which
txreq -hdr "Word: bazquux"
rxresp
expect resp.status == 200
expect resp.http.Match == "true"
expect resp.http.N == "1"
expect resp.http.Which == "3"
expect resp.http.Which-Unique == resp.http.Which
txreq -hdr "Word: quuxxyzzy"
rxresp
expect resp.status == 200
expect resp.http.Match == "true"
expect resp.http.N == "1"
expect resp.http.Which == "4"
expect resp.http.Which-Unique == resp.http.Which
txreq -hdr "Word: oof"
rxresp
expect resp.status == 200
expect resp.http.Match == "false"
expect resp.http.N == "0"
expect resp.http.Which == "0"
expect resp.http.Which-Unique == resp.http.Which
txreq -hdr "Word: rab"
rxresp
expect resp.status == 200
expect resp.http.Match == "false"
expect resp.http.N == "0"
expect resp.http.Which == "0"
expect resp.http.Which-Unique == resp.http.Which
txreq -hdr "Word: zab"
rxresp
expect resp.status == 200
expect resp.http.Match == "false"
expect resp.http.N == "0"
expect resp.http.Which == "0"
expect resp.http.Which-Unique == resp.http.Which
txreq -hdr "Word: xuuq"
rxresp
expect resp.status == 200
expect resp.http.Match == "false"
expect resp.http.N == "0"
expect resp.http.Which == "0"
expect resp.http.Which-Unique == resp.http.Which
txreq -hdr "Word: raboof"
rxresp
expect resp.status == 200
expect resp.http.Match == "false"
expect resp.http.N == "0"
expect resp.http.Which == "0"
expect resp.http.Which-Unique == resp.http.Which
} -run
varnish v1 -errvcl {vmod selector failure: s.compile(): allow_overlaps is false but strings with common prefixes were added} {
import ${vmod_selector};
backend b None;
sub vcl_init {
new s = selector.set(allow_overlaps=false);
s.add("foobar");
s.add("foo");
s.compile();
}
}
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub vcl_init {
new s = selector.set(allow_overlaps=false);
s.add("foofighters");
s.add("foodfight");
s.add("foobar");
s.compile();
}
}
...@@ -82,6 +82,7 @@ struct bitmaps { ...@@ -82,6 +82,7 @@ struct bitmaps {
struct vmod_selector_set { struct vmod_selector_set {
unsigned magic; unsigned magic;
#define VMOD_SELECTOR_SET_MAGIC 0x838979ef #define VMOD_SELECTOR_SET_MAGIC 0x838979ef
unsigned nmembers;
struct entry **table; struct entry **table;
char **members; char **members;
char **lomembers; char **lomembers;
...@@ -89,8 +90,8 @@ struct vmod_selector_set { ...@@ -89,8 +90,8 @@ struct vmod_selector_set {
struct ph *hash; struct ph *hash;
char *vcl_name; char *vcl_name;
struct bitmaps *bitmaps; struct bitmaps *bitmaps;
unsigned nmembers; unsigned int case_sensitive:1;
VCL_BOOL case_sensitive; unsigned int allow_overlaps:1;
}; };
struct vsc_entry { struct vsc_entry {
...@@ -161,7 +162,7 @@ vmod_event(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) ...@@ -161,7 +162,7 @@ vmod_event(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
VCL_VOID VCL_VOID
vmod_set__init(VRT_CTX, struct vmod_selector_set **setp, const char *vcl_name, vmod_set__init(VRT_CTX, struct vmod_selector_set **setp, const char *vcl_name,
VCL_BOOL case_sensitive) VCL_BOOL case_sensitive, VCL_BOOL allow_overlaps)
{ {
struct vmod_selector_set *set; struct vmod_selector_set *set;
...@@ -175,7 +176,8 @@ vmod_set__init(VRT_CTX, struct vmod_selector_set **setp, const char *vcl_name, ...@@ -175,7 +176,8 @@ vmod_set__init(VRT_CTX, struct vmod_selector_set **setp, const char *vcl_name,
*setp = set; *setp = set;
set->vcl_name = strdup(vcl_name); set->vcl_name = strdup(vcl_name);
AN(set->vcl_name); AN(set->vcl_name);
set->case_sensitive = case_sensitive; set->case_sensitive = (case_sensitive != 0);
set->allow_overlaps = (allow_overlaps != 0);
ALLOC_OBJ(set->bitmaps, VMOD_SELECTOR_BITMAPS_MAGIC); ALLOC_OBJ(set->bitmaps, VMOD_SELECTOR_BITMAPS_MAGIC);
AN(set->bitmaps); AN(set->bitmaps);
...@@ -381,10 +383,15 @@ vmod_set_compile(VRT_CTX, struct VPFX(selector_set) *set) ...@@ -381,10 +383,15 @@ vmod_set_compile(VRT_CTX, struct VPFX(selector_set) *set)
qsort(idx, set->nmembers, sizeof(*idx), cmp); qsort(idx, set->nmembers, sizeof(*idx), cmp);
for (unsigned i = 0; i < set->nmembers; i++) { for (unsigned i = 0; i < set->nmembers; i++) {
errno = 0; errno = 0;
if (QP_Insert(&set->origo, idx[i].n, members) != 0) { if (QP_Insert(&set->origo, idx[i].n, members,
set->allow_overlaps) != 0) {
if (errno == EINVAL) if (errno == EINVAL)
VFAIL(ctx, "%s.compile(): \"%s\" added more " VFAIL(ctx, "%s.compile(): \"%s\" added more "
"than once", set->vcl_name, members[i]); "than once", set->vcl_name, members[i]);
else if (errno == EPERM)
VFAIL(ctx, "%s.compile(): allow_overlaps is "
"false but strings with common prefixes "
"were added", set->vcl_name);
else else
VFAIL(ctx, "%s.compile(\"%s\") failed: %s", VFAIL(ctx, "%s.compile(\"%s\") failed: %s",
set->vcl_name, members[i], set->vcl_name, members[i],
......
...@@ -315,7 +315,7 @@ or the longest match, and so on:: ...@@ -315,7 +315,7 @@ or the longest match, and so on::
# bar_backend for /foo/bar/quux # bar_backend for /foo/bar/quux
# foo_backend for /foo/quux # foo_backend for /foo/quux
$Object set(BOOL case_sensitive=1) $Object set(BOOL case_sensitive=1, BOOL allow_overlaps=1)
Create a set object. When ``case_sensitive`` is ``false``, matches Create a set object. When ``case_sensitive`` is ``false``, matches
using the ``.match()`` and ``.hasprefix()`` methods are using the ``.match()`` and ``.hasprefix()`` methods are
......
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