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