2 * maskmap - Tcl extension for address mask map data structures
3 * Copyright 2006 Ian Jackson
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 #include "chiark_tcl_hbytes.h"
24 /*---------- operations on AddrMap_Entry ----------*/
26 static void ame_free(AddrMap_Entry *ame) {
27 TFREE(ame->start); ame->start=0;
28 if (ame->data) { Tcl_DecrRefCount(ame->data); ame->data=0; }
31 static const Byte *ame_parsecheck_addr(Tcl_Interp *ip, const AddrMap_Value *am,
32 const HBytes_Value *hb) {
33 int hbl= cht_hb_len(hb);
35 cht_staticerr(ip,"addr-map address too short","HBYTES ADDRMAP UNDERRUN");
39 cht_staticerr(ip,"addr-map address too long","HBYTES ADDRMAP OVERRUN");
42 return cht_hb_data(hb);
45 static int ame_parsecheck_range(Tcl_Interp *ip, const AddrMap_Value *am,
46 const HBytes_Value *starthb,
47 const HBytes_Value *endhb,
49 p_r[0]= ame_parsecheck_addr(ip,am,starthb); if (!p_r[0]) return TCL_ERROR;
50 p_r[1]= ame_parsecheck_addr(ip,am,endhb); if (!p_r[0]) return TCL_ERROR;
51 if (memcmp(p_r[0],p_r[1],am->byl) > 0)
52 return cht_staticerr(ip, "addr-map range start is after end",
53 "HBYTES ADDRMAP BADRANGE");
57 static int ame_ba_addsubtractone(Byte *out, const Byte *in, int byl,
58 unsigned signum, unsigned onoverflow) {
60 * *in is an array of byl bytes
61 * signum is 0xff or 0x01
62 * onoverflow is what counts as overflowed value,
63 * ie (for unsigned arith) 0x00 for add and 0xff for subtract
65 * *out is the resulting value (subject to overflow truncation)
66 * return value is TCL_OK, or TCL_ERROR if overflow happened
67 * (but interpreter result is not set on overflow)
71 for (j= byl, in += byl, out += byl;
74 *out = (*out) + signum;
75 if (*out != onoverflow)
81 /*---------- useful operations on AddrMap_Value etc. ----------*/
83 static void am_init0(AddrMap_Value *am, int byl) {
90 static void am_reallocentries(AddrMap_Value *am, int len) {
91 AddrMap_Entry *newentries;
93 assert(len >= am->space);
96 assert(len < INT_MAX/sizeof(*newentries));
97 newentries= TREALLOC(am->entries, sizeof(*newentries)*len);
101 am->entries= newentries;
104 static void am_free(AddrMap_Value *am) {
110 for (i=0, ame=am->entries; i<am->used; i++, ame++)
117 /*---------- Tcl type and arg parsing functions ----------*/
119 int cht_pat_addrmapv(Tcl_Interp *ip, Tcl_Obj *var, AddrMap_Var *agg) {
121 rc= cht_pat_somethingv(ip,var,&agg->sth,&cht_addrmap_type);
123 agg->am= agg->sth.obj->internalRep.otherValuePtr;
127 static void addrmap_t_free(Tcl_Obj *o) {
128 AddrMap_Value *am= o->internalRep.otherValuePtr;
132 static void addrmap_t_dup(Tcl_Obj *sob, Tcl_Obj *dob) {
133 AddrMap_Value *sm= sob->internalRep.otherValuePtr;
135 AddrMap_Entry *sme, *dme;
138 assert(sob->typePtr == &cht_addrmap_type);
140 dm= TALLOC(sizeof(*dm));
142 am_init0(dm,sm->byl);
143 am_reallocentries(dm,sm->used);
145 for (i=0, sme=sm->entries, dme=dm->entries;
149 dme->start= TALLOC(sm->byl); assert(dme->start);
150 memcpy(dme->start, sme->start, sm->byl);
151 Tcl_IncrRefCount(dme->data);
153 dob->internalRep.otherValuePtr= dm;
154 dob->typePtr= &cht_addrmap_type;
157 static void addrmap_t_ustr(Tcl_Obj *so) {
158 AddrMap_Value *sm= so->internalRep.otherValuePtr;
159 Tcl_Obj **mainlobjsl, *surrogate;
161 int entnum, listlength;
163 assert(so->typePtr == &cht_addrmap_type);
164 mainlobjsl= TALLOC(sizeof(*mainlobjsl) * (sm->used+1)); assert(mainlobjsl);
165 mainlobjsl[0]= Tcl_NewIntObj(sm->byl * 8);
168 for (entnum=0, sme=sm->entries; entnum<sm->used; entnum++, sme++) {
170 Tcl_Obj *subl[3], *sublo;
172 if (!sme->data) continue;
174 cht_hb_array(&hb, sme->start, sm->byl);
175 subl[0]= cht_ret_hb(0, hb); assert(subl[0]);
177 if (entnum+1 < sm->used) {
178 ame_ba_addsubtractone(cht_hb_arrayspace(&hb, sm->byl),
179 (sme+1)->start, sm->byl,
180 /*subtract:*/ 0x0ffu, 0x0ffu);
182 memset(cht_hb_arrayspace(&hb, sm->byl),
186 subl[1]= cht_ret_hb(0, hb); assert(subl[1]);
189 sublo= Tcl_NewListObj(3,subl); assert(sublo);
190 mainlobjsl[listlength++]= sublo;
192 assert(listlength <= sm->used+1);
193 surrogate= Tcl_NewListObj(listlength,mainlobjsl); assert(surrogate);
196 so->bytes= Tcl_GetStringFromObj(surrogate, &so->length); assert(so->bytes);
197 surrogate->bytes= 0; surrogate->length= 0; /* we stole it */
200 static AddrMap_Entry *ame_sfa_alloc(AddrMap_Value *am) {
203 ame= am->entries + am->used;
206 assert(am->used <= am->space);
208 ame->start= TALLOC(am->byl); assert(ame->start);
213 static int addrmap_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
214 int rc, inlen, eol, innum, bitlen, cmp;
215 Tcl_Obj *eo, *starto, *endo;
216 HBytes_Value starthb, endhb;
217 const Byte *rangeptrs[2];
221 am= TALLOC(sizeof(*am)); assert(am);
224 rc= Tcl_ListObjLength(ip,o,&inlen); if (rc) goto x_badvalue_rc;
227 rc= cht_staticerr(ip, "addr-map overall length < 1", 0);
231 rc= Tcl_ListObjIndex(ip,o,0,&eo); if (rc) goto x_badvalue_rc;
232 rc= Tcl_GetIntFromObj(ip,eo,&bitlen); if (rc) goto x_badvalue_rc;
234 if (bitlen<0 || bitlen % 8) {
235 rc= cht_staticerr(ip, "addr-map overall length < 1", 0);
240 assert(inlen < INT_MAX/2);
241 am_reallocentries(am, (inlen-1)*2+1);
243 ame= ame_sfa_alloc(am);
244 memset(ame->start,0,am->byl);
246 for (innum=1; innum < inlen; innum++) {
247 rc= Tcl_ListObjIndex(ip,o,innum,&eo); if (rc) goto x_badvalue_rc;
248 rc= Tcl_ListObjLength(ip,eo,&eol); if (rc) goto x_badvalue_rc;
251 rc= cht_staticerr(ip, "addr-map entry length != 3", 0);
254 rc= Tcl_ListObjIndex(ip,eo,0,&starto); if (rc) goto x_badvalue_rc;
255 rc= Tcl_ListObjIndex(ip,eo,1,&endo); if (rc) goto x_badvalue_rc;
257 rc= cht_pat_hb(ip,starto,&starthb); if (rc) goto x_badvalue_rc;
258 rc= cht_pat_hb(ip,endo,&endhb); if (rc) goto x_badvalue_rc;
260 rc= ame_parsecheck_range(ip,am,&starthb,&endhb,rangeptrs);
261 if (rc) goto x_badvalue_rc;
263 cmp= memcmp(ame->start, rangeptrs[0], am->byl);
265 rc= cht_staticerr(ip, "addr-map entries out of order", 0);
269 ame= ame_sfa_alloc(am);
270 memcpy(ame->start, rangeptrs[0], am->byl);
274 rc= Tcl_ListObjIndex(ip,eo,2,&ame->data); if (rc) goto x_badvalue_rc;
275 Tcl_IncrRefCount(ame->data);
277 ame= ame_sfa_alloc(am);
278 rc= ame_ba_addsubtractone(ame->start, rangeptrs[1], am->byl,
279 /*add:*/ 0x01u, 0x00u);
281 /* we've overflowed. it must have been ffffffff.... */
282 if (innum != inlen-1) {
283 rc= cht_staticerr(ip, "addr-map non-last entry end is all-bits-1", 0);
294 o->internalRep.otherValuePtr= am;
295 o->typePtr= &cht_addrmap_type;
300 Tcl_SetObjErrorCode(ip, Tcl_NewStringObj("HBYTES ADDRMAP VALUE", -1));
306 Tcl_ObjType cht_addrmap_type = {
308 addrmap_t_free, addrmap_t_dup, addrmap_t_ustr, addrmap_t_sfa