import Devel-Size 0.68 from CPAN
[p5sagit/Devel-Size.git] / Size.xs
CommitLineData
e98cedbf 1#include "EXTERN.h"
2#include "perl.h"
3#include "XSUB.h"
4
98ecbbc6 5static int regex_whine;
6static int fm_whine;
7
8
6a9ad7ec 9#define carp puts
7ccc7d88 10UV thing_size(SV *, HV *);
11typedef enum {
12 OPc_NULL, /* 0 */
13 OPc_BASEOP, /* 1 */
14 OPc_UNOP, /* 2 */
15 OPc_BINOP, /* 3 */
16 OPc_LOGOP, /* 4 */
17 OPc_LISTOP, /* 5 */
18 OPc_PMOP, /* 6 */
19 OPc_SVOP, /* 7 */
20 OPc_PADOP, /* 8 */
21 OPc_PVOP, /* 9 */
62691e7c 22 OPc_LOOP, /* 10 */
23 OPc_COP /* 11 */
7ccc7d88 24} opclass;
25
26static opclass
27cc_opclass(OP *o)
28{
29 if (!o)
30 return OPc_NULL;
31
32 if (o->op_type == 0)
33 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
34
35 if (o->op_type == OP_SASSIGN)
36 return ((o->op_private & OPpASSIGN_BACKWARDS) ? OPc_UNOP : OPc_BINOP);
37
38#ifdef USE_ITHREADS
39 if (o->op_type == OP_GV || o->op_type == OP_GVSV || o->op_type == OP_AELEMFAST)
40 return OPc_PADOP;
41#endif
42
0430b7f7 43 if ((o->op_type = OP_TRANS)) {
62691e7c 44 return OPc_BASEOP;
45 }
46
7ccc7d88 47 switch (PL_opargs[o->op_type] & OA_CLASS_MASK) {
48 case OA_BASEOP:
49 return OPc_BASEOP;
50
51 case OA_UNOP:
52 return OPc_UNOP;
53
54 case OA_BINOP:
55 return OPc_BINOP;
56
57 case OA_LOGOP:
58 return OPc_LOGOP;
59
60 case OA_LISTOP:
61 return OPc_LISTOP;
62
63 case OA_PMOP:
64 return OPc_PMOP;
65
66 case OA_SVOP:
67 return OPc_SVOP;
68
69 case OA_PADOP:
70 return OPc_PADOP;
71
72 case OA_PVOP_OR_SVOP:
73 /*
74 * Character translations (tr///) are usually a PVOP, keeping a
75 * pointer to a table of shorts used to look up translations.
76 * Under utf8, however, a simple table isn't practical; instead,
77 * the OP is an SVOP, and the SV is a reference to a swash
78 * (i.e., an RV pointing to an HV).
79 */
80 return (o->op_private & (OPpTRANS_TO_UTF|OPpTRANS_FROM_UTF))
81 ? OPc_SVOP : OPc_PVOP;
82
83 case OA_LOOP:
84 return OPc_LOOP;
85
86 case OA_COP:
87 return OPc_COP;
88
89 case OA_BASEOP_OR_UNOP:
90 /*
91 * UNI(OP_foo) in toke.c returns token UNI or FUNC1 depending on
92 * whether parens were seen. perly.y uses OPf_SPECIAL to
93 * signal whether a BASEOP had empty parens or none.
94 * Some other UNOPs are created later, though, so the best
95 * test is OPf_KIDS, which is set in newUNOP.
96 */
97 return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP;
98
99 case OA_FILESTATOP:
100 /*
101 * The file stat OPs are created via UNI(OP_foo) in toke.c but use
102 * the OPf_REF flag to distinguish between OP types instead of the
103 * usual OPf_SPECIAL flag. As usual, if OPf_KIDS is set, then we
104 * return OPc_UNOP so that walkoptree can find our children. If
105 * OPf_KIDS is not set then we check OPf_REF. Without OPf_REF set
106 * (no argument to the operator) it's an OP; with OPf_REF set it's
107 * an SVOP (and op_sv is the GV for the filehandle argument).
108 */
109 return ((o->op_flags & OPf_KIDS) ? OPc_UNOP :
110#ifdef USE_ITHREADS
111 (o->op_flags & OPf_REF) ? OPc_PADOP : OPc_BASEOP);
112#else
113 (o->op_flags & OPf_REF) ? OPc_SVOP : OPc_BASEOP);
114#endif
115 case OA_LOOPEXOP:
116 /*
117 * next, last, redo, dump and goto use OPf_SPECIAL to indicate that a
118 * label was omitted (in which case it's a BASEOP) or else a term was
119 * seen. In this last case, all except goto are definitely PVOP but
120 * goto is either a PVOP (with an ordinary constant label), an UNOP
121 * with OPf_STACKED (with a non-constant non-sub) or an UNOP for
122 * OP_REFGEN (with goto &sub) in which case OPf_STACKED also seems to
123 * get set.
124 */
125 if (o->op_flags & OPf_STACKED)
126 return OPc_UNOP;
127 else if (o->op_flags & OPf_SPECIAL)
128 return OPc_BASEOP;
129 else
130 return OPc_PVOP;
131 }
132 warn("can't determine class of operator %s, assuming BASEOP\n",
133 PL_op_name[o->op_type]);
134 return OPc_BASEOP;
135}
136
6a9ad7ec 137
a6ea0805 138#if !defined(NV)
139#define NV double
140#endif
141
ebb2c5b9 142static int go_yell = 1;
143
6a9ad7ec 144/* Checks to see if thing is in the hash. Returns true or false, and
145 notes thing in the hash.
146
147 This code does one Evil Thing. Since we're tracking pointers, we
148 tell perl that the string key is the address in the pointer. We do this by
149 passing in the address of the address, along with the size of a
150 pointer as the length. Perl then uses the four (or eight, on
151 64-bit machines) bytes of the address as the string we're using as
152 the key */
24d37977 153IV check_new(HV *tracking_hash, const void *thing) {
7ccc7d88 154 if (NULL == thing) {
155 return FALSE;
156 }
6a9ad7ec 157 if (hv_exists(tracking_hash, (char *)&thing, sizeof(void *))) {
158 return FALSE;
159 }
78dfb4e7 160 hv_store(tracking_hash, (char *)&thing, sizeof(void *), &PL_sv_yes, 0);
6a9ad7ec 161 return TRUE;
162
163}
164
165/* Figure out how much magic is attached to the SV and return the
166 size */
167IV magic_size(SV *thing, HV *tracking_hash) {
168 IV total_size = 0;
169 MAGIC *magic_pointer;
170
171 /* Is there any? */
172 if (!SvMAGIC(thing)) {
173 /* No, bail */
174 return 0;
175 }
176
177 /* Get the base magic pointer */
178 magic_pointer = SvMAGIC(thing);
179
180 /* Have we seen the magic pointer? */
181 while (magic_pointer && check_new(tracking_hash, magic_pointer)) {
182 total_size += sizeof(MAGIC);
183
184 /* Have we seen the magic vtable? */
185 if (magic_pointer->mg_virtual &&
186 check_new(tracking_hash, magic_pointer->mg_virtual)) {
187 total_size += sizeof(MGVTBL);
188 }
189
190 /* Get the next in the chain */
191 magic_pointer = magic_pointer->mg_moremagic;
192 }
193
194 return total_size;
195}
196
7ccc7d88 197UV regex_size(REGEXP *baseregex, HV *tracking_hash) {
198 UV total_size = 0;
199
98ecbbc6 200 total_size += sizeof(REGEXP);
201 /* Note hte size of the paren offset thing */
202 total_size += sizeof(I32) * baseregex->nparens * 2;
203 total_size += strlen(baseregex->precomp);
204
205 if (go_yell && !regex_whine) {
206 carp("Devel::Size: Calculated sizes for compiled regexes are incomple, and probably always will be");
207 regex_whine = 1;
208 }
209
7ccc7d88 210 return total_size;
211}
212
213UV op_size(OP *baseop, HV *tracking_hash) {
214 UV total_size = 0;
215
216 if (check_new(tracking_hash, baseop->op_next)) {
217 total_size += op_size(baseop->op_next, tracking_hash);
218 }
7ccc7d88 219
220 switch (cc_opclass(baseop)) {
221 case OPc_BASEOP:
222 total_size += sizeof(struct op);
223 break;
224 case OPc_UNOP:
225 total_size += sizeof(struct unop);
226 if (check_new(tracking_hash, cUNOPx(baseop)->op_first)) {
227 total_size += op_size(cUNOPx(baseop)->op_first, tracking_hash);
228 }
229 break;
230 case OPc_BINOP:
231 total_size += sizeof(struct binop);
232 if (check_new(tracking_hash, cBINOPx(baseop)->op_first)) {
233 total_size += op_size(cBINOPx(baseop)->op_first, tracking_hash);
234 }
235 if (check_new(tracking_hash, cBINOPx(baseop)->op_last)) {
236 total_size += op_size(cBINOPx(baseop)->op_last, tracking_hash);
237 }
238 break;
239 case OPc_LOGOP:
240 total_size += sizeof(struct logop);
241 if (check_new(tracking_hash, cLOGOPx(baseop)->op_first)) {
242 total_size += op_size(cBINOPx(baseop)->op_first, tracking_hash);
243 }
244 if (check_new(tracking_hash, cLOGOPx(baseop)->op_other)) {
245 total_size += op_size(cLOGOPx(baseop)->op_other, tracking_hash);
246 }
247 break;
248 case OPc_LISTOP:
249 total_size += sizeof(struct listop);
250 if (check_new(tracking_hash, cLISTOPx(baseop)->op_first)) {
251 total_size += op_size(cLISTOPx(baseop)->op_first, tracking_hash);
252 }
253 if (check_new(tracking_hash, cLISTOPx(baseop)->op_last)) {
254 total_size += op_size(cLISTOPx(baseop)->op_last, tracking_hash);
255 }
256 break;
257 case OPc_PMOP:
258 total_size += sizeof(struct pmop);
259 if (check_new(tracking_hash, cPMOPx(baseop)->op_first)) {
260 total_size += op_size(cPMOPx(baseop)->op_first, tracking_hash);
261 }
262 if (check_new(tracking_hash, cPMOPx(baseop)->op_last)) {
263 total_size += op_size(cPMOPx(baseop)->op_last, tracking_hash);
264 }
265 if (check_new(tracking_hash, cPMOPx(baseop)->op_pmreplroot)) {
266 total_size += op_size(cPMOPx(baseop)->op_pmreplroot, tracking_hash);
267 }
268 if (check_new(tracking_hash, cPMOPx(baseop)->op_pmreplstart)) {
269 total_size += op_size(cPMOPx(baseop)->op_pmreplstart, tracking_hash);
270 }
271 if (check_new(tracking_hash, cPMOPx(baseop)->op_pmnext)) {
272 total_size += op_size((OP *)cPMOPx(baseop)->op_pmnext, tracking_hash);
273 }
98ecbbc6 274 /* This is defined away in perl 5.8.x, but it is in there for
275 5.6.x */
276#ifdef PM_GETRE
277 if (check_new(tracking_hash, PM_GETRE((cPMOPx(baseop))))) {
278 total_size += regex_size(PM_GETRE(cPMOPx(baseop)), tracking_hash);
279 }
280#else
281 if (check_new(tracking_hash, cPMOPx(baseop)->op_pmregexp)) {
282 total_size += regex_size(cPMOPx(baseop)->op_pmregexp, tracking_hash);
283 }
284#endif
7ccc7d88 285 break;
286 case OPc_SVOP:
287 total_size += sizeof(struct pmop);
288 if (check_new(tracking_hash, cSVOPx(baseop)->op_sv)) {
289 total_size += thing_size(cSVOPx(baseop)->op_sv, tracking_hash);
290 }
291 break;
292 case OPc_PADOP:
293 total_size += sizeof(struct padop);
294 break;
295 case OPc_PVOP:
296 if (check_new(tracking_hash, cPVOPx(baseop)->op_pv)) {
297 total_size += strlen(cPVOPx(baseop)->op_pv);
298 }
299 case OPc_LOOP:
300 total_size += sizeof(struct loop);
301 if (check_new(tracking_hash, cLOOPx(baseop)->op_first)) {
302 total_size += op_size(cLOOPx(baseop)->op_first, tracking_hash);
303 }
304 if (check_new(tracking_hash, cLOOPx(baseop)->op_last)) {
305 total_size += op_size(cLOOPx(baseop)->op_last, tracking_hash);
306 }
307 if (check_new(tracking_hash, cLOOPx(baseop)->op_redoop)) {
308 total_size += op_size(cLOOPx(baseop)->op_redoop, tracking_hash);
309 }
310 if (check_new(tracking_hash, cLOOPx(baseop)->op_nextop)) {
311 total_size += op_size(cLOOPx(baseop)->op_nextop, tracking_hash);
312 }
98ecbbc6 313 /* Not working for some reason, but the code's here for later
314 fixing
315 if (check_new(tracking_hash, cLOOPx(baseop)->op_lastop)) {
316 total_size += op_size(cLOOPx(baseop)->op_lastop, tracking_hash);
317 }
318 */
62691e7c 319 break;
7ccc7d88 320 case OPc_COP:
321 {
322 COP *basecop;
323 basecop = (COP *)baseop;
324 total_size += sizeof(struct cop);
325
326 if (check_new(tracking_hash, basecop->cop_label)) {
327 total_size += strlen(basecop->cop_label);
328 }
329#ifdef USE_ITHREADS
330 if (check_new(tracking_hash, basecop->cop_file)) {
331 total_size += strlen(basecop->cop_file);
332 }
333 if (check_new(tracking_hash, basecop->cop_stashpv)) {
334 total_size += strlen(basecop->cop_stashpv);
335 }
336#else
337 if (check_new(tracking_hash, basecop->cop_stash)) {
338 total_size += thing_size((SV *)basecop->cop_stash, tracking_hash);
339 }
340 if (check_new(tracking_hash, basecop->cop_filegv)) {
341 total_size += thing_size((SV *)basecop->cop_filegv, tracking_hash);
342 }
343#endif
344
345 }
346 break;
347 default:
348 break;
349 }
350 return total_size;
351}
6a9ad7ec 352
24d37977 353#if PERL_VERSION > 9 || (PERL_VERSION == 9 && PERL_SUBVERSION > 2)
354# define NEW_HEAD_LAYOUT
355#endif
356
6a9ad7ec 357UV thing_size(SV *orig_thing, HV *tracking_hash) {
e98cedbf 358 SV *thing = orig_thing;
359 UV total_size = sizeof(SV);
b1e5ad85 360
e98cedbf 361 switch (SvTYPE(thing)) {
362 /* Is it undef? */
363 case SVt_NULL:
364 break;
365 /* Just a plain integer. This will be differently sized depending
366 on whether purify's been compiled in */
367 case SVt_IV:
24d37977 368#ifndef NEW_HEAD_LAYOUT
369# ifdef PURIFY
e98cedbf 370 total_size += sizeof(sizeof(XPVIV));
24d37977 371# else
e98cedbf 372 total_size += sizeof(IV);
24d37977 373# endif
e98cedbf 374#endif
375 break;
376 /* Is it a float? Like the int, it depends on purify */
377 case SVt_NV:
378#ifdef PURIFY
379 total_size += sizeof(sizeof(XPVNV));
380#else
381 total_size += sizeof(NV);
382#endif
383 break;
384 /* Is it a reference? */
385 case SVt_RV:
24d37977 386#ifndef NEW_HEAD_LAYOUT
e98cedbf 387 total_size += sizeof(XRV);
24d37977 388#endif
e98cedbf 389 break;
390 /* How about a plain string? In which case we need to add in how
391 much has been allocated */
392 case SVt_PV:
393 total_size += sizeof(XPV);
b1e5ad85 394 total_size += SvROK(thing) ? thing_size( SvRV(thing), tracking_hash) : SvLEN(thing);
e98cedbf 395 break;
396 /* A string with an integer part? */
397 case SVt_PVIV:
398 total_size += sizeof(XPVIV);
b1e5ad85 399 total_size += SvROK(thing) ? thing_size( SvRV(thing), tracking_hash) : SvLEN(thing);
0430b7f7 400 if(SvOOK(thing)) {
401 total_size += SvIVX(thing);
402 }
e98cedbf 403 break;
c8db37d3 404 /* A scalar/string/reference with a float part? */
e98cedbf 405 case SVt_PVNV:
406 total_size += sizeof(XPVNV);
b1e5ad85 407 total_size += SvROK(thing) ? thing_size( SvRV(thing), tracking_hash) : SvLEN(thing);
e98cedbf 408 break;
409 case SVt_PVMG:
4ab42718 410 total_size += sizeof(XPVMG);
b1e5ad85 411 total_size += SvROK(thing) ? thing_size( SvRV(thing), tracking_hash) : SvLEN(thing);
6a9ad7ec 412 total_size += magic_size(thing, tracking_hash);
e98cedbf 413 break;
0430b7f7 414#if PERL_VERSION <= 8
e98cedbf 415 case SVt_PVBM:
6a9ad7ec 416 total_size += sizeof(XPVBM);
b1e5ad85 417 total_size += SvROK(thing) ? thing_size( SvRV(thing), tracking_hash) : SvLEN(thing);
6a9ad7ec 418 total_size += magic_size(thing, tracking_hash);
e98cedbf 419 break;
0430b7f7 420#endif
e98cedbf 421 case SVt_PVLV:
6a9ad7ec 422 total_size += sizeof(XPVLV);
b1e5ad85 423 total_size += SvROK(thing) ? thing_size( SvRV(thing), tracking_hash) : SvLEN(thing);
6a9ad7ec 424 total_size += magic_size(thing, tracking_hash);
e98cedbf 425 break;
426 /* How much space is dedicated to the array? Not counting the
427 elements in the array, mind, just the array itself */
428 case SVt_PVAV:
429 total_size += sizeof(XPVAV);
430 /* Is there anything in the array? */
431 if (AvMAX(thing) != -1) {
c8db37d3 432 /* an array with 10 slots has AvMax() set to 9 - te 2007-04-22 */
433 total_size += sizeof(SV *) * (AvMAX(thing) + 1);
434 /* printf ("total_size: %li AvMAX: %li av_len: %i\n", total_size, AvMAX(thing), av_len(thing)); */
e98cedbf 435 }
436 /* Add in the bits on the other side of the beginning */
0430b7f7 437
c8db37d3 438 /* printf ("total_size %li, sizeof(SV *) %li, AvARRAY(thing) %li, AvALLOC(thing)%li , sizeof(ptr) %li \n",
0430b7f7 439 total_size, sizeof(SV*), AvARRAY(thing), AvALLOC(thing), sizeof( thing )); */
440
441 /* under Perl 5.8.8 64bit threading, AvARRAY(thing) was a pointer while AvALLOC was 0,
b1e5ad85 442 resulting in grossly overstated sized for arrays. Technically, this shouldn't happen... */
0430b7f7 443 if (AvALLOC(thing) != 0) {
444 total_size += (sizeof(SV *) * (AvARRAY(thing) - AvALLOC(thing)));
445 }
e98cedbf 446 /* Is there something hanging off the arylen element? */
447 if (AvARYLEN(thing)) {
6a9ad7ec 448 if (check_new(tracking_hash, AvARYLEN(thing))) {
449 total_size += thing_size(AvARYLEN(thing), tracking_hash);
450 }
e98cedbf 451 }
6a9ad7ec 452 total_size += magic_size(thing, tracking_hash);
e98cedbf 453 break;
454 case SVt_PVHV:
a6ea0805 455 /* First the base struct */
456 total_size += sizeof(XPVHV);
457 /* Now the array of buckets */
458 total_size += (sizeof(HE *) * (HvMAX(thing) + 1));
459 /* Now walk the bucket chain */
6a9ad7ec 460 if (HvARRAY(thing)) {
a6ea0805 461 HE *cur_entry;
462 IV cur_bucket = 0;
a6ea0805 463 for (cur_bucket = 0; cur_bucket <= HvMAX(thing); cur_bucket++) {
464 cur_entry = *(HvARRAY(thing) + cur_bucket);
465 while (cur_entry) {
466 total_size += sizeof(HE);
467 if (cur_entry->hent_hek) {
6a9ad7ec 468 /* Hash keys can be shared. Have we seen this before? */
469 if (check_new(tracking_hash, cur_entry->hent_hek)) {
b98fcdb9 470 total_size += HEK_BASESIZE + cur_entry->hent_hek->hek_len + 2;
6a9ad7ec 471 }
a6ea0805 472 }
473 cur_entry = cur_entry->hent_next;
474 }
475 }
476 }
6a9ad7ec 477 total_size += magic_size(thing, tracking_hash);
e98cedbf 478 break;
479 case SVt_PVCV:
6a9ad7ec 480 total_size += sizeof(XPVCV);
5c2e1b12 481 total_size += magic_size(thing, tracking_hash);
7ccc7d88 482
483 total_size += ((XPVIO *) SvANY(thing))->xpv_len;
484 if (check_new(tracking_hash, CvSTASH(thing))) {
485 total_size += thing_size((SV *)CvSTASH(thing), tracking_hash);
486 }
487 if (check_new(tracking_hash, SvSTASH(thing))) {
488 total_size += thing_size((SV *)SvSTASH(thing), tracking_hash);
489 }
490 if (check_new(tracking_hash, CvGV(thing))) {
491 total_size += thing_size((SV *)CvGV(thing), tracking_hash);
ebb2c5b9 492 }
7ccc7d88 493 if (check_new(tracking_hash, CvPADLIST(thing))) {
494 total_size += thing_size((SV *)CvPADLIST(thing), tracking_hash);
495 }
496 if (check_new(tracking_hash, CvOUTSIDE(thing))) {
497 total_size += thing_size((SV *)CvOUTSIDE(thing), tracking_hash);
498 }
499
500 if (check_new(tracking_hash, CvSTART(thing))) {
501 total_size += op_size(CvSTART(thing), tracking_hash);
502 }
503 if (check_new(tracking_hash, CvROOT(thing))) {
504 total_size += op_size(CvROOT(thing), tracking_hash);
505 }
506
e98cedbf 507 break;
508 case SVt_PVGV:
5c2e1b12 509 total_size += magic_size(thing, tracking_hash);
6a9ad7ec 510 total_size += sizeof(XPVGV);
5c2e1b12 511 total_size += GvNAMELEN(thing);
78dfb4e7 512#ifdef GvFILE
0bff12d8 513 /* Is there a file? */
514 if (GvFILE(thing)) {
515 if (check_new(tracking_hash, GvFILE(thing))) {
516 total_size += strlen(GvFILE(thing));
517 }
518 }
78dfb4e7 519#endif
5c2e1b12 520 /* Is there something hanging off the glob? */
521 if (GvGP(thing)) {
522 if (check_new(tracking_hash, GvGP(thing))) {
523 total_size += sizeof(GP);
5073b933 524 {
525 SV *generic_thing;
0430b7f7 526 if ((generic_thing = (SV *)(GvGP(thing)->gp_sv))) {
5073b933 527 total_size += thing_size(generic_thing, tracking_hash);
528 }
0430b7f7 529 if ((generic_thing = (SV *)(GvGP(thing)->gp_form))) {
5073b933 530 total_size += thing_size(generic_thing, tracking_hash);
531 }
0430b7f7 532 if ((generic_thing = (SV *)(GvGP(thing)->gp_av))) {
5073b933 533 total_size += thing_size(generic_thing, tracking_hash);
534 }
0430b7f7 535 if ((generic_thing = (SV *)(GvGP(thing)->gp_hv))) {
5073b933 536 total_size += thing_size(generic_thing, tracking_hash);
537 }
0430b7f7 538 if ((generic_thing = (SV *)(GvGP(thing)->gp_egv))) {
5073b933 539 total_size += thing_size(generic_thing, tracking_hash);
540 }
0430b7f7 541 if ((generic_thing = (SV *)(GvGP(thing)->gp_cv))) {
5073b933 542 total_size += thing_size(generic_thing, tracking_hash);
543 }
544 }
5c2e1b12 545 }
546 }
e98cedbf 547 break;
548 case SVt_PVFM:
6a9ad7ec 549 total_size += sizeof(XPVFM);
7ccc7d88 550 total_size += magic_size(thing, tracking_hash);
551 total_size += ((XPVIO *) SvANY(thing))->xpv_len;
552 if (check_new(tracking_hash, CvPADLIST(thing))) {
553 total_size += thing_size((SV *)CvPADLIST(thing), tracking_hash);
554 }
555 if (check_new(tracking_hash, CvOUTSIDE(thing))) {
556 total_size += thing_size((SV *)CvOUTSIDE(thing), tracking_hash);
557 }
558
98ecbbc6 559 if (go_yell && !fm_whine) {
5073b933 560 carp("Devel::Size: Calculated sizes for FMs are incomplete");
98ecbbc6 561 fm_whine = 1;
ebb2c5b9 562 }
e98cedbf 563 break;
564 case SVt_PVIO:
6a9ad7ec 565 total_size += sizeof(XPVIO);
5073b933 566 total_size += magic_size(thing, tracking_hash);
24d37977 567 if (check_new(tracking_hash, (SvPVX(thing)))) {
5073b933 568 total_size += ((XPVIO *) SvANY(thing))->xpv_cur;
ebb2c5b9 569 }
5073b933 570 /* Some embedded char pointers */
571 if (check_new(tracking_hash, ((XPVIO *) SvANY(thing))->xio_top_name)) {
572 total_size += strlen(((XPVIO *) SvANY(thing))->xio_top_name);
573 }
574 if (check_new(tracking_hash, ((XPVIO *) SvANY(thing))->xio_fmt_name)) {
575 total_size += strlen(((XPVIO *) SvANY(thing))->xio_fmt_name);
576 }
577 if (check_new(tracking_hash, ((XPVIO *) SvANY(thing))->xio_bottom_name)) {
578 total_size += strlen(((XPVIO *) SvANY(thing))->xio_bottom_name);
579 }
580 /* Throw the GVs on the list to be walked if they're not-null */
581 if (((XPVIO *) SvANY(thing))->xio_top_gv) {
582 total_size += thing_size((SV *)((XPVIO *) SvANY(thing))->xio_top_gv,
583 tracking_hash);
584 }
585 if (((XPVIO *) SvANY(thing))->xio_bottom_gv) {
586 total_size += thing_size((SV *)((XPVIO *) SvANY(thing))->xio_bottom_gv,
587 tracking_hash);
588 }
589 if (((XPVIO *) SvANY(thing))->xio_fmt_gv) {
590 total_size += thing_size((SV *)((XPVIO *) SvANY(thing))->xio_fmt_gv,
591 tracking_hash);
592 }
593
594 /* Only go trotting through the IO structures if they're really
595 trottable. If USE_PERLIO is defined we can do this. If
596 not... we can't, so we don't even try */
597#ifdef USE_PERLIO
598 /* Dig into xio_ifp and xio_ofp here */
599 croak("Devel::Size: Can't size up perlio layers yet");
600#endif
e98cedbf 601 break;
602 default:
5073b933 603 croak("Devel::Size: Unknown variable type");
e98cedbf 604 }
605 return total_size;
606}
607
e98cedbf 608MODULE = Devel::Size PACKAGE = Devel::Size
609
fea63ffa 610PROTOTYPES: DISABLE
611
a6ea0805 612IV
613size(orig_thing)
614 SV *orig_thing
e98cedbf 615CODE:
616{
6a9ad7ec 617 SV *thing = orig_thing;
618 /* Hash to track our seen pointers */
619 HV *tracking_hash = newHV();
ebb2c5b9 620 SV *warn_flag;
621
622 /* Check warning status */
623 go_yell = 0;
98ecbbc6 624 regex_whine = 0;
625 fm_whine = 0;
ebb2c5b9 626
78dfb4e7 627 if (NULL != (warn_flag = perl_get_sv("Devel::Size::warn", FALSE))) {
ebb2c5b9 628 go_yell = SvIV(warn_flag);
629 }
630
6a9ad7ec 631
632 /* If they passed us a reference then dereference it. This is the
633 only way we can check the sizes of arrays and hashes */
634 if (SvOK(thing) && SvROK(thing)) {
635 thing = SvRV(thing);
636 }
637
638 RETVAL = thing_size(thing, tracking_hash);
639 /* Clean up after ourselves */
640 SvREFCNT_dec(tracking_hash);
641}
642OUTPUT:
643 RETVAL
644
645
646IV
647total_size(orig_thing)
648 SV *orig_thing
649CODE:
650{
651 SV *thing = orig_thing;
652 /* Hash to track our seen pointers */
653 HV *tracking_hash = newHV();
654 AV *pending_array = newAV();
b98fcdb9 655 IV size = 0;
ebb2c5b9 656 SV *warn_flag;
b98fcdb9 657
6a9ad7ec 658 /* Size starts at zero */
659 RETVAL = 0;
660
ebb2c5b9 661 /* Check warning status */
662 go_yell = 0;
98ecbbc6 663 regex_whine = 0;
664 fm_whine = 0;
ebb2c5b9 665
78dfb4e7 666 if (NULL != (warn_flag = perl_get_sv("Devel::Size::warn", FALSE))) {
ebb2c5b9 667 go_yell = SvIV(warn_flag);
668 }
669
670
6a9ad7ec 671 /* If they passed us a reference then dereference it. This is the
672 only way we can check the sizes of arrays and hashes */
673 if (SvOK(thing) && SvROK(thing)) {
674 thing = SvRV(thing);
675 }
676
677 /* Put it on the pending array */
678 av_push(pending_array, thing);
679
680 /* Now just yank things off the end of the array until it's done */
e96acca9 681 while (av_len(pending_array) >= 0) {
682 thing = av_pop(pending_array);
6a9ad7ec 683 /* Process it if we've not seen it */
684 if (check_new(tracking_hash, thing)) {
e96acca9 685 /* Is it valid? */
686 if (thing) {
c8db37d3 687 /* printf ("Found type %i at %p\n", SvTYPE(thing), thing); */
688
6a9ad7ec 689 /* Yes, it is. So let's check the type */
6a9ad7ec 690 switch (SvTYPE(thing)) {
691 case SVt_RV:
692 av_push(pending_array, SvRV(thing));
693 break;
694
b1e5ad85 695 /* fix for bug #24846 (Does not correctly recurse into references in a PVNV-type scalar) */
696 case SVt_PVNV:
697 if (SvROK(thing))
698 {
699 av_push(pending_array, SvRV(thing));
c8db37d3 700 }
b1e5ad85 701 break;
702
6a9ad7ec 703 case SVt_PVAV:
704 {
705 /* Quick alias to cut down on casting */
706 AV *tempAV = (AV *)thing;
707 SV **tempSV;
708
709 /* Any elements? */
710 if (av_len(tempAV) != -1) {
711 IV index;
712 /* Run through them all */
713 for (index = 0; index <= av_len(tempAV); index++) {
714 /* Did we get something? */
0430b7f7 715 if ((tempSV = av_fetch(tempAV, index, 0))) {
6a9ad7ec 716 /* Was it undef? */
717 if (*tempSV != &PL_sv_undef) {
718 /* Apparently not. Save it for later */
719 av_push(pending_array, *tempSV);
720 }
721 }
722 }
723 }
724 }
725 break;
726
727 case SVt_PVHV:
e96acca9 728 /* Is there anything in here? */
729 if (hv_iterinit((HV *)thing)) {
5c2e1b12 730 HE *temp_he;
0430b7f7 731 while ((temp_he = hv_iternext((HV *)thing))) {
5c2e1b12 732 av_push(pending_array, hv_iterval((HV *)thing, temp_he));
e96acca9 733 }
734 }
6a9ad7ec 735 break;
736
5c2e1b12 737 case SVt_PVGV:
0bff12d8 738 /* Run through all the pieces and push the ones with bits */
739 if (GvSV(thing)) {
740 av_push(pending_array, (SV *)GvSV(thing));
741 }
742 if (GvFORM(thing)) {
743 av_push(pending_array, (SV *)GvFORM(thing));
744 }
745 if (GvAV(thing)) {
746 av_push(pending_array, (SV *)GvAV(thing));
747 }
748 if (GvHV(thing)) {
749 av_push(pending_array, (SV *)GvHV(thing));
750 }
751 if (GvCV(thing)) {
752 av_push(pending_array, (SV *)GvCV(thing));
753 }
754 break;
6a9ad7ec 755 default:
e96acca9 756 break;
6a9ad7ec 757 }
758 }
759
b98fcdb9 760
761 size = thing_size(thing, tracking_hash);
762 RETVAL += size;
6a9ad7ec 763 }
764 }
765
766 /* Clean up after ourselves */
767 SvREFCNT_dec(tracking_hash);
768 SvREFCNT_dec(pending_array);
e98cedbf 769}
770OUTPUT:
771 RETVAL
6a9ad7ec 772