chiark / gitweb /
amend seems to work
[chiark-tcl.git] / maskmap / maskmap.c
index e0c7f90ee6aa1b44e27004449aba93a23755c176..9c92810f0197c4a5d8aea6356ba72ba36e5bb2f5 100644 (file)
@@ -26,6 +26,7 @@ static int prefix_bytes (int prefixlen) {
 /*---------- operations on MaskMap_Entry ----------*/
 
 static void mme_init(MaskMap_Entry *mme) {
+  mme->prefixlen= -1;
   mme->prefix= 0;
   mme->data= 0;
 }
@@ -36,21 +37,22 @@ static void mme_free(MaskMap_Entry *mme) {
 }
 
 static int mme_parsekey(Tcl_Interp *ip, MaskMap_Entry *mme,
-                       Tcl_Obj *prefixo, Tcl_Obj *prefixleno,
+                       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, prefixlen, wantprefixbytes;
+  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,prefixleno,&prefixlen);  if (rc) goto x_rc;
+  rc= pat_int(ip,prefixbitso,&prefixbits);  if (rc) goto x_rc;
 
-  wantprefixbytes= prefix_bytes(prefixlen);
+  wantprefixbytes= prefix_bytes(prefixbits);
   suppliedprefixbytes= hbytes_len(&prefix);
 
   if (suppliedprefixbytes < wantprefixbytes) {
@@ -63,9 +65,17 @@ static int mme_parsekey(Tcl_Interp *ip, MaskMap_Entry *mme,
                  "HBYTES MASKMAP SYNTAX OVERRUN");
     goto x_rc;
   }
-  mme->prefixlen= prefixlen;
+  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, hbytes_data(&prefix), wantprefixbytes);
+  memcpy(mme->prefix, data, wantprefixbytes);
   return TCL_OK;
 
  x_rc:
@@ -88,26 +98,82 @@ static int mm_count(const MaskMap_Value *mm) {
   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= TALLOC(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, HBytes_Value prefix,
-                    int preflen, Tcl_Obj *data) {
-  return TCL_OK; /*fixme*/
+                    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 addr, Tcl_Obj *def,
                      Tcl_Obj **result) {
+  /*MaskMap_Var *map= (void*)&mapo->internalRep;*/
   *result= Tcl_NewIntObj(42);
   return TCL_OK; /*fixme*/
 }
@@ -142,7 +208,8 @@ static void maskmap_t_dup(Tcl_Obj *sob, Tcl_Obj *dob) {
   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++) {
@@ -188,11 +255,10 @@ static int maskmap_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
   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(&mm, len);
+  mm_reallocentries(&mm, len);
   
   for (i=0; i<len; i++) {
     rc= Tcl_ListObjIndex(ip,o,i,&eo);  if (rc) goto x_rc;