1 /* Filename: Bzip2.xs
2  * Author  : Paul Marquess, <pmqs@cpan.org>
3  * Created : 5th October 2005
4  * Version : 2.000
5  *
6  *   Copyright (c) 2005-2010 Paul Marquess. All rights reserved.
7  *   This program is free software; you can redistribute it and/or
8  *   modify it under the same terms as Perl itself.
9  *
10  */
11 
12 #define PERL_NO_GET_CONTEXT
13 #include "EXTERN.h"
14 #include "perl.h"
15 #include "XSUB.h"
16 
17 #include "bzlib.h"
18 
19 #ifdef USE_PPPORT_H
20 #  define NEED_sv_2pv_nolen
21 #  include "ppport.h"
22 #endif
23 
24 #if PERL_REVISION == 5 && (PERL_VERSION < 8 || (PERL_VERSION == 8 && PERL_SUBVERSION < 4 ))
25 
26 #    ifdef SvPVbyte_force
27 #        undef SvPVbyte_force
28 #    endif
29 
30 #    define SvPVbyte_force(sv,lp) SvPV_force(sv,lp)
31 
32 #endif
33 
34 #ifndef SvPVbyte_nolen
35 #    define SvPVbyte_nolen SvPV_nolen
36 #endif
37 
38 
39 #if PERL_REVISION == 5 && (PERL_VERSION >= 8 || (PERL_VERSION == 8 && PERL_SUBVERSION < 4 ))
40 #    define UTF8_AVAILABLE
41 #endif
42 
43 typedef int                     DualType ;
44 typedef int                     int_undef ;
45 
46 typedef unsigned long           uLong;
47 typedef unsigned int            uInt;
48 
49 typedef struct di_stream {
50     int      flags ;
51 #define FLAG_APPEND_OUTPUT      1
52 #define FLAG_CONSUME_INPUT      8
53 #define FLAG_LIMIT_OUTPUT       16
54     bz_stream stream;
55     uInt     bufsize;
56     int      last_error ;
57     uLong    bytesInflated ;
58     uLong    compressedBytes ;
59     uLong    uncompressedBytes ;
60 
61 } di_stream;
62 
63 typedef di_stream * deflateStream ;
64 typedef di_stream * Compress__Raw__Bzip2 ;
65 
66 typedef di_stream * inflateStream ;
67 typedef di_stream * Compress__Raw__Bunzip2 ;
68 
69 #define COMPRESS_CLASS    "Compress::Raw::Bzip2"
70 #define UNCOMPRESS_CLASS  "Compress::Raw::Bunzip2"
71 
72 #define ZMALLOC(to, typ) ((to = (typ *)safemalloc(sizeof(typ))), \
73                                 Zero(to,1,typ))
74 
75 
76 /* static const char * const my_z_errmsg[] = { */
77 static const char my_z_errmsg[][32] = {
78     "End of Stream",        /* BZ_STREAM_END        4       */
79     "Finish OK",            /* BZ_FINISH_OK         3       */
80     "Flush OK",             /* BZ_FLUSH_OK          2       */
81     "Run OK",               /* BZ_RUN_OK            1       */
82     "",                     /* BZ_OK                0       */
83     "Sequence Error",       /* BZ_SEQUENCE_ERROR    (-1)    */
84     "Param Error",          /* BZ_PARAM_ERROR       (-2)    */
85     "Memory Error",         /* BZ_MEM_ERROR         (-3)    */
86     "Data Error",           /* BZ_DATA_ERROR        (-4)    */
87     "Magic Error",          /* BZ_DATA_ERROR_MAGIC  (-5)    */
88     "IO Error",             /* BZ_IO_ERROR          (-6)    */
89     "Unexpected EOF",       /* BZ_UNEXPECTED_EOF    (-7)    */
90     "Output Buffer Full",   /* BZ_OUTBUFF_FULL      (-8)    */
91     "Config Error",         /* BZ_CONFIG_ERROR      (-9)    */
92     ""};
93 
94 #define setDUALstatus(var, err)                                         \
95                 sv_setnv(var, (double)err) ;                            \
96                 sv_setpv(var, ((err) ? GetErrorString(err) : "")) ;     \
97                 SvNOK_on(var);
98 
99 
100 #if defined(__SYMBIAN32__)
101 # define NO_WRITEABLE_DATA
102 #endif
103 
104 /* Set TRACE_DEFAULT to a non-zero value to enable tracing */
105 #define TRACE_DEFAULT 0
106 
107 #if defined(NO_WRITEABLE_DATA) || TRACE_DEFAULT == 0
108 #  define trace TRACE_DEFAULT
109 #else
110   static int trace = TRACE_DEFAULT ;
111 #endif
112 
113 /* Dodge PerlIO hiding of these functions. */
114 #undef printf
115 
116 #if 1
117 #define getInnerObject(x) (*av_fetch((AV*)SvRV(x), 0, FALSE))
118 #else
119 #define getInnerObject(x) ((SV*)SvRV(sv))
120 #endif
121 
122 #ifdef BZ_NO_STDIO
bz_internal_error(int errorcode)123 void bz_internal_error(int errorcode)
124 {
125     croak("bz_internal_error %d\n", errorcode);
126 }
127 #endif
128 
129 static char *
130 #ifdef CAN_PROTOTYPE
GetErrorString(int error_no)131 GetErrorString(int error_no)
132 #else
133 GetErrorString(error_no)
134 int error_no ;
135 #endif
136 {
137     return(char*) my_z_errmsg[4 - error_no];
138 }
139 
140 static void
141 #ifdef CAN_PROTOTYPE
DispHex(void * ptr,int length)142 DispHex(void * ptr, int length)
143 #else
144 DispHex(ptr, length)
145     void * ptr;
146     int length;
147 #endif
148 {
149     char * p = (char*)ptr;
150     int i;
151     for (i = 0; i < length; ++i) {
152         printf(" %02x", 0xFF & *(p+i));
153     }
154 }
155 
156 
157 static void
158 #ifdef CAN_PROTOTYPE
DispStream(di_stream * s,const char * message)159 DispStream(di_stream * s, const char * message)
160 #else
161 DispStream(s, message)
162     di_stream * s;
163     const char * message;
164 #endif
165 {
166 
167 #if 0
168     if (! trace)
169         return ;
170 #endif
171 
172 #define EnDis(f) (s->flags & f ? "Enabled" : "Disabled")
173 
174     printf("DispStream 0x%p", s) ;
175     if (message)
176         printf(" - %s \n", message) ;
177     printf("\n") ;
178 
179     if (!s)  {
180         printf("    stream pointer is NULL\n");
181     }
182     else     {
183         printf("    stream           0x%p\n", &(s->stream));
184         printf("           opaque    0x%p\n", s->stream.opaque);
185         printf("           state     0x%p\n", s->stream.state );
186         printf("           next_in   0x%p", s->stream.next_in);
187         if (s->stream.next_in){
188             printf(" =>");
189             DispHex(s->stream.next_in, 4);
190         }
191         printf("\n");
192 
193         printf("           next_out  0x%p", s->stream.next_out);
194         if (s->stream.next_out){
195             printf(" =>");
196             DispHex(s->stream.next_out, 4);
197         }
198         printf("\n");
199 
200         printf("           avail_in  %lu\n",  (unsigned long)s->stream.avail_in);
201         printf("           avail_out %lu\n",  (unsigned long)s->stream.avail_out);
202         printf("    bufsize          %lu\n",  (unsigned long)s->bufsize);
203         printf("      total_in_lo32  %u\n",  s->stream.total_in_lo32);
204         printf("      total_in_hi32  %u\n",  s->stream.total_in_hi32);
205         printf("      total_out_lo32 %u\n",  s->stream.total_out_lo32);
206         printf("      total_out_hi32 %u\n",  s->stream.total_out_hi32);
207         printf("    flags            0x%x\n", s->flags);
208         printf("           APPEND    %s\n",   EnDis(FLAG_APPEND_OUTPUT));
209         printf("           CONSUME   %s\n",   EnDis(FLAG_CONSUME_INPUT));
210         printf("           LIMIT     %s\n",   EnDis(FLAG_LIMIT_OUTPUT));
211 
212         printf("\n");
213 
214     }
215 }
216 
217 static di_stream *
218 #ifdef CAN_PROTOTYPE
InitStream(void)219 InitStream(void)
220 #else
221 InitStream()
222 #endif
223 {
224     di_stream *s ;
225 
226     ZMALLOC(s, di_stream) ;
227 
228     return s ;
229 
230 }
231 
232 static void
233 #ifdef CAN_PROTOTYPE
PostInitStream(di_stream * s,int flags)234 PostInitStream(di_stream * s, int flags)
235 #else
236 PostInitStream(s, flags)
237     di_stream *s ;
238     int flags ;
239 #endif
240 {
241     s->bufsize  = 1024 * 16 ;
242     s->last_error = 0 ;
243     s->flags    = flags ;
244 }
245 
246 
247 static SV*
248 #ifdef CAN_PROTOTYPE
deRef(SV * sv,const char * string)249 deRef(SV * sv, const char * string)
250 #else
251 deRef(sv, string)
252 SV * sv ;
253 char * string;
254 #endif
255 {
256     dTHX;
257     SvGETMAGIC(sv);
258 
259     if (SvROK(sv)) {
260         sv = SvRV(sv) ;
261         SvGETMAGIC(sv);
262         switch(SvTYPE(sv)) {
263             case SVt_PVAV:
264             case SVt_PVHV:
265             case SVt_PVCV:
266                 croak("%s: buffer parameter is not a SCALAR reference", string);
267             default:
268                 break;
269         }
270         if (SvROK(sv))
271             croak("%s: buffer parameter is a reference to a reference", string) ;
272     }
273 
274     if (!SvOK(sv))
275         sv = sv_2mortal(newSVpv("", 0));
276 
277     return sv ;
278 }
279 
280 static SV*
281 #ifdef CAN_PROTOTYPE
deRef_l(SV * sv,const char * string)282 deRef_l(SV * sv, const char * string)
283 #else
284 deRef_l(sv, string)
285 SV * sv ;
286 char * string ;
287 #endif
288 {
289     dTHX;
290     bool wipe = 0 ;
291     STRLEN na;
292 
293     SvGETMAGIC(sv);
294     wipe = ! SvOK(sv) ;
295 
296     if (SvROK(sv)) {
297         sv = SvRV(sv) ;
298         SvGETMAGIC(sv);
299         wipe = ! SvOK(sv) ;
300 
301         switch(SvTYPE(sv)) {
302             case SVt_PVAV:
303             case SVt_PVHV:
304             case SVt_PVCV:
305                 croak("%s: buffer parameter is not a SCALAR reference", string);
306             default:
307                 break;
308         }
309         if (SvROK(sv))
310             croak("%s: buffer parameter is a reference to a reference", string) ;
311     }
312 
313     if (SvREADONLY(sv) && PL_curcop != &PL_compiling)
314         croak("%s: buffer parameter is read-only", string);
315 
316     SvUPGRADE(sv, SVt_PV);
317 
318     if (wipe)
319         sv_setpv(sv, "") ;
320     else
321         (void)SvPVbyte_force(sv, na) ;
322 
323     return sv ;
324 }
325 
326 
327 #include "constants.h"
328 
329 MODULE = Compress::Raw::Bzip2 PACKAGE = Compress::Raw::Bzip2        PREFIX = Zip_
330 
331 REQUIRE:	1.924
332 PROTOTYPES:	DISABLE
333 
334 INCLUDE: constants.xs
335 
336 BOOT:
337     /* Check this version of bzip2 is == 1 */
338     if (BZ2_bzlibVersion()[0] != '1')
339 	croak(COMPRESS_CLASS " needs bzip2 version 1.x, you have %s\n", BZ2_bzlibVersion()) ;
340 
341 
342 MODULE = Compress::Raw::Bzip2 PACKAGE = Compress::Raw::Bzip2
343 
344 #define bzlibversion() BZ2_bzlibVersion()
345 const char *
bzlibversion()346 bzlibversion()
347 
348 void
349 new(className, appendOut=1, blockSize100k=1, workfactor=0, verbosity=0)
350     const char * className
351     int appendOut
352     int	blockSize100k
353     int workfactor
354     int verbosity
355   PPCODE:
356   {
357     int err ;
358     deflateStream s ;
359 #if 0
360     /* if (trace) */
361         warn("in Compress::Raw::Bzip2::_new(items=%d,appendOut=%d, blockSize100k=%d, workfactor=%d, verbosity=%d\n",
362 	items, appendOut, blockSize100k, workfactor, verbosity);
363 #endif
364     if ((s = InitStream() )) {
365 
366         err = BZ2_bzCompressInit ( &(s->stream),
367                                      blockSize100k,
368                                      verbosity,
369                                      workfactor );
370 
371         if (err != BZ_OK) {
372             Safefree(s) ;
373             s = NULL ;
374 	}
375 	else {
376             int flags = 0 ;
377             if (appendOut)
378                 flags |= FLAG_APPEND_OUTPUT;
379             PostInitStream(s, appendOut ? FLAG_APPEND_OUTPUT :0) ;
380         }
381     }
382     else
383         err = BZ_MEM_ERROR ;
384 
385     {
386         SV* obj = sv_setref_pv(sv_newmortal(), className, (void*)s);
387         XPUSHs(obj);
388     }
389     if(0)
390     {
391         SV* obj = sv_2mortal(newSViv(PTR2IV(s))) ;
392         XPUSHs(obj);
393     }
394     if (GIMME == G_ARRAY) {
395         SV * sv = sv_2mortal(newSViv(err)) ;
396 	setDUALstatus(sv, err);
397         XPUSHs(sv) ;
398     }
399   }
400 
401 MODULE = Compress::Raw::Bunzip2 PACKAGE = Compress::Raw::Bunzip2
402 
403 void
404 new(className, appendOut=1 , consume=1, small=0, verbosity=0, limitOutput=0)
405     const char* className
406     int appendOut
407     int consume
408     int small
409     int verbosity
410     int limitOutput
411   PPCODE:
412   {
413     int err = BZ_OK ;
414     inflateStream s ;
415 #if 0
416     if (trace)
417         warn("in _inflateInit(windowBits=%d, bufsize=%lu, dictionary=%lu\n",
418                 windowBits, bufsize, (unsigned long)SvCUR(dictionary)) ;
419 #endif
420     if ((s = InitStream() )) {
421 
422         err = BZ2_bzDecompressInit (&(s->stream), verbosity, small);
423         if (err != BZ_OK) {
424             Safefree(s) ;
425             s = NULL ;
426 	}
427 	if (s) {
428             int flags = 0;
429             if (appendOut)
430                 flags |= FLAG_APPEND_OUTPUT;
431             if (consume)
432                 flags |= FLAG_CONSUME_INPUT;
433             if (limitOutput)
434                 flags |= (FLAG_LIMIT_OUTPUT|FLAG_CONSUME_INPUT);
435 	    PostInitStream(s, flags) ;
436         }
437     }
438     else
439 	err = BZ_MEM_ERROR ;
440 
441     {
442         SV* obj = sv_setref_pv(sv_newmortal(), className, (void*)s);
443         XPUSHs(obj);
444     }
445        if (0)
446     {
447         SV* obj = sv_2mortal(newSViv(PTR2IV(s))) ;
448         XPUSHs(obj);
449     }
450     if (GIMME == G_ARRAY) {
451         SV * sv = sv_2mortal(newSViv(err)) ;
452 	setDUALstatus(sv, err);
453         XPUSHs(sv) ;
454     }
455   }
456 
457 
458 
459 MODULE = Compress::Raw::Bzip2 PACKAGE = Compress::Raw::Bzip2
460 
461 void
462 DispStream(s, message=NULL)
463     Compress::Raw::Bzip2   s
464     const char *  message
465 
466 DualType
467 bzdeflate (s, buf, output)
468     Compress::Raw::Bzip2	s
469     SV *	buf
470     SV * 	output
471     uInt	cur_length = NO_INIT
472     uInt	increment = NO_INIT
473     int		RETVAL = 0;
474     uInt   bufinc = NO_INIT
475     STRLEN   origlen = NO_INIT
476   CODE:
477     bufinc = s->bufsize;
478 
479     /* If the input buffer is a reference, dereference it */
480     buf = deRef(buf, "deflate") ;
481 
482     /* initialise the input buffer */
483 #ifdef UTF8_AVAILABLE
484     if (DO_UTF8(buf) && !sv_utf8_downgrade(buf, 1))
485          croak("Wide character in " COMPRESS_CLASS "::bzdeflate input parameter");
486 #endif
487     s->stream.next_in = (char*)SvPV_nomg(buf, origlen) ;
488     s->stream.avail_in = (unsigned int) origlen;
489 
490     /* and retrieve the output buffer */
491     output = deRef_l(output, "deflate") ;
492 #ifdef UTF8_AVAILABLE
493     if (DO_UTF8(output) && !sv_utf8_downgrade(output, 1))
494          croak("Wide character in " COMPRESS_CLASS "::bzdeflate output parameter");
495 #endif
496 
497      if((s->flags & FLAG_APPEND_OUTPUT) == FLAG_APPEND_OUTPUT) {
498          SvOOK_off(output);
499      } else {
500          SvCUR_set(output, 0);
501      }
502     cur_length =  SvCUR(output) ;
503     s->stream.next_out = (char*) SvPVX(output) + cur_length;
504     increment =  SvLEN(output) -  cur_length;
505     s->stream.avail_out =  increment;
506     while (s->stream.avail_in != 0) {
507 
508         if (s->stream.avail_out == 0) {
509 	    /* out of space in the output buffer so make it bigger */
510             s->stream.next_out = Sv_Grow(output, SvLEN(output) + bufinc) ;
511             cur_length += increment ;
512             s->stream.next_out += cur_length ;
513             increment = bufinc ;
514             s->stream.avail_out = increment;
515             bufinc *= 2 ;
516         }
517 
518         RETVAL = BZ2_bzCompress(&(s->stream), BZ_RUN);
519         if (RETVAL != BZ_RUN_OK)
520             break;
521     }
522 
523     s->compressedBytes    += cur_length + increment - s->stream.avail_out ;
524     s->uncompressedBytes  += origlen - s->stream.avail_in  ;
525 
526     s->last_error = RETVAL ;
527     if (RETVAL == BZ_RUN_OK) {
528         SvPOK_only(output);
529         SvCUR_set(output, cur_length + increment - s->stream.avail_out) ;
530         SvSETMAGIC(output);
531     }
532     OUTPUT:
533 	RETVAL
534 
535 
536 void
537 DESTROY(s)
538     Compress::Raw::Bzip2	s
539   CODE:
540     BZ2_bzCompressEnd(&s->stream) ;
541     Safefree(s) ;
542 
543 
544 DualType
545 bzclose(s, output)
546     Compress::Raw::Bzip2	s
547     SV * output
548     uInt	cur_length = NO_INIT
549     uInt	increment = NO_INIT
550     uInt    bufinc = NO_INIT
551   CODE:
552     bufinc = s->bufsize;
553 
554     s->stream.avail_in = 0; /* should be zero already anyway */
555 
556     /* retrieve the output buffer */
557     output = deRef_l(output, "close") ;
558 #ifdef UTF8_AVAILABLE
559     if (DO_UTF8(output) && !sv_utf8_downgrade(output, 1))
560          croak("Wide character in " COMPRESS_CLASS "::bzclose input parameter");
561 #endif
562      if((s->flags & FLAG_APPEND_OUTPUT) == FLAG_APPEND_OUTPUT) {
563          SvOOK_off(output);
564      } else {
565          SvCUR_set(output, 0);
566      }
567     cur_length =  SvCUR(output) ;
568     s->stream.next_out = (char*) SvPVX(output) + cur_length;
569     increment =  SvLEN(output) -  cur_length;
570     s->stream.avail_out =  increment;
571 
572     for (;;) {
573         if (s->stream.avail_out == 0) {
574 	    /* consumed all the available output, so extend it */
575             s->stream.next_out = Sv_Grow(output, SvLEN(output) + bufinc) ;
576             cur_length += increment ;
577             s->stream.next_out += cur_length ;
578             increment = bufinc ;
579             s->stream.avail_out = increment;
580             bufinc *= 2 ;
581         }
582         RETVAL = BZ2_bzCompress(&(s->stream), BZ_FINISH);
583 
584         /* deflate has finished flushing only when it hasn't used up
585          * all the available space in the output buffer:
586          */
587         /* if (s->stream.avail_out != 0 || RETVAL < 0 ) */
588         if (RETVAL == BZ_STREAM_END || RETVAL < 0 )
589             break;
590     }
591 
592     /* RETVAL =  (RETVAL == BZ_STREAM_END ? BZ_OK : RETVAL) ; */
593     s->last_error = RETVAL ;
594 
595     s->compressedBytes    += cur_length + increment - s->stream.avail_out ;
596 
597     if (RETVAL == BZ_STREAM_END) {
598         SvPOK_only(output);
599         SvCUR_set(output, cur_length + increment - s->stream.avail_out) ;
600         SvSETMAGIC(output);
601     }
602     OUTPUT:
603 	RETVAL
604 
605 
606 DualType
607 bzflush(s, output)
608     Compress::Raw::Bzip2	s
609     SV * output
610     uInt	cur_length = NO_INIT
611     uInt	increment = NO_INIT
612     uInt    bufinc = NO_INIT
613   CODE:
614     bufinc = s->bufsize;
615 
616     s->stream.avail_in = 0; /* should be zero already anyway */
617 
618     /* retrieve the output buffer */
619     output = deRef_l(output, "close") ;
620 #ifdef UTF8_AVAILABLE
621     if (DO_UTF8(output) && !sv_utf8_downgrade(output, 1))
622          croak("Wide character in " COMPRESS_CLASS "::bzflush input parameter");
623 #endif
624      if((s->flags & FLAG_APPEND_OUTPUT) == FLAG_APPEND_OUTPUT) {
625          SvOOK_off(output);
626      } else {
627          SvCUR_set(output, 0);
628      }
629     cur_length =  SvCUR(output) ;
630     s->stream.next_out = (char*) SvPVX(output) + cur_length;
631     increment =  SvLEN(output) -  cur_length;
632     s->stream.avail_out =  increment;
633 
634     for (;;) {
635         if (s->stream.avail_out == 0) {
636 	    /* consumed all the available output, so extend it */
637             s->stream.next_out = Sv_Grow(output, SvLEN(output) + bufinc) ;
638             cur_length += increment ;
639             s->stream.next_out += cur_length ;
640             increment = bufinc ;
641             s->stream.avail_out = increment;
642             bufinc *= 2 ;
643         }
644         RETVAL = BZ2_bzCompress(&(s->stream), BZ_FLUSH);
645 
646         if (RETVAL == BZ_RUN_OK || RETVAL < 0)
647                 break;
648 
649         /* deflate has finished flushing only when it hasn't used up
650          * all the available space in the output buffer:
651          */
652         /* RETVAL == if (s->stream.avail_out != 0 || RETVAL < 0 )
653             break; */
654     }
655 
656     /* RETVAL =  (RETVAL == BZ_STREAM_END ? BZ_OK : RETVAL) ; */
657     s->last_error = RETVAL ;
658 
659     s->compressedBytes    += cur_length + increment - s->stream.avail_out ;
660 
661     if (RETVAL == BZ_RUN_OK) {
662         SvPOK_only(output);
663         SvCUR_set(output, cur_length + increment - s->stream.avail_out) ;
664         SvSETMAGIC(output);
665     }
666     OUTPUT:
667 	RETVAL
668 
669 uLong
670 total_in_lo32(s)
671         Compress::Raw::Bzip2   s
672     CODE:
673         RETVAL = s->stream.total_in_lo32 ;
674     OUTPUT:
675 	RETVAL
676 
677 uLong
678 total_out_lo32(s)
679         Compress::Raw::Bzip2   s
680     CODE:
681         RETVAL = s->stream.total_out_lo32 ;
682     OUTPUT:
683 	RETVAL
684 
685 uLong
686 compressedBytes(s)
687         Compress::Raw::Bzip2   s
688     CODE:
689         RETVAL = s->compressedBytes;
690   OUTPUT:
691 	RETVAL
692 
693 uLong
694 uncompressedBytes(s)
695         Compress::Raw::Bzip2   s
696     CODE:
697         RETVAL = s->uncompressedBytes;
698   OUTPUT:
699 	RETVAL
700 
701 
702 MODULE = Compress::Raw::Bunzip2 PACKAGE = Compress::Raw::Bunzip2
703 
704 void
705 DispStream(s, message=NULL)
706     Compress::Raw::Bunzip2   s
707     const char *  message
708 
709 DualType
710 bzinflate (s, buf, output)
711     Compress::Raw::Bunzip2	s
712     SV *	buf
713     SV * 	output
714     uInt	cur_length = 0;
715     uInt	prefix_length = 0;
716     uInt	increment = 0;
717     uInt    bufinc = NO_INIT
718     STRLEN  na = NO_INIT ;
719     STRLEN    origlen = NO_INIT
720   PREINIT:
721 #ifdef UTF8_AVAILABLE
722     bool	out_utf8  = FALSE;
723 #endif
724   CODE:
725     bufinc = s->bufsize;
726     /* If the buffer is a reference, dereference it */
727     buf = deRef(buf, "bzinflate") ;
728 
729     if (s->flags & FLAG_CONSUME_INPUT) {
730         if (SvREADONLY(buf))
731             croak(UNCOMPRESS_CLASS "::bzinflate input parameter cannot be read-only when ConsumeInput is specified");
732         SvPV_force(buf, na);
733     }
734 #ifdef UTF8_AVAILABLE
735     if (DO_UTF8(buf) && !sv_utf8_downgrade(buf, 1))
736          croak("Wide character in " UNCOMPRESS_CLASS "::bzinflate input parameter");
737 #endif
738 
739     /* initialise the input buffer */
740     s->stream.next_in = (char*)SvPV_nomg(buf, origlen) ;
741     s->stream.avail_in = (unsigned int) origlen;
742 
743     /* and retrieve the output buffer */
744     output = deRef_l(output, "bzinflate") ;
745 #ifdef UTF8_AVAILABLE
746     if (DO_UTF8(output))
747          out_utf8 = TRUE ;
748     if (DO_UTF8(output) && !sv_utf8_downgrade(output, 1))
749          croak("Wide character in " UNCOMPRESS_CLASS "::bzinflate output parameter");
750 #endif
751      if((s->flags & FLAG_APPEND_OUTPUT) == FLAG_APPEND_OUTPUT) {
752          SvOOK_off(output);
753      } else {
754          SvCUR_set(output, 0);
755      }
756 
757     /* Assume no output buffer - the code below will update if there is any available */
758     s->stream.avail_out = 0;
759 
760     if (SvLEN(output)) {
761         prefix_length = cur_length =  SvCUR(output) ;
762 
763         if (s->flags & FLAG_LIMIT_OUTPUT && SvLEN(output) - cur_length - 1 < bufinc)
764         {
765             Sv_Grow(output, bufinc + cur_length + 1) ;
766         }
767 
768         /* Only setup the stream output pointers if there is spare
769            capacity in the outout SV
770         */
771         if (SvLEN(output) > cur_length + 1)
772         {
773             s->stream.next_out = (char*) SvPVX(output) + cur_length;
774             increment = SvLEN(output) -  cur_length - 1;
775             s->stream.avail_out = increment;
776         }
777     }
778 
779     s->bytesInflated = 0;
780 
781     RETVAL = BZ_OK;
782 
783     while (1) {
784 
785         if (s->stream.avail_out == 0) {
786 	    /* out of space in the output buffer so make it bigger */
787             s->stream.next_out = Sv_Grow(output, SvLEN(output) + bufinc + 1) ;
788             cur_length += increment ;
789             s->stream.next_out += cur_length ;
790             increment = bufinc ;
791             s->stream.avail_out = increment;
792             bufinc *= 2 ;
793         }
794 
795         /* DispStream(s, "pre"); */
796         RETVAL = BZ2_bzDecompress (&(s->stream));
797 
798         /*
799         printf("Status %d\n", RETVAL);
800         DispStream(s, "apres");
801         */
802         if (RETVAL != BZ_OK || s->flags & FLAG_LIMIT_OUTPUT)
803             break ;
804 
805         if (s->stream.avail_out == 0)
806             continue ;
807 
808         if (s->stream.avail_in == 0) {
809             RETVAL = BZ_OK ;
810             break ;
811         }
812 
813     }
814 
815     s->last_error = RETVAL ;
816     if (RETVAL == BZ_OK || RETVAL == BZ_STREAM_END) {
817 	unsigned in ;
818 
819         s->bytesInflated = cur_length + increment - s->stream.avail_out - prefix_length;
820         s->uncompressedBytes += s->bytesInflated ;
821         s->compressedBytes   += origlen - s->stream.avail_in  ;
822 
823         SvPOK_only(output);
824         SvCUR_set(output, prefix_length + s->bytesInflated) ;
825 	*SvEND(output) = '\0';
826 #ifdef UTF8_AVAILABLE
827         if (out_utf8)
828             sv_utf8_upgrade(output);
829 #endif
830         SvSETMAGIC(output);
831 
832 	/* fix the input buffer */
833 	if (s->flags & FLAG_CONSUME_INPUT) {
834 	    in = s->stream.avail_in ;
835 	    SvCUR_set(buf, in) ;
836 	    if (in)
837 	        Move(s->stream.next_in, SvPVX(buf), in, char) ;
838             *SvEND(buf) = '\0';
839             SvSETMAGIC(buf);
840 	}
841     }
842     OUTPUT:
843 	RETVAL
844 
845 uLong
846 inflateCount(s)
847     Compress::Raw::Bunzip2	s
848     CODE:
849         RETVAL = s->bytesInflated;
850   OUTPUT:
851 	RETVAL
852 
853 
854 void
855 DESTROY(s)
856     Compress::Raw::Bunzip2	s
857   CODE:
858     BZ2_bzDecompressEnd(&s->stream) ;
859     Safefree(s) ;
860 
861 
862 uLong
863 status(s)
864         Compress::Raw::Bunzip2   s
865     CODE:
866 	RETVAL = s->last_error ;
867     OUTPUT:
868 	RETVAL
869 
870 uLong
871 total_in_lo32(s)
872         Compress::Raw::Bunzip2   s
873     CODE:
874         RETVAL = s->stream.total_in_lo32 ;
875     OUTPUT:
876 	RETVAL
877 
878 uLong
879 total_out_lo32(s)
880         Compress::Raw::Bunzip2   s
881     CODE:
882         RETVAL = s->stream.total_out_lo32 ;
883     OUTPUT:
884 	RETVAL
885 
886 uLong
887 compressedBytes(s)
888         Compress::Raw::Bunzip2   s
889     CODE:
890         RETVAL = s->compressedBytes;
891   OUTPUT:
892 	RETVAL
893 
894 uLong
895 uncompressedBytes(s)
896         Compress::Raw::Bunzip2   s
897     CODE:
898         RETVAL = s->uncompressedBytes;
899   OUTPUT:
900 	RETVAL
901 
902 MODULE = Compress::Raw::Bzip2 PACKAGE = Compress::Raw::Bzip2        PREFIX = Zip_
903