chiark / gitweb /
fix c violations detected by newer gcc - urgh
[chiark-tcl.git] / maskmap / maskmap.c
index 99688015252641a884983c387dd33f8af8b7ba0c..f51dba4b19cf587690b170174a9c933dd36b6150 100644 (file)
@@ -1,6 +1,8 @@
 /*
  */
 
+#include <string.h>
+
 #include "tables.h"
 #include "hbytes.h"
 
@@ -15,29 +17,323 @@ struct MaskMap_Value {
   MaskMap_Entry *entries;
 }; /* overlays internalRep */
 
-int pat_maskmapv(Tcl_Interp *ip, Tcl_Obj *var, MaskMap_Var *agg) {
+/*---------- 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;
-  rc= pat_somethingv(ip,var,&agg->sth,&maskmap_type);  if (rc) return rc;
-  agg->mm= (void*)&agg->sth.obj->internalRep;
+
+  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<mm->allocd && mm->entries[i].prefixlen != -1; i++);
+  return i;
+}
+
+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= newentries;
+}
+  
+/*---------- substantial operations on mask maps ----------*/
+
 int do_maskmap_amend(ClientData cd, Tcl_Interp *ip,
-                    MaskMap_Var map, HBytes_Value prefix,
-                    int preflen, Tcl_Obj *data) {
+                    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,
-                     MaskMap_Var map, HBytes_Value addr, Tcl_Obj *def,
+                     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;
 }
 
-static void maskmap_t_free(Tcl_Obj *o) { }
-static void maskmap_t_dup(Tcl_Obj *src, Tcl_Obj *dup) { }
-static void maskmap_t_ustr(Tcl_Obj *o) { }
-static int maskmap_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) { return TCL_ERROR; }
+/*---------- 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;
+  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 *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(sob->typePtr == &maskmap_type);
+  objfreeir(dob);
+  l= mm_count(sm);
+
+  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= 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;
+  int i, l;
+
+  assert(so->typePtr == &maskmap_type);
+  l= mm_count(sm);
+  mainlobjsl= TALLOC(sizeof(*mainlobjsl) * l);  assert(mainlobjsl);
+
+  for (i=0; i<l; i++) {
+    MaskMap_Entry *sme= &sm->entries[i];
+    HBytes_Value hb;
+    Tcl_Obj *subl[3], *sublo;
+
+    hbytes_array(&hb, sme->prefix, prefix_bytes(sme->prefixlen));
+    subl[0]= ret_hb(0, hb);  assert(subl[0]);
+    subl[1]= Tcl_NewIntObj(sme->prefixlen);  assert(subl[1]);
+    subl[2]= sme->data;
+    
+    sublo= Tcl_NewListObj(3,subl);  assert(sublo);
+    mainlobjsl[i]= sublo;
+  }
+
+  mainobj= Tcl_NewListObj(l,mainlobjsl);  assert(mainobj);
+  so->bytes= Tcl_GetStringFromObj(mainobj, &so->length);  assert(so->bytes);
+  mainobj->bytes= 0; mainobj->length= 0; /* we stole it */
+}
+
+static int maskmap_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
+  int rc, len, eol, i;
+  MaskMap_Value mm;
+  Tcl_Obj *eo, *prefixo, *prefixleno;
+
+  mm_init(&mm);
+  
+  rc= Tcl_ListObjLength(ip,o,&len);  if (rc) goto x_rc;
+  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",
+                   "HBYTES MASKMAP SYNTAX LLENGTH");
+      goto x_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(ip, &mm.entries[i], prefixo, prefixleno, 1);
+    if (rc) goto x_rc;
+
+    if (i>0) {
+      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));
+  objfreeir(o);
+  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]);
+    TFREE(mm.entries);
+  }
+  return rc;
+}
 
 Tcl_ObjType maskmap_type = {
   "mask-map",