chiark / gitweb /
working on new addrmap instead of maskmap - compiles, but have not yet implemented...
[chiark-tcl.git] / maskmap / addrmap.c
1 /*
2  */
3
4 #include <string.h>
5
6 #include "tables.h"
7 #include "hbytes.h"
8
9 typedef struct {
10   Byte *start; /* byl bytes */
11   Tcl_Obj *data; /* may be 0 to mean empty */
12 } AddrMap_Entry;
13
14 struct AddrMap_Value {
15   int byl, used, space;
16   AddrMap_Entry *entries;
17   /* Entries are sorted by start.  Each entry gives value (or lack of
18    * it) for all A st START <= A < NEXT-START.  Last entry has value
19    * (or lack of it) for all A >= START.  First entry is always
20    * present and always has start all-bits-0. */
21 }; /* internalRep.otherValuePtr */
22
23 /*---------- operations on AddrMap_Entry ----------*/
24
25 static void ame_free(AddrMap_Entry *ame) {
26   TFREE(ame->start);  ame->start=0;
27   if (ame->data) { Tcl_DecrRefCount(ame->data); ame->data=0; }
28 }
29
30 static const Byte *ame_parsecheck_addr(Tcl_Interp *ip, const AddrMap_Value *am,
31                                        const HBytes_Value *hb) {
32   int hbl= hbytes_len(hb);
33   if (hbl < am->byl) {
34     staticerr(ip,"addr-map address too short","HBYTES ADDRMAP UNDERRUN");
35     return 0;
36   }
37   if (hbl > am->byl) {
38     staticerr(ip,"addr-map address too long","HBYTES ADDRMAP OVERRUN");
39     return 0;
40   }
41   return hbytes_data(hb);
42 }
43   
44 static int ame_parsecheck_range(Tcl_Interp *ip, const AddrMap_Value *am,
45                                 const HBytes_Value *starthb,
46                                 const HBytes_Value *endhb,
47                                 const Byte *p_r[2]) {
48   p_r[0]= ame_parsecheck_addr(ip,am,starthb);  if (!p_r[0]) return TCL_ERROR;
49   p_r[1]= ame_parsecheck_addr(ip,am,endhb);    if (!p_r[0]) return TCL_ERROR;
50   if (memcmp(p_r[0],p_r[1],am->byl) > 0)
51     return staticerr(ip, "addr-map range start is after end",
52                      "HBYTES ADDRMAP BADRANGE");
53   return TCL_OK;
54 }
55   
56 static int ame_ba_addsubtractone(Byte *out, const Byte *in, int byl,
57                                  unsigned signum, unsigned onoverflow) {
58   /* On entry:
59    *   *in is an array of byl bytes
60    *   signum is 0xff or 0x01
61    *   onoverflow is what counts as overflowed value,
62    *     ie (for unsigned arith) 0x00 for add and 0xff for subtract
63    * On exit:
64    *   *out is the resulting value (subject to overflow truncation)
65    *   return value is TCL_OK, or TCL_ERROR if overflow happened
66    *   (but interpreter result is not set on overflow)
67    */
68   int j;
69       
70   for (j= byl, in += byl, out += byl;
71        in--, out--, j>0;
72        j--) {
73     *out = (*out) + signum;
74     if (*out != onoverflow)
75       return TCL_OK;
76   }
77   return TCL_ERROR;
78 }
79
80 /*---------- useful operations on AddrMap_Value etc. ----------*/
81
82 static void am_init0(AddrMap_Value *am, int byl) {
83   am->byl= byl;
84   am->used= 0;
85   am->space= 0;
86   am->entries= 0;
87 }
88
89 static void am_reallocentries(AddrMap_Value *am, int len) {
90   AddrMap_Entry *newentries;
91
92   assert(len >= am->space);
93   if (!len) return;
94
95   newentries= TREALLOC(am->entries, sizeof(*newentries)*len);
96   assert(newentries);
97   
98   am->space= len;
99   am->entries= newentries;
100 }
101
102 static void am_free(AddrMap_Value *am) {
103   AddrMap_Entry *ame;
104   int i;
105
106   if (!am) return;
107   
108   for (i=0, ame=am->entries; i<am->used; i++, ame++)
109     ame_free(ame);
110
111   TFREE(am->entries);
112   TFREE(am);
113 }
114
115 /*---------- Tcl type and arg parsing functions ----------*/
116
117 int pat_addrmapv(Tcl_Interp *ip, Tcl_Obj *var, AddrMap_Var *agg) {
118   int rc;
119   rc= pat_somethingv(ip,var,&agg->sth,&addrmap_type);  if (rc) return rc;
120   agg->am= agg->sth.obj->internalRep.otherValuePtr;
121   return TCL_OK;
122 }
123
124 static void addrmap_t_free(Tcl_Obj *o) {
125   AddrMap_Value *am= o->internalRep.otherValuePtr;
126   am_free(am);
127 }
128
129 static void addrmap_t_dup(Tcl_Obj *sob, Tcl_Obj *dob) {
130   AddrMap_Value *sm= sob->internalRep.otherValuePtr;
131   AddrMap_Value *dm;
132   AddrMap_Entry *sme, *dme;
133   int i;
134
135   assert(sob->typePtr == &addrmap_type);
136   objfreeir(dob);
137   dm= TALLOC(sizeof(*dm));
138
139   am_init0(dm,sm->byl);
140   am_reallocentries(dm,sm->used);
141   dm->used= sm->used;
142   for (i=0, sme=sm->entries, dme=dm->entries;
143        i < dm->used;
144        i++, sme++, dme++) {
145     *dme= *sme;
146     dme->start= TALLOC(sm->byl);  assert(dme->start);
147     memcpy(dme->start, sme->start, sm->byl);
148     Tcl_IncrRefCount(dme->data);
149   }
150   dob->internalRep.otherValuePtr= dm;
151   dob->typePtr= &addrmap_type;
152 }
153
154 static void addrmap_t_ustr(Tcl_Obj *so) {
155   AddrMap_Value *sm= so->internalRep.otherValuePtr;
156   Tcl_Obj **mainlobjsl, *surrogate;
157   AddrMap_Entry *sme;
158   int entnum, listlength;
159
160   assert(so->typePtr == &addrmap_type);
161   mainlobjsl= TALLOC(sizeof(*mainlobjsl) * (sm->used+1));  assert(mainlobjsl);
162   mainlobjsl[0]= Tcl_NewIntObj(sm->byl * 8);
163   listlength= 1;
164
165   for (entnum=0, sme=sm->entries; entnum<sm->used; entnum++, sme++) {
166     HBytes_Value hb;
167     Tcl_Obj *subl[3], *sublo;
168
169     if (!sme->data) continue;
170
171     hbytes_array(&hb, sme->start, sm->byl);
172     subl[0]= ret_hb(0, hb);  assert(subl[0]);
173
174     if (entnum+1 < sm->used) {
175       ame_ba_addsubtractone(hbytes_arrayspace(&hb, sm->byl),
176                             (sme+1)->start, sm->byl,
177                             /*subtract:*/ 0x0ffu, 0x0ffu);
178     } else {
179       memset(hbytes_arrayspace(&hb, sm->byl),
180              0x0ffu, sm->byl);
181     }
182
183     subl[1]= ret_hb(0, hb);  assert(subl[1]);
184     subl[2]= sme->data;
185     
186     sublo= Tcl_NewListObj(3,subl);  assert(sublo);
187     mainlobjsl[listlength++]= sublo;
188   }
189   assert(listlength <= sm->used+1);
190   surrogate= Tcl_NewListObj(listlength,mainlobjsl);  assert(surrogate);
191   assert(surrogate);
192   
193   so->bytes= Tcl_GetStringFromObj(surrogate, &so->length);  assert(so->bytes);
194   surrogate->bytes= 0; surrogate->length= 0; /* we stole it */
195 }
196
197 static AddrMap_Entry *ame_sfa_alloc(AddrMap_Value *am) {
198   AddrMap_Entry *ame;
199   
200   ame= am->entries + am->used;
201
202   am->used++;
203   assert(am->used <= am->space);
204
205   ame->start= TALLOC(am->byl);  assert(ame->start);
206   ame->data= 0;
207   return ame;
208 }
209
210 static int addrmap_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
211   int rc, inlen, eol, innum, bitlen, cmp;
212   Tcl_Obj *eo, *starto, *endo;
213   HBytes_Value starthb, endhb;
214   const Byte *rangeptrs[2];
215   AddrMap_Value *am;
216   AddrMap_Entry *ame;
217
218   am= TALLOC(sizeof(*am));  assert(am);
219   am_init0(am,0);
220
221   rc= Tcl_ListObjLength(ip,o,&inlen);  if (rc) goto x_badvalue_rc;
222
223   if (inlen<0) {
224     rc= staticerr(ip, "addr-map overall length < 1", 0);
225     goto x_badvalue_rc;
226   }
227
228   rc= Tcl_ListObjIndex(ip,o,0,&eo);  if (rc) goto x_badvalue_rc;
229   rc= Tcl_GetIntFromObj(ip,eo,&bitlen);  if (rc) goto x_badvalue_rc;
230
231   if (bitlen<0 || bitlen % 8) {
232     rc= staticerr(ip, "addr-map overall length < 1", 0);
233     goto x_badvalue_rc;
234   }
235
236   am->byl= bitlen/8;
237   am_reallocentries(am, (inlen-1)*2+1);
238
239   ame= ame_sfa_alloc(am);
240   memset(ame->start,0,am->byl);
241
242   for (innum=1; innum < inlen; innum++) {
243     rc= Tcl_ListObjIndex(ip,o,innum,&eo);  if (rc) goto x_badvalue_rc;
244     rc= Tcl_ListObjLength(ip,eo,&eol);  if (rc) goto x_badvalue_rc;
245
246     if (eol != 3) {
247       rc= staticerr(ip, "addr-map entry length != 3", 0);
248       goto x_badvalue_rc;
249     }
250     rc= Tcl_ListObjIndex(ip,eo,0,&starto);  if (rc) goto x_badvalue_rc;
251     rc= Tcl_ListObjIndex(ip,eo,1,&endo);    if (rc) goto x_badvalue_rc;
252
253     rc= pat_hb(ip,starto,&starthb);  if (rc) goto x_badvalue_rc;
254     rc= pat_hb(ip,endo,&endhb);  if (rc) goto x_badvalue_rc;
255
256     rc= ame_parsecheck_range(ip,am,&starthb,&endhb,rangeptrs);
257     if (rc) goto x_badvalue_rc;
258
259     cmp= memcmp(ame->start, rangeptrs[0], am->byl);
260     if (cmp < 0) {
261       rc= staticerr(ip, "addr-map entries out of order", 0);
262       goto x_badvalue_rc;
263     }
264     if (cmp > 0) {
265       ame= ame_sfa_alloc(am);
266       memcpy(ame->start, rangeptrs[0], am->byl);
267     }
268     
269     assert(!ame->data);
270     rc= Tcl_ListObjIndex(ip,eo,2,&ame->data);  if (rc) goto x_badvalue_rc;
271     Tcl_IncrRefCount(ame->data);
272
273     ame= ame_sfa_alloc(am);
274     rc= ame_ba_addsubtractone(ame->start, rangeptrs[1], am->byl,
275                               /*add:*/ 0x01u, 0x00u);
276     if (rc) {
277       /* we've overflowed.  it must have been ffffffff.... */
278       if (innum != inlen-1) {
279         rc= staticerr(ip, "addr-map non-last entry end is all-bits-1", 0);
280         goto x_badvalue_rc;
281       }
282       TFREE(ame->start);
283       am->used--;
284       break;
285     }
286   }
287     
288   /* we commit now */
289   objfreeir(o);
290   o->internalRep.otherValuePtr= am;
291   o->typePtr= &addrmap_type;
292   return TCL_OK;
293
294  x_badvalue_rc:
295   if (rc == TCL_ERROR)
296     Tcl_SetObjErrorCode(ip, Tcl_NewStringObj("HBYTES ADDRMAP VALUE", -1));
297
298   am_free(am);
299   return rc;
300 }
301
302 Tcl_ObjType addrmap_type = {
303   "addr-map",
304   addrmap_t_free, addrmap_t_dup, addrmap_t_ustr, addrmap_t_sfa
305 };