Back to index

avfs  1.0.1
bzread.c
Go to the documentation of this file.
00001 /*
00002     AVFS: A Virtual File System Library
00003     Copyright (C) 2000-2001  Miklos Szeredi <miklos@szeredi.hu>
00004 
00005     This program can be distributed under the terms of the GNU GPL.
00006     See the file COPYING.
00007 */
00008 
00009 #include "config.h"
00010 #include "bzfile.h"
00011 #include "bzlib.h"
00012 #include "oper.h"
00013 #include "exit.h"
00014 
00015 #include <stdlib.h>
00016 #include <fcntl.h>
00017 
00018 
00019 #define INBUFSIZE 16384
00020 #define OUTBUFSIZE 32768
00021 
00022 struct bzstreamcache {
00023     int id;
00024     bz_stream *s;
00025 };
00026 
00027 /* FIXME: the streamcache should be cleaned up after a short timeout
00028    because it uses a LOT of memory */
00029 
00030 static struct bzstreamcache bzscache;
00031 static int bzread_nextid;
00032 static AV_LOCK_DECL(bzread_lock);
00033 
00034 struct bzindex {
00035     avoff_t offset;          /* The number of output bytes */
00036     avoff_t inbits;          /* The number of input bits */
00037     avuint crc;              /* The cumulative CRC up to this point */
00038     avbyte blocksize;        /* the blocksize in 100k (1 .. 9) */
00039     avbyte startbits;        /* the bits at the start of the block */
00040 };
00041 
00042 struct bzcache {
00043     int id;
00044     avoff_t size;
00045     unsigned int numindex;
00046     struct bzindex *indexes;
00047 };
00048 
00049 struct bzfile {
00050     bz_stream *s;
00051     int iseof;
00052     int iserror;
00053     int id; /* The id of the last used bzcache */
00054     
00055     vfile *infile;
00056     char inbuf[INBUFSIZE];
00057 };
00058 
00059 void bz_internal_error(int errorcode)
00060 {
00061     av_log(AVLOG_ERROR, "BZLIB: internal error %i", errorcode);
00062 }
00063 
00064 static avoff_t bz_total_in(bz_stream *s)
00065 {
00066     return (avoff_t) s->total_in_lo32 + ((avoff_t) s->total_in_hi32 << 32);
00067 }
00068 
00069 static avoff_t bz_total_out(bz_stream *s)
00070 {
00071     return (avoff_t) s->total_out_lo32 + ((avoff_t) s->total_out_hi32 << 32);
00072 }
00073 
00074 static void bz_delete_stream(bz_stream *s)
00075 {
00076     int res;
00077 
00078     if(s != NULL) {
00079         res = BZ2_bzDecompressEnd(s);
00080         if(res != BZ_OK)
00081             av_log(AVLOG_ERROR, "BZFILE: decompress end error: %i", res);
00082         
00083         av_free(s);
00084     }
00085 }
00086 
00087 static int bz_new_stream(bz_stream **resp)
00088 {
00089     int res;
00090     bz_stream *s;
00091 
00092     AV_NEW(s);
00093     memset(s, 0, sizeof(*s));
00094     res = BZ2_bzDecompressInit(s, 0, 0);
00095     if(res != BZ_OK) {
00096         *resp = NULL;
00097         av_log(AVLOG_ERROR, "BZFILE: decompress init error: %i", res);
00098         return -EIO;
00099     }
00100 
00101     *resp = s;
00102     return 0;
00103 }
00104 
00105 static void bzfile_scache_delete()
00106 {
00107     AV_LOCK(bzread_lock);
00108     if(bzscache.id != 0) {
00109         bz_delete_stream(bzscache.s);
00110         bzscache.id = 0;
00111     }
00112     AV_UNLOCK(bzread_lock);
00113 }
00114 
00115 static void bzfile_scache_save(int id, bz_stream *s)
00116 {
00117     static int regdestroy = 0;
00118     if(!regdestroy) {
00119         regdestroy = 1;
00120         av_add_exithandler(bzfile_scache_delete);
00121     }
00122 
00123     if(id == 0 || s == NULL) {
00124         bz_delete_stream(s);
00125         return;
00126     }
00127     
00128     if(bzscache.id != 0)
00129         bz_delete_stream(bzscache.s);
00130 
00131     bzscache.id = id;
00132     bzscache.s = s;
00133 }
00134 
00135 static int bzfile_reset(struct bzfile *fil)
00136 {
00137     /* FIXME: Is it a good idea to save the previous state or not? */
00138     if (fil->iseof || fil->iserror)
00139         bz_delete_stream(fil->s);
00140     else
00141         bzfile_scache_save(fil->id, fil->s);
00142 
00143     fil->iseof = 0;
00144     fil->iserror = 0;
00145     return bz_new_stream(&fil->s);
00146 }
00147 
00148 #ifndef USE_SYSTEM_BZLIB
00149 static int bzfile_seek_index(struct bzfile *fil, struct bzindex *zi)
00150 {
00151     int res;
00152     unsigned int bitsrem;
00153     avoff_t total_in;
00154     unsigned int val;
00155     
00156     /* FIXME: Is it a good idea to save the previous state or not? */
00157     bzfile_scache_save(fil->id, fil->s);
00158     res = bz_new_stream(&fil->s);
00159     if(res < 0)
00160         return res;
00161 
00162     total_in = (zi->inbits + 7) >> 3;
00163     bitsrem = (total_in << 3) - zi->inbits;
00164     total_in -= 4;
00165     
00166     fil->s->next_in = fil->inbuf;
00167     fil->s->avail_in = 4;
00168 
00169     fil->s->total_in_lo32 = total_in & 0xFFFFFFFF;
00170     fil->s->total_in_hi32 = (total_in >> 32) & 0xFFFFFFFF;
00171     fil->s->total_out_lo32 = zi->offset & 0xFFFFFFFF;
00172     fil->s->total_out_hi32 = (zi->offset >> 32) & 0xFFFFFFFF;
00173     
00174     val = ('B' << 24) + ('Z' << 16) + ('h' << 8) + (zi->blocksize + '0');
00175     val <<= bitsrem;
00176     val += zi->startbits;
00177 
00178     fil->inbuf[0] = (val >> 24) & 0xFF;
00179     fil->inbuf[1] = (val >> 16) & 0xFF;
00180     fil->inbuf[2] = (val >> 8) & 0xFF;
00181     fil->inbuf[3] = val & 0xFF;
00182 
00183     av_log(AVLOG_DEBUG, "BZFILE: restore: %lli %lli/%i %08x %i",
00184            bz_total_out(fil->s), bz_total_in(fil->s), bitsrem,
00185            zi->crc, zi->blocksize);
00186         
00187     BZ2_bzRestoreBlockEnd(fil->s, bitsrem, zi->crc);
00188 
00189     return 0;
00190 }
00191 
00192 static struct bzindex *bzcache_find_index(struct bzcache *zc, avoff_t offset)
00193 {
00194     unsigned int i;
00195 
00196     for(i = 0; i < zc->numindex; i++) {
00197         if(zc->indexes[i].offset > offset)
00198             break;
00199     }
00200     if(i == 0)
00201         return NULL;
00202     
00203     return &zc->indexes[i-1];
00204 }
00205 #endif
00206 
00207 static int bzfile_fill_inbuf(struct bzfile *fil)
00208 {
00209     avssize_t res;
00210     avoff_t inoff = bz_total_in(fil->s);
00211 
00212     res = av_pread(fil->infile, fil->inbuf, INBUFSIZE, inoff);
00213     if(res < 0)
00214         return res;
00215     
00216     fil->s->next_in = fil->inbuf;
00217     fil->s->avail_in = res;
00218 
00219     return 0;
00220 }
00221 
00222 #ifndef USE_SYSTEM_BZLIB
00223 static void bzfile_save_state(struct bzcache *zc, bz_stream *s,
00224                               unsigned int bitsrem, unsigned int bits,
00225                               unsigned int crc, unsigned int blocksize)
00226 {
00227     struct bzindex *zi;
00228     avoff_t offset = bz_total_out(s);
00229     int i;
00230     
00231     for(i = 0; i < zc->numindex; i++) {
00232         if(zc->indexes[i].offset >= offset)
00233             return;
00234     }
00235 
00236     zc->numindex ++;
00237     zc->indexes = (struct bzindex *)
00238         av_realloc(zc->indexes, sizeof(*zc->indexes) * zc->numindex);
00239     
00240     zi = &zc->indexes[i];
00241     zi->offset = offset;
00242     zi->inbits = (bz_total_in(s) << 3) - bitsrem;
00243     zi->startbits = bits & ((1 << bitsrem) - 1);
00244     zi->crc = crc;
00245     zi->blocksize = blocksize;
00246 
00247     av_log(AVLOG_DEBUG, "BZFILE: new block end: %lli %lli/%i %08x %i",
00248            zi->offset, bz_total_in(s), bitsrem, zi->crc, zi->blocksize);
00249 
00250 }
00251 
00252 static void bz_block_end(void *data, bz_stream *s, unsigned int bitsrem,
00253                          unsigned int bits, unsigned int crc,
00254                          unsigned int blocksize)
00255 {
00256     struct bzcache *zc = (struct bzcache *) data;
00257 
00258     AV_LOCK(bzread_lock);
00259     bzfile_save_state(zc, s, bitsrem, bits, crc, blocksize);
00260     AV_UNLOCK(bzread_lock);
00261 }
00262 #endif
00263 
00264 static int bzfile_decompress(struct bzfile *fil, struct bzcache *zc)
00265 {
00266     int res;
00267     unsigned char *start;
00268 
00269     if(fil->s->avail_in == 0) {
00270         res = bzfile_fill_inbuf(fil);
00271         if(res < 0)
00272             return res;
00273        if(fil->s->avail_in == 0) {
00274          /* still no byte available */
00275          av_log(AVLOG_ERROR, "BZFILE: decompress error");
00276          return -EIO;
00277        }
00278     }
00279     
00280     start = (unsigned char*)( fil->s->next_out );
00281 #ifndef USE_SYSTEM_BZLIB
00282     BZ2_bzSetBlockEndHandler(fil->s, bz_block_end, zc);
00283 #endif
00284     res = BZ2_bzDecompress(fil->s);
00285     if(res == BZ_STREAM_END) {
00286         fil->iseof = 1;
00287         AV_LOCK(bzread_lock);
00288         zc->size = bz_total_out(fil->s);
00289         AV_UNLOCK(bzread_lock);
00290         return 0;
00291     }
00292     if(res != BZ_OK) {
00293         av_log(AVLOG_ERROR, "BZFILE: decompress error: %i", res);
00294         return -EIO;
00295     }
00296     
00297     return 0;
00298 }
00299 
00300 
00301 static int bzfile_read(struct bzfile *fil, struct bzcache *zc, char *buf,
00302                       avsize_t nbyte)
00303 {
00304     int res;
00305 
00306     fil->s->next_out = buf;
00307     fil->s->avail_out = nbyte;
00308     while(fil->s->avail_out != 0 && !fil->iseof) {
00309         res = bzfile_decompress(fil, zc);
00310         if(res < 0)
00311             return res;
00312     }
00313 
00314     return nbyte - fil->s->avail_out;
00315 }
00316 
00317 static int bzfile_skip_to(struct bzfile *fil, struct bzcache *zc,
00318                           avoff_t offset)
00319 {
00320     int res;
00321     char outbuf[OUTBUFSIZE];
00322     
00323     while(!fil->iseof) {
00324         avoff_t curroff = bz_total_out(fil->s);
00325 
00326         if(curroff == offset)
00327             break;
00328 
00329         /* FIXME: Maybe cache some data as well */
00330         fil->s->next_out = outbuf;
00331         fil->s->avail_out = AV_MIN(OUTBUFSIZE, offset - curroff);
00332 
00333         res = bzfile_decompress(fil, zc);
00334         if(res < 0)
00335             return res;
00336     }
00337 
00338     return 0;
00339 }
00340 
00341 #ifndef USE_SYSTEM_BZLIB
00342 static int bzfile_seek(struct bzfile *fil, struct bzcache *zc, avoff_t offset)
00343 {
00344     struct bzindex *zi;
00345     avoff_t curroff = bz_total_out(fil->s);
00346     avoff_t zcdist;
00347     avoff_t scdist;
00348     avoff_t dist;
00349 
00350     if(offset >= curroff)
00351         dist = offset - curroff;
00352     else
00353         dist = -1;
00354 
00355     zi = bzcache_find_index(zc, offset);
00356     if(zi != NULL)
00357         zcdist = offset - zi->offset;
00358     else
00359         zcdist = offset;
00360 
00361     if(bzscache.id == zc->id) {
00362         avoff_t scacheoff = bz_total_out(bzscache.s);
00363 
00364         if(offset >= scacheoff) {
00365             scdist = offset - scacheoff;
00366             if((dist == -1 || scdist < dist) && scdist < zcdist) {
00367                 bz_stream *tmp = fil->s;
00368                 fil->s = bzscache.s;
00369                 fil->s->avail_in = 0;
00370                 bzscache.s = tmp;
00371                 return 0;
00372             }
00373         }
00374     }
00375 
00376     if(dist == -1 || zcdist < dist) {
00377         if(zi == NULL)
00378             return bzfile_reset(fil);
00379         else
00380             return bzfile_seek_index(fil, zi);
00381     }
00382 
00383     return 0;
00384 }
00385 #endif
00386 
00387 static avssize_t av_bzfile_do_pread(struct bzfile *fil, struct bzcache *zc,
00388                                    char *buf, avsize_t nbyte, avoff_t offset)
00389 {
00390     avssize_t res;
00391     avoff_t curroff;
00392 
00393     fil->id = zc->id;
00394 
00395     curroff = bz_total_out(fil->s);
00396     if(offset != curroff) {
00397         AV_LOCK(bzread_lock);
00398 #ifndef USE_SYSTEM_BZLIB
00399         res = bzfile_seek(fil, zc, offset);
00400 #else
00401         if ( curroff > offset ) {
00402             res = bzfile_reset( fil );
00403         } else {
00404             res = 0;
00405         }
00406 #endif
00407         AV_UNLOCK(bzread_lock);
00408         if(res < 0)
00409             return res;
00410 
00411         res = bzfile_skip_to(fil, zc, offset);
00412         if(res < 0)
00413             return res;
00414     }
00415 
00416     res = bzfile_read(fil, zc, buf, nbyte);
00417     
00418     return res;
00419 }
00420 
00421 avssize_t av_bzfile_pread(struct bzfile *fil, struct bzcache *zc, char *buf,
00422                          avsize_t nbyte, avoff_t offset)
00423 {
00424     avssize_t res;
00425 
00426     if(fil->iserror)
00427         return -EIO;
00428 
00429     res = av_bzfile_do_pread(fil, zc, buf, nbyte, offset);
00430     if(res < 0)
00431         fil->iserror = 1;
00432 
00433     return res;
00434 }
00435 
00436 int av_bzfile_size(struct bzfile *fil, struct bzcache *zc, avoff_t *sizep)
00437 {
00438     int res;
00439     avoff_t size;
00440 
00441     AV_LOCK(bzread_lock);
00442     size = zc->size;
00443     AV_UNLOCK(bzread_lock);
00444 
00445     if(size != -1 || fil == NULL) {
00446         *sizep = size;
00447         return 0;
00448     }
00449 
00450     fil->id = zc->id;
00451 
00452     AV_LOCK(bzread_lock);
00453 #ifndef USE_SYSTEM_BZLIB
00454     res = bzfile_seek(fil, zc, AV_MAXOFF);
00455 #else
00456     res = bzfile_reset( fil );
00457 #endif
00458     AV_UNLOCK(bzread_lock);
00459     if(res < 0)
00460         return res;
00461 
00462     res = bzfile_skip_to(fil, zc, AV_MAXOFF);
00463     if(res < 0)
00464         return res;
00465     
00466     AV_LOCK(bzread_lock);
00467     size = zc->size;
00468     AV_UNLOCK(bzread_lock);
00469     
00470     if(size == -1) {
00471         av_log(AVLOG_ERROR, "BZFILE: Internal error: could not find size");
00472         return -EIO;
00473     }
00474     
00475     *sizep = size;
00476     return 0;
00477 }
00478 
00479 static void bzfile_destroy(struct bzfile *fil)
00480 {
00481     AV_LOCK(bzread_lock);
00482     bzfile_scache_save(fil->id, fil->s);
00483     AV_UNLOCK(bzread_lock);
00484 }
00485 
00486 struct bzfile *av_bzfile_new(vfile *vf)
00487 {
00488     int res;
00489     struct bzfile *fil;
00490 
00491     AV_NEW_OBJ(fil, bzfile_destroy);
00492     fil->iseof = 0;
00493     fil->iserror = 0;
00494     fil->infile = vf;
00495     fil->id = 0;
00496 
00497     res = bz_new_stream(&fil->s);
00498     if(res < 0)
00499         fil->iserror = 1;
00500 
00501     return fil;
00502 }
00503 
00504 static void bzcache_destroy(struct bzcache *zc)
00505 {
00506     av_free(zc->indexes);
00507 }
00508 
00509 struct bzcache *av_bzcache_new()
00510 {
00511     struct bzcache *zc;
00512 
00513     AV_NEW_OBJ(zc, bzcache_destroy);
00514     zc->numindex = 0;
00515     zc->indexes = NULL;
00516     zc->size = -1;
00517 
00518     AV_LOCK(bzread_lock);
00519     if(bzread_nextid == 0)
00520         bzread_nextid = 1;
00521 
00522     zc->id = bzread_nextid ++;
00523     AV_UNLOCK(bzread_lock);
00524     
00525     return zc;
00526 }