chiark / gitweb /
1ae8439b6a3c28acac50ee6e1f729968f7e6a659
[chiark-tcl.git] / maskmap / maskmap.c
1 /*
2  */
3
4 #include <string.h>
5
6 #include "tables.h"
7 #include "hbytes.h"
8
9 typedef struct {
10   int prefixlen; /* there may be some empty slots with prefixlen==-1 at end */
11   Byte *prefix; /* ceil(prefixlen/8) bytes */
12   Tcl_Obj *data;
13 } MaskMap_Entry;
14
15 struct MaskMap_Value {
16   int allocd;
17   MaskMap_Entry *entries;
18 }; /* overlays internalRep */
19
20 /*---------- useful stuff ----------*/
21
22 static int prefix_bytes (int prefixlen) {
23   return (prefixlen + 7)/8;
24 }
25
26 /*---------- operations on MaskMap_Entry ----------*/
27
28 static void mme_init(MaskMap_Entry *mme) {
29   mme->prefix= 0;
30   mme->data= 0;
31 }
32
33 static void mme_free(MaskMap_Entry *mme) {
34   TFREE(mme->prefix);  mme->prefix=0;
35   if (mme->data) { Tcl_DecrRefCount(mme->data); mme->data=0; }
36 }
37
38 static int mme_parsekey(Tcl_Interp *ip, MaskMap_Entry *mme,
39                         Tcl_Obj *prefixo, Tcl_Obj *prefixleno,
40                         int inmap) {
41  /* *mme should be blank entry; after exit (even error exit) it will be valid
42   * - on errors, it will be blank.  inmap is 1 if we're parsing an existing
43   * map or 0 if it's an entry to be added or modified. */
44   HBytes_Value prefix;
45   int suppliedprefixbytes, prefixlen, wantprefixbytes;
46   int rc;
47
48   hbytes_empty(&prefix);
49   
50   rc= pat_hb(ip,prefixo,&prefix);  if (rc) goto x_rc;
51   rc= pat_int(ip,prefixleno,&prefixlen);  if (rc) goto x_rc;
52
53   wantprefixbytes= prefix_bytes(prefixlen);
54   suppliedprefixbytes= hbytes_len(&prefix);
55
56   if (suppliedprefixbytes < wantprefixbytes) {
57     rc= staticerr(ip, "mask-map entry PREFIX too short for PREFIX-LEN",
58                   "HBYTES MASKMAP SYNTAX UNDERRUN");
59     goto x_rc;
60   }
61   if (inmap && suppliedprefixbytes > wantprefixbytes) {
62     rc= staticerr(ip, "mask-map existing entry PREFIX too long for PREFIX-LEN",
63                   "HBYTES MASKMAP SYNTAX OVERRUN");
64     goto x_rc;
65   }
66   mme->prefixlen= prefixlen;
67   mme->prefix= TALLOC(wantprefixbytes);  assert(mme->prefix);
68   memcpy(mme->prefix, hbytes_data(&prefix), wantprefixbytes);
69   return TCL_OK;
70
71  x_rc:
72   mme_free(mme);
73   return rc;
74 }
75
76 static int mme_ordercompare(MaskMap_Entry *a, MaskMap_Entry *b) {
77   if (a->prefixlen != b->prefixlen)
78     return a->prefixlen - b->prefixlen;
79   else
80     return memcmp(b->prefix, a->prefix, prefix_bytes(a->prefixlen));
81 }
82
83 /*---------- useful operations on MaskMap_Value etc. ----------*/
84
85 static int mm_count(const MaskMap_Value *mm) {
86   int i;
87   for (i=0; i<mm->allocd && mm->entries[i].prefixlen != -1; i++);
88   return i;
89 }
90
91 static void mm_allocentries(MaskMap_Value *mm, int len) {
92   int i;
93   mm->allocd= len;
94   mm->entries= TALLOC(sizeof(*mm->entries) * len);
95   assert(mm->entries);
96   for (i=0; i < len; i++)
97     mme_init(&mm->entries[i]);
98 }
99   
100 /*---------- substantial operations on mask maps ----------*/
101
102 int do_maskmap_amend(ClientData cd, Tcl_Interp *ip,
103                      MaskMap_Var map, HBytes_Value prefix,
104                      int preflen, Tcl_Obj *data) {
105   return TCL_OK; /*fixme*/
106 }
107
108 int do_maskmap_lookup(ClientData cd, Tcl_Interp *ip,
109                       Tcl_Obj *mapo, HBytes_Value addr, Tcl_Obj *def,
110                       Tcl_Obj **result) {
111   /*MaskMap_Var *map= (void*)&mapo->internalRep;*/
112   *result= Tcl_NewIntObj(42);
113   return TCL_OK; /*fixme*/
114 }
115
116 /*---------- Tcl type and arg parsing functions ----------*/
117
118 int pat_maskmapv(Tcl_Interp *ip, Tcl_Obj *var, MaskMap_Var *agg) {
119   int rc;
120   rc= pat_somethingv(ip,var,&agg->sth,&maskmap_type);  if (rc) return rc;
121   agg->mm= (void*)&agg->sth.obj->internalRep;
122   return TCL_OK;
123 }
124
125 static void maskmap_t_free(Tcl_Obj *o) {
126   MaskMap_Value *mm= (void*)&o->internalRep;
127   MaskMap_Entry *mme;
128   int i;
129   
130   for (i=0, mme=mm->entries; i<mm->allocd; i++, mme++) {
131     if (mme->prefixlen==-1) break;
132     mme_free(mme);
133   }
134 }
135
136 static void maskmap_t_dup(Tcl_Obj *sob, Tcl_Obj *dob) {
137   MaskMap_Value *sm= (void*)&sob->internalRep;
138   MaskMap_Value *dm= (void*)&dob->internalRep;
139   MaskMap_Entry *sme, *dme;
140   int l, i, nbytes;
141
142   assert(sob->typePtr == &maskmap_type);
143   objfreeir(dob);
144   l= mm_count(sm);
145
146   mm_allocentries(dm,l);
147   for (i=0, sme=sm->entries, dme=dm->entries;
148        i<dm->allocd;
149        i++, sme++, dme++) {
150     *dme= *sme;
151     nbytes= prefix_bytes(sme->prefixlen);
152     dme->prefix= TALLOC(nbytes);  assert(dme->prefix);
153     memcpy(dme->prefix, sme->prefix, nbytes);
154     Tcl_IncrRefCount(dme->data);
155   }
156   dob->typePtr= &maskmap_type;
157 }
158
159 static void maskmap_t_ustr(Tcl_Obj *so) {
160   MaskMap_Value *sm= (void*)&so->internalRep;
161   Tcl_Obj **mainlobjsl, *mainobj;
162   int i, l;
163
164   assert(so->typePtr == &maskmap_type);
165   l= mm_count(sm);
166   mainlobjsl= TALLOC(sizeof(*mainlobjsl) * l);  assert(mainlobjsl);
167
168   for (i=0; i<l; i++) {
169     MaskMap_Entry *sme= &sm->entries[i];
170     HBytes_Value hb;
171     Tcl_Obj *subl[3], *sublo;
172
173     hbytes_array(&hb, sme->prefix, prefix_bytes(sme->prefixlen));
174     subl[0]= ret_hb(0, hb);  assert(subl[0]);
175     subl[1]= Tcl_NewIntObj(sme->prefixlen);  assert(subl[1]);
176     subl[2]= sme->data;
177     
178     sublo= Tcl_NewListObj(3,subl);  assert(sublo);
179     mainlobjsl[i]= sublo;
180   }
181
182   mainobj= Tcl_NewListObj(l,mainlobjsl);  assert(mainobj);
183   so->bytes= Tcl_GetStringFromObj(mainobj, &so->length);  assert(so->bytes);
184   mainobj->bytes= 0; mainobj->length= 0; /* we stole it */
185 }
186
187 static int maskmap_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
188   int rc, len, eol, i;
189   MaskMap_Value mm;
190   Tcl_Obj *eo, *prefixo, *prefixleno;
191
192   mm.allocd= 0;
193   mm.entries= 0;
194   
195   rc= Tcl_ListObjLength(ip,o,&len);  if (rc) goto x_rc;
196   mm_allocentries(&mm, len);
197   
198   for (i=0; i<len; i++) {
199     rc= Tcl_ListObjIndex(ip,o,i,&eo);  if (rc) goto x_rc;
200     rc= Tcl_ListObjLength(ip,eo,&eol);  if (rc) goto x_rc;
201     if (eol != 3) {
202       rc= staticerr(ip, "mask-map entry length != 3",
203                     "HBYTES MASKMAP SYNTAX LLENGTH");
204       goto x_rc;
205     }
206     rc= Tcl_ListObjIndex(ip,eo,0,&prefixo);  if (rc) goto x_rc;
207     rc= Tcl_ListObjIndex(ip,eo,1,&prefixleno);  if (rc) goto x_rc;
208     rc= Tcl_ListObjIndex(ip,eo,2,&mm.entries[i].data);  if (rc) goto x_rc;
209     Tcl_IncrRefCount(mm.entries[i].data);
210
211     rc= mme_parsekey(ip, &mm.entries[i], prefixo, prefixleno, 1);
212     if (rc) goto x_rc;
213
214     if (i>0) {
215       if (mme_ordercompare(&mm.entries[i-1], &mm.entries[i]) <= 0) {
216         rc= staticerr(ip, "mask-map entries out of order",
217                       "HBYTES MASKMAP SYNTAX ORDER");
218         goto x_rc;
219       }
220     }
221   }
222
223   /* we commit now */
224   assert(sizeof(mm) <= sizeof(o->internalRep));
225   objfreeir(o);
226   memcpy(&o->internalRep, &mm, sizeof(mm));
227   o->typePtr= &maskmap_type;
228   return TCL_OK;
229
230 x_rc:
231   if (mm.entries) {
232     for (i=0; i<len; i++)
233       mme_free(&mm.entries[i]);
234     TFREE(mm.entries);
235   }
236   return rc;
237 }
238
239 Tcl_ObjType maskmap_type = {
240   "mask-map",
241   maskmap_t_free, maskmap_t_dup, maskmap_t_ustr, maskmap_t_sfa
242 };