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