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