import Devel-Size 0.52 from CPAN
[p5sagit/Devel-Size.git] / Size.xs
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 #define carp puts
6
7 #if !defined(NV)
8 #define NV double
9 #endif
10
11 /* Checks to see if thing is in the hash. Returns true or false, and
12    notes thing in the hash.
13
14    This code does one Evil Thing. Since we're tracking pointers, we
15    tell perl that the string key is the address in the pointer. We do this by
16    passing in the address of the address, along with the size of a
17    pointer as the length. Perl then uses the four (or eight, on
18    64-bit machines) bytes of the address as the string we're using as
19    the key */
20 IV check_new(HV *tracking_hash, void *thing) {
21   if (hv_exists(tracking_hash, (char *)&thing, sizeof(void *))) {
22     return FALSE;
23   }
24   hv_store(tracking_hash, (char *)&thing, sizeof(void *), &PL_sv_undef, 0);
25   return TRUE;
26
27 }
28
29 /* Figure out how much magic is attached to the SV and return the
30    size */
31 IV magic_size(SV *thing, HV *tracking_hash) {
32   IV total_size = 0;
33   MAGIC *magic_pointer;
34
35   /* Is there any? */
36   if (!SvMAGIC(thing)) {
37     /* No, bail */
38     return 0;
39   }
40
41   /* Get the base magic pointer */
42   magic_pointer = SvMAGIC(thing);
43
44   /* Have we seen the magic pointer? */
45   while (magic_pointer && check_new(tracking_hash, magic_pointer)) {
46     total_size += sizeof(MAGIC);
47
48     /* Have we seen the magic vtable? */
49     if (magic_pointer->mg_virtual &&
50         check_new(tracking_hash, magic_pointer->mg_virtual)) {
51       total_size += sizeof(MGVTBL);
52     }
53
54     /* Get the next in the chain */
55     magic_pointer = magic_pointer->mg_moremagic;
56   }
57
58   return total_size;
59 }
60
61
62 UV thing_size(SV *orig_thing, HV *tracking_hash) {
63   SV *thing = orig_thing;
64   UV total_size = sizeof(SV);
65   
66   switch (SvTYPE(thing)) {
67     /* Is it undef? */
68   case SVt_NULL:
69     break;
70     /* Just a plain integer. This will be differently sized depending
71        on whether purify's been compiled in */
72   case SVt_IV:
73 #ifdef PURIFY
74     total_size += sizeof(sizeof(XPVIV));
75 #else
76     total_size += sizeof(IV);
77 #endif
78     break;
79     /* Is it a float? Like the int, it depends on purify */
80   case SVt_NV:
81 #ifdef PURIFY
82     total_size += sizeof(sizeof(XPVNV));
83 #else
84     total_size += sizeof(NV);
85 #endif
86     break;
87     /* Is it a reference? */
88   case SVt_RV:
89     total_size += sizeof(XRV);
90     break;
91     /* How about a plain string? In which case we need to add in how
92        much has been allocated */
93   case SVt_PV:
94     total_size += sizeof(XPV);
95     total_size += SvLEN(thing);
96     break;
97     /* A string with an integer part? */
98   case SVt_PVIV:
99     total_size += sizeof(XPVIV);
100     total_size += SvLEN(thing);
101     break;
102     /* A string with a float part? */
103   case SVt_PVNV:
104     total_size += sizeof(XPVNV);
105     total_size += SvLEN(thing);
106     break;
107   case SVt_PVMG:
108     total_size += sizeof(XPVMG);
109     total_size += SvLEN(thing);
110     total_size += magic_size(thing, tracking_hash);
111     break;
112   case SVt_PVBM:
113     total_size += sizeof(XPVBM);
114     total_size += SvLEN(thing);
115     total_size += magic_size(thing, tracking_hash);
116     break;
117   case SVt_PVLV:
118     total_size += sizeof(XPVLV);
119     total_size += SvLEN(thing);
120     total_size += magic_size(thing, tracking_hash);
121     break;
122     /* How much space is dedicated to the array? Not counting the
123        elements in the array, mind, just the array itself */
124   case SVt_PVAV:
125     total_size += sizeof(XPVAV);
126     /* Is there anything in the array? */
127     if (AvMAX(thing) != -1) {
128       total_size += sizeof(SV *) * AvMAX(thing);
129     }
130     /* Add in the bits on the other side of the beginning */
131     total_size += (sizeof(SV *) * (AvARRAY(thing) - AvALLOC(thing)));
132     /* Is there something hanging off the arylen element? */
133     if (AvARYLEN(thing)) {
134       if (check_new(tracking_hash, AvARYLEN(thing))) {
135         total_size += thing_size(AvARYLEN(thing), tracking_hash);
136       }
137     }
138     total_size += magic_size(thing, tracking_hash);
139     break;
140   case SVt_PVHV:
141     /* First the base struct */
142     total_size += sizeof(XPVHV);
143     /* Now the array of buckets */
144     total_size += (sizeof(HE *) * (HvMAX(thing) + 1));
145     /* Now walk the bucket chain */
146     if (HvARRAY(thing)) {
147       HE *cur_entry;
148       IV cur_bucket = 0;
149       for (cur_bucket = 0; cur_bucket <= HvMAX(thing); cur_bucket++) {
150         cur_entry = *(HvARRAY(thing) + cur_bucket);
151         while (cur_entry) {
152           total_size += sizeof(HE);
153           if (cur_entry->hent_hek) {
154             /* Hash keys can be shared. Have we seen this before? */
155             if (check_new(tracking_hash, cur_entry->hent_hek)) {
156               total_size += sizeof(HEK);
157               total_size += cur_entry->hent_hek->hek_len - 1;
158             }
159           }
160           cur_entry = cur_entry->hent_next;
161         }
162       }
163     }
164     total_size += magic_size(thing, tracking_hash);
165     break;
166   case SVt_PVCV:
167     total_size += sizeof(XPVCV);
168     total_size += magic_size(thing, tracking_hash);
169     carp("CV isn't complete");
170     break;
171   case SVt_PVGV:
172     total_size += magic_size(thing, tracking_hash);
173     total_size += sizeof(XPVGV);
174     total_size += GvNAMELEN(thing);
175     /* Is there something hanging off the glob? */
176     if (GvGP(thing)) {
177       if (check_new(tracking_hash, GvGP(thing))) {
178         total_size += sizeof(GP);
179       }
180     }
181     carp("GC isn't complete");
182     break;
183   case SVt_PVFM:
184     total_size += sizeof(XPVFM);
185     carp("FM isn't complete");
186     break;
187   case SVt_PVIO:
188     total_size += sizeof(XPVIO);
189     carp("IO isn't complete");
190     break;
191   default:
192     croak("Unknown variable type");
193   }
194   return total_size;
195 }
196
197
198 MODULE = Devel::Size            PACKAGE = Devel::Size           
199
200 IV
201 size(orig_thing)
202      SV *orig_thing
203 CODE:
204 {
205   SV *thing = orig_thing;
206   /* Hash to track our seen pointers */
207   HV *tracking_hash = newHV();
208
209   /* If they passed us a reference then dereference it. This is the
210      only way we can check the sizes of arrays and hashes */
211   if (SvOK(thing) && SvROK(thing)) {
212     thing = SvRV(thing);
213   }
214   
215   RETVAL = thing_size(thing, tracking_hash);
216   /* Clean up after ourselves */
217   SvREFCNT_dec(tracking_hash);
218 }
219 OUTPUT:
220   RETVAL
221
222
223 IV
224 total_size(orig_thing)
225        SV *orig_thing
226 CODE:
227 {
228   SV *thing = orig_thing;
229   /* Hash to track our seen pointers */
230   HV *tracking_hash = newHV();
231   AV *pending_array = newAV();
232
233   /* Size starts at zero */
234   RETVAL = 0;
235
236   /* If they passed us a reference then dereference it. This is the
237      only way we can check the sizes of arrays and hashes */
238   if (SvOK(thing) && SvROK(thing)) {
239     thing = SvRV(thing);
240   }
241
242   /* Put it on the pending array */
243   av_push(pending_array, thing);
244
245   /* Now just yank things off the end of the array until it's done */
246   while (av_len(pending_array) >= 0) {
247     thing = av_pop(pending_array);
248     /* Process it if we've not seen it */
249     if (check_new(tracking_hash, thing)) {
250       /* Is it valid? */
251       if (thing) {
252         /* Yes, it is. So let's check the type */
253         switch (SvTYPE(thing)) {
254         case SVt_RV:
255           av_push(pending_array, SvRV(thing));
256           break;
257
258         case SVt_PVAV:
259           {
260             /* Quick alias to cut down on casting */
261             AV *tempAV = (AV *)thing;
262             SV **tempSV;
263             
264             /* Any elements? */
265             if (av_len(tempAV) != -1) {
266               IV index;
267               /* Run through them all */
268               for (index = 0; index <= av_len(tempAV); index++) {
269                 /* Did we get something? */
270                 if (tempSV = av_fetch(tempAV, index, 0)) {
271                   /* Was it undef? */
272                   if (*tempSV != &PL_sv_undef) {
273                     /* Apparently not. Save it for later */
274                     av_push(pending_array, *tempSV);
275                   }
276                 }
277               }
278             }
279           }
280           break;
281
282         case SVt_PVHV:
283           /* Is there anything in here? */
284           if (hv_iterinit((HV *)thing)) {
285             HE *temp_he;
286             while (temp_he = hv_iternext((HV *)thing)) {
287               av_push(pending_array, hv_iterval((HV *)thing, temp_he));
288             }
289           }
290           break;
291          
292         case SVt_PVGV:
293         default:
294           break;
295         }
296       }
297
298       RETVAL += thing_size(thing, tracking_hash);
299     }
300   }
301   
302   /* Clean up after ourselves */
303   SvREFCNT_dec(tracking_hash);
304   SvREFCNT_dec(pending_array);
305 }
306 OUTPUT:
307   RETVAL
308