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