/*
*/
+#include <string.h>
+
#include "tables.h"
#include "hbytes.h"
MaskMap_Entry *entries;
}; /* overlays internalRep */
-int pat_maskmapv(Tcl_Interp *ip, Tcl_Obj *var, MaskMap_Var *agg) {
- int rc;
- rc= pat_somethingv(ip,var,&agg->sth,&maskmap_type); if (rc) return rc;
- agg->mm= (void*)&agg->sth.obj->internalRep;
- return TCL_OK;
-}
-
-int do_maskmap_amend(ClientData cd, Tcl_Interp *ip,
- MaskMap_Var map, HBytes_Value prefix,
- int preflen, Tcl_Obj *data) {
- return TCL_OK;
-}
-
-int do_maskmap_lookup(ClientData cd, Tcl_Interp *ip,
- MaskMap_Var map, HBytes_Value addr, Tcl_Obj *def,
- Tcl_Obj **result) {
- return TCL_OK;
-}
+/*---------- useful stuff ----------*/
static int prefix_bytes (int prefixlen) {
return (prefixlen + 7)/8;
}
+/*---------- operations on MaskMap_Entry ----------*/
+
static void mme_init(MaskMap_Entry *mme) {
+ mme->prefixlen= -1;
mme->prefix= 0;
mme->data= 0;
}
+static void mme_free(MaskMap_Entry *mme) {
+ TFREE(mme->prefix); mme->prefix=0;
+ if (mme->data) { Tcl_DecrRefCount(mme->data); mme->data=0; }
+}
+
+static int mme_parsekey(Tcl_Interp *ip, MaskMap_Entry *mme,
+ Tcl_Obj *prefixo, Tcl_Obj *prefixbitso,
+ int inmap) {
+ /* *mme should be blank entry; after exit (even error exit) it will be valid
+ * - on errors, it will be blank. inmap is 1 if we're parsing an existing
+ * map or 0 if it's an entry to be added or modified. */
+ HBytes_Value prefix;
+ int suppliedprefixbytes, prefixbits, wantprefixbytes, sparebits;
+ const Byte *data;
+ int rc;
+
+ hbytes_empty(&prefix);
+
+ rc= pat_hb(ip,prefixo,&prefix); if (rc) goto x_rc;
+ rc= pat_int(ip,prefixbitso,&prefixbits); if (rc) goto x_rc;
+
+ wantprefixbytes= prefix_bytes(prefixbits);
+ suppliedprefixbytes= hbytes_len(&prefix);
+
+ if (suppliedprefixbytes < wantprefixbytes) {
+ rc= staticerr(ip, "mask-map entry PREFIX too short for PREFIX-LEN",
+ "HBYTES MASKMAP SYNTAX UNDERRUN");
+ goto x_rc;
+ }
+ if (inmap && suppliedprefixbytes > wantprefixbytes) {
+ rc= staticerr(ip, "mask-map existing entry PREFIX too long for PREFIX-LEN",
+ "HBYTES MASKMAP SYNTAX OVERRUN");
+ goto x_rc;
+ }
+ sparebits= wantprefixbytes*8 - prefixbits;
+ data= hbytes_data(&prefix);
+ if (sparebits && (data[wantprefixbytes-1] & ((1u << sparebits)-1))) {
+ rc= staticerr(ip, "mask-map entry PREFIX contains bits excluded"
+ " by PREFIX-LEN", "HBYTES MASKMAP SYNTAX EXCLBITS");
+ goto x_rc;
+ }
+
+ mme->prefixlen= prefixbits;
+ mme->prefix= TALLOC(wantprefixbytes); assert(mme->prefix);
+ memcpy(mme->prefix, data, wantprefixbytes);
+ return TCL_OK;
+
+ x_rc:
+ mme_free(mme);
+ return rc;
+}
+
+static int mme_ordercompare(MaskMap_Entry *a, MaskMap_Entry *b) {
+ if (a->prefixlen != b->prefixlen)
+ return a->prefixlen - b->prefixlen;
+ else
+ return memcmp(b->prefix, a->prefix, prefix_bytes(a->prefixlen));
+}
+
+/*---------- useful operations on MaskMap_Value etc. ----------*/
+
static int mm_count(const MaskMap_Value *mm) {
int i;
- for (i=0; i<sm->allocd && sm->entries[i].prefixlen != -1; i++);
+ for (i=0; i<mm->allocd && mm->entries[i].prefixlen != -1; i++);
return i;
}
-static void mm_allocentries(MaskMap_Value *mm, int len) {
+static void mm_init(MaskMap_Value *mm) {
+ mm->allocd= 0;
+ mm->entries= 0;
+}
+
+static void mm_reallocentries(MaskMap_Value *mm, int len) {
int i;
+ MaskMap_Entry *newentries, *clear;
+
+ assert(len >= mm->allocd);
+ if (!len) return;
+
+ newentries= TREALLOC(mm->entries, sizeof(*newentries)*len);
+ assert(newentries);
+
+ for (i=mm->allocd, clear=newentries+mm->allocd;
+ i < len;
+ i++, clear++)
+ mme_init(clear);
+
mm->allocd= len;
- mm->entries= Tcl_Alloc(sizeof(*mm->entries) * len);
- assert(mm->entries);
- for (i=0; i < len; i++)
- mme_init(&mm->entries[i]);
+ mm->entries= newentries;
}
+/*---------- substantial operations on mask maps ----------*/
+
+int do_maskmap_amend(ClientData cd, Tcl_Interp *ip,
+ MaskMap_Var map, Tcl_Obj *prefix,
+ Tcl_Obj *preflen, Tcl_Obj *data) {
+ MaskMap_Value *mm= map.mm;
+ MaskMap_Entry mme, *search;
+ int rc, insertat, findend, cmp;
+
+ mme_init(&mme);
+
+ rc= mme_parsekey(ip,&mme,prefix,preflen,0); if (rc) goto x_rc;
+
+ for (insertat=0, search=mm->entries;
+ insertat < mm->allocd &&
+ search->prefixlen != -1;
+ insertat++, search++) {
+ cmp= mme_ordercompare(&mme,search);
+ if (!cmp) {
+ mme_free(&mme);
+ Tcl_DecrRefCount(search->data);
+ goto put_here;
+ }
+ if (cmp>0)
+ /* the new one sorts later, insert it here */
+ break;
+ }
+
+ /* we're adding an entry, make room for it */
+ findend= mm_count(mm);
+ if (findend == mm->allocd) {
+ mm_reallocentries(mm, mm->allocd*2 + 1);
+ search= mm->entries + insertat;
+ }
+ if (findend > insertat)
+ memmove(search + 1, search, sizeof(*search) * (findend - insertat));
+ *search= mme;
+
+ put_here:
+ Tcl_IncrRefCount(data);
+ search->data= data;
+ return TCL_OK;
+
+ x_rc:
+ mme_free(&mme);
+ return rc;
+}
+
+int do_maskmap_lookup(ClientData cd, Tcl_Interp *ip,
+ Tcl_Obj *mapo, HBytes_Value addrhb, Tcl_Obj *def,
+ Tcl_Obj **result) {
+ MaskMap_Value *mm= (void*)&mapo->internalRep;
+ MaskMap_Entry *search;
+ const Byte *addr= hbytes_data(&addrhb);
+ int addrbytes= hbytes_len(&addrhb);
+ int i, directbytes, leftoverbits;
+
+ search= mm->entries;
+ if (!search || search->prefixlen==-1) goto not_found;
+
+ /* longest masks are first, so we can test this once */
+ if (addrbytes < prefix_bytes(search->prefixlen))
+ return staticerr(ip, "address shorter than mask(s) in map",
+ "HBYTES MASKMAP UNDERRUN");
+
+ for (i=0;
+ i < mm->allocd && search->prefixlen != -1;
+ i++, search++) {
+ directbytes= search->prefixlen / 8;
+ if (memcmp(search->prefix, addr, directbytes)) continue;
+
+ leftoverbits= search->prefixlen % 8;
+ if (leftoverbits)
+ if ((addr[directbytes] & (0xffu << leftoverbits))
+ != search->prefix[directbytes])
+ continue;
+
+ /*found*/
+ *result= search->data;
+ return TCL_OK;
+ }
+
+ not_found:
+ if (!def)
+ return staticerr(ip, "address not found in mask-map",
+ "HBYTES MASKMAP NOMATCH");
+ *result= def;
+ return TCL_OK;
+}
+
+/*---------- Tcl type and arg parsing functions ----------*/
+
+int pat_maskmapv(Tcl_Interp *ip, Tcl_Obj *var, MaskMap_Var *agg) {
+ int rc;
+ rc= pat_somethingv(ip,var,&agg->sth,&maskmap_type); if (rc) return rc;
+ agg->mm= (void*)&agg->sth.obj->internalRep;
+ return TCL_OK;
+}
+
static void maskmap_t_free(Tcl_Obj *o) {
MaskMap_Value *mm= (void*)&o->internalRep;
- for (i=0; i<mm.allocd; i++) {
- if (i.prefixlen==-1) break;
- mme_free(&mm.entries[i]);
+ MaskMap_Entry *mme;
+ int i;
+
+ for (i=0, mme=mm->entries; i<mm->allocd; i++, mme++) {
+ if (mme->prefixlen==-1) break;
+ mme_free(mme);
}
}
-static void maskmap_t_dup(Tcl_Obj *so, Tcl_Obj *do) {
- MaskMap_Value *sm= (void*)&so->internalRep;
- MaskMap_Value *dm= (void*)&do->internalRep;
+static void maskmap_t_dup(Tcl_Obj *sob, Tcl_Obj *dob) {
+ MaskMap_Value *sm= (void*)&sob->internalRep;
+ MaskMap_Value *dm= (void*)&dob->internalRep;
+ MaskMap_Entry *sme, *dme;
int l, i, nbytes;
- assert(so->typePtr == &maskmap_type);
- objfreeir(do);
- l= mm_count(&sm);
+ assert(sob->typePtr == &maskmap_type);
+ objfreeir(dob);
+ l= mm_count(sm);
- mm_allocentries(&dm,l);
+ mm_init(dm);
+ mm_reallocentries(dm,l);
for (i=0, sme=sm->entries, dme=dm->entries;
i<dm->allocd;
i++, sme++, dme++) {
*dme= *sme;
nbytes= prefix_bytes(sme->prefixlen);
- dme->prefix= Tcl_Alloc(nbytes); assert(dme->prefix);
+ dme->prefix= TALLOC(nbytes); assert(dme->prefix);
memcpy(dme->prefix, sme->prefix, nbytes);
Tcl_IncrRefCount(dme->data);
}
+ dob->typePtr= &maskmap_type;
}
static void maskmap_t_ustr(Tcl_Obj *so) {
MaskMap_Value *sm= (void*)&so->internalRep;
Tcl_Obj **mainlobjsl, *mainobj;
- char *string;
- int stringl;
+ int i, l;
assert(so->typePtr == &maskmap_type);
l= mm_count(sm);
- mainlobjsl= Tcl_Alloc(sizeof(*mainobjsl) * l); assert(mainlobjsl);
+ mainlobjsl= TALLOC(sizeof(*mainlobjsl) * l); assert(mainlobjsl);
for (i=0; i<l; i++) {
MaskMap_Entry *sme= &sm->entries[i];
Tcl_Obj *subl[3], *sublo;
hbytes_array(&hb, sme->prefix, prefix_bytes(sme->prefixlen));
- subl[0]= ret_hb(ip, &hb); assert(subl[0]);
+ subl[0]= ret_hb(0, hb); assert(subl[0]);
subl[1]= Tcl_NewIntObj(sme->prefixlen); assert(subl[1]);
subl[2]= sme->data;
}
static int maskmap_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
- int len, eol;
+ int rc, len, eol, i;
MaskMap_Value mm;
+ Tcl_Obj *eo, *prefixo, *prefixleno;
- mm.allocd= 0;
- mm.entries= 0;
+ mm_init(&mm);
rc= Tcl_ListObjLength(ip,o,&len); if (rc) goto x_rc;
- mm_allocentries(len);
+ mm_reallocentries(&mm, len);
for (i=0; i<len; i++) {
rc= Tcl_ListObjIndex(ip,o,i,&eo); if (rc) goto x_rc;
rc= Tcl_ListObjLength(ip,eo,&eol); if (rc) goto x_rc;
if (eol != 3) {
- rc= staticerr(ip, "mask-map entry length != 3");
+ rc= staticerr(ip, "mask-map entry length != 3",
+ "HBYTES MASKMAP SYNTAX LLENGTH");
goto x_rc;
}
- rc= Tcl_ListObjIndex(ip,eo,0,&prefixo); if (rc) goto rc;
- rc= Tcl_ListObjIndex(ip,eo,1,&prefixleno); if (rc) goto rc;
+ rc= Tcl_ListObjIndex(ip,eo,0,&prefixo); if (rc) goto x_rc;
+ rc= Tcl_ListObjIndex(ip,eo,1,&prefixleno); if (rc) goto x_rc;
rc= Tcl_ListObjIndex(ip,eo,2,&mm.entries[i].data); if (rc) goto x_rc;
Tcl_IncrRefCount(mm.entries[i].data);
- rc= mme_parsekey(&mm.entries[i], prefixo, prefixleno, 1);
- if (rc) goto rc;
+ rc= mme_parsekey(ip, &mm.entries[i], prefixo, prefixleno, 1);
+ if (rc) goto x_rc;
if (i>0) {
- if (mme_compare(&mm.entries[i-1], &mm.entries[i]) <= 0) {
- rc= staticerr(ip, "mask-map entries out of order");
+ if (mme_ordercompare(&mm.entries[i-1], &mm.entries[i]) <= 0) {
+ rc= staticerr(ip, "mask-map entries out of order",
+ "HBYTES MASKMAP SYNTAX ORDER");
goto x_rc;
}
}
}
/* we commit now */
- assert(sizeof(mm) <= sizeof(o.internalRep));
+ assert(sizeof(mm) <= sizeof(o->internalRep));
objfreeir(o);
- memcpy(&o.internalRep, &mm, sizeof(mm));
- o.typePtr= &maskmap_type;
+ memcpy(&o->internalRep, &mm, sizeof(mm));
+ o->typePtr= &maskmap_type;
return TCL_OK;
x_rc:
if (mm.entries) {
for (i=0; i<len; i++)
mme_free(&mm.entries[i]);
- Tcl_Free(mm.entries);
+ TFREE(mm.entries);
}
return rc;
}
- { free(mm.e
- return rc;
- rc= Tcl_ConvertToType(ip,o,
-}
-
Tcl_ObjType maskmap_type = {
"mask-map",
maskmap_t_free, maskmap_t_dup, maskmap_t_ustr, maskmap_t_sfa