Back to index

avfs  1.0.1
xzread.c
Go to the documentation of this file.
00001 /*
00002     AVFS: A Virtual File System Library
00003     Copyright (C) 2010  Ralf Hoffmann <ralf@boomerangsworld.de>
00004 
00005     This program can be distributed under the terms of the GNU GPL.
00006     See the file COPYING.
00007 
00008     based on bzread.c
00009 */
00010 
00011 #include "config.h"
00012 #include "xzfile.h"
00013 #include "lzma.h"
00014 #include "oper.h"
00015 #include "exit.h"
00016 
00017 #include <stdlib.h>
00018 #include <fcntl.h>
00019 
00020 
00021 #define INBUFSIZE 16384
00022 #define OUTBUFSIZE 32768
00023 #define INITIAL_MEMLIMIT (100<<20)
00024 
00025 struct xzstreamcache {
00026     int id;
00027     lzma_stream *s;
00028 };
00029 
00030 /* FIXME: the streamcache should be cleaned up after a short timeout
00031    because it uses a LOT of memory */
00032 
00033 static struct xzstreamcache xzscache;
00034 static int xzread_nextid;
00035 static AV_LOCK_DECL(xzread_lock);
00036 
00037 struct xzcache {
00038     int id;
00039     avoff_t size;
00040 };
00041 
00042 struct xzfile {
00043     lzma_stream *s;
00044     int iseof;
00045     int iserror;
00046     int id; /* The id of the last used xzcache */
00047     
00048     vfile *infile;
00049     char inbuf[INBUFSIZE];
00050 };
00051 
00052 void xz_internal_error(int errorcode)
00053 {
00054     av_log(AVLOG_ERROR, "XZ: internal error %i", errorcode);
00055 }
00056 
00057 static avoff_t xz_total_in(lzma_stream *s)
00058 {
00059     return (avoff_t) s->total_in;
00060 }
00061 
00062 static avoff_t xz_total_out(lzma_stream *s)
00063 {
00064     return (avoff_t) s->total_out;
00065 }
00066 
00067 static void xz_delete_stream(lzma_stream *s)
00068 {
00069     if(s != NULL) {
00070         lzma_end(s);
00071         
00072         av_free(s);
00073     }
00074 }
00075 
00076 static int xz_new_stream(lzma_stream **resp)
00077 {
00078     int res;
00079     lzma_stream *s;
00080     lzma_stream tmp = LZMA_STREAM_INIT;
00081 
00082     AV_NEW(s);
00083     *s = tmp;
00084 
00085     /* TODO: choose good memory limit */
00086     res = lzma_auto_decoder(s, INITIAL_MEMLIMIT, 0);
00087     if(res != LZMA_OK) {
00088         *resp = NULL;
00089         av_log(AVLOG_ERROR, "XZ: decompress init error: %i", res);
00090         return -EIO;
00091     }
00092 
00093     *resp = s;
00094     return 0;
00095 }
00096 
00097 static void xzfile_scache_delete()
00098 {
00099     AV_LOCK(xzread_lock);
00100     if(xzscache.id != 0) {
00101         xz_delete_stream(xzscache.s);
00102         xzscache.id = 0;
00103     }
00104     AV_UNLOCK(xzread_lock);
00105 }
00106 
00107 static void xzfile_scache_save(int id, lzma_stream *s)
00108 {
00109     static int regdestroy = 0;
00110     if(!regdestroy) {
00111         regdestroy = 1;
00112         av_add_exithandler(xzfile_scache_delete);
00113     }
00114 
00115     if(id == 0 || s == NULL) {
00116         xz_delete_stream(s);
00117         return;
00118     }
00119     
00120     if(xzscache.id != 0)
00121         xz_delete_stream(xzscache.s);
00122 
00123     xzscache.id = id;
00124     xzscache.s = s;
00125 }
00126 
00127 static int xzfile_reset(struct xzfile *fil)
00128 {
00129     /* FIXME: Is it a good idea to save the previous state or not? */
00130     if (fil->iseof || fil->iserror)
00131         xz_delete_stream(fil->s);
00132     else
00133         xzfile_scache_save(fil->id, fil->s);
00134 
00135     fil->iseof = 0;
00136     fil->iserror = 0;
00137     return xz_new_stream(&fil->s);
00138 }
00139 
00140 static int xzfile_fill_inbuf(struct xzfile *fil)
00141 {
00142     avssize_t res;
00143     avoff_t inoff = xz_total_in(fil->s);
00144 
00145     res = av_pread(fil->infile, fil->inbuf, INBUFSIZE, inoff);
00146     if(res < 0)
00147         return res;
00148     
00149     fil->s->next_in = (uint8_t*)fil->inbuf;
00150     fil->s->avail_in = res;
00151 
00152     return 0;
00153 }
00154 
00155 static int xzfile_decompress(struct xzfile *fil, struct xzcache *zc)
00156 {
00157     int res;
00158     unsigned char *start;
00159 
00160     if(fil->s->avail_in == 0) {
00161         res = xzfile_fill_inbuf(fil);
00162         if(res < 0)
00163             return res;
00164        if(fil->s->avail_in == 0) {
00165          /* still no byte available */
00166          av_log(AVLOG_ERROR, "XZ: decompress error");
00167          return -EIO;
00168        }
00169     }
00170     
00171     start = (unsigned char*)( fil->s->next_out );
00172 
00173     res = lzma_code(fil->s, LZMA_RUN);
00174     if(res == LZMA_STREAM_END) {
00175         fil->iseof = 1;
00176         AV_LOCK(xzread_lock);
00177         zc->size = xz_total_out(fil->s);
00178         AV_UNLOCK(xzread_lock);
00179         return 0;
00180     }
00181     /*TODO handle LZMA_MEMLIMIT_ERROR */
00182     if(res != LZMA_OK) {
00183         av_log(AVLOG_ERROR, "XZ: decompress error: %i", res);
00184         return -EIO;
00185     }
00186     /*TODO what if avail_in is not 0? */
00187     
00188     return 0;
00189 }
00190 
00191 
00192 static int xzfile_read(struct xzfile *fil, struct xzcache *zc, char *buf,
00193                       avsize_t nbyte)
00194 {
00195     int res;
00196 
00197     fil->s->next_out = (uint8_t*)buf;
00198     fil->s->avail_out = nbyte;
00199     while(fil->s->avail_out != 0 && !fil->iseof) {
00200         res = xzfile_decompress(fil, zc);
00201         if(res < 0)
00202             return res;
00203     }
00204 
00205     return nbyte - fil->s->avail_out;
00206 }
00207 
00208 static int xzfile_skip_to(struct xzfile *fil, struct xzcache *zc,
00209                           avoff_t offset)
00210 {
00211     int res;
00212     uint8_t outbuf[OUTBUFSIZE];
00213     
00214     while(!fil->iseof) {
00215         avoff_t curroff = xz_total_out(fil->s);
00216 
00217         if(curroff == offset)
00218             break;
00219 
00220         /* FIXME: Maybe cache some data as well */
00221         fil->s->next_out = outbuf;
00222         fil->s->avail_out = AV_MIN(OUTBUFSIZE, offset - curroff);
00223 
00224         res = xzfile_decompress(fil, zc);
00225         if(res < 0)
00226             return res;
00227     }
00228 
00229     return 0;
00230 }
00231 
00232 static avssize_t av_xzfile_do_pread(struct xzfile *fil, struct xzcache *zc,
00233                                    char *buf, avsize_t nbyte, avoff_t offset)
00234 {
00235     avssize_t res;
00236     avoff_t curroff;
00237 
00238     fil->id = zc->id;
00239 
00240     curroff = xz_total_out(fil->s);
00241     if(offset != curroff) {
00242         AV_LOCK(xzread_lock);
00243         if ( curroff > offset ) {
00244             res = xzfile_reset( fil );
00245         } else {
00246             res = 0;
00247         }
00248         AV_UNLOCK(xzread_lock);
00249         if(res < 0)
00250             return res;
00251 
00252         res = xzfile_skip_to(fil, zc, offset);
00253         if(res < 0)
00254             return res;
00255     }
00256 
00257     res = xzfile_read(fil, zc, buf, nbyte);
00258     
00259     return res;
00260 }
00261 
00262 avssize_t av_xzfile_pread(struct xzfile *fil, struct xzcache *zc, char *buf,
00263                          avsize_t nbyte, avoff_t offset)
00264 {
00265     avssize_t res;
00266 
00267     if(fil->iserror)
00268         return -EIO;
00269 
00270     res = av_xzfile_do_pread(fil, zc, buf, nbyte, offset);
00271     if(res < 0)
00272         fil->iserror = 1;
00273 
00274     return res;
00275 }
00276 
00277 int av_xzfile_size(struct xzfile *fil, struct xzcache *zc, avoff_t *sizep)
00278 {
00279     int res;
00280     avoff_t size;
00281 
00282     AV_LOCK(xzread_lock);
00283     size = zc->size;
00284     AV_UNLOCK(xzread_lock);
00285 
00286     if(size != -1 || fil == NULL) {
00287         *sizep = size;
00288         return 0;
00289     }
00290 
00291     fil->id = zc->id;
00292 
00293     AV_LOCK(xzread_lock);
00294     res = xzfile_reset( fil );
00295     AV_UNLOCK(xzread_lock);
00296     if(res < 0)
00297         return res;
00298 
00299     res = xzfile_skip_to(fil, zc, AV_MAXOFF);
00300     if(res < 0)
00301         return res;
00302     
00303     AV_LOCK(xzread_lock);
00304     size = zc->size;
00305     AV_UNLOCK(xzread_lock);
00306     
00307     if(size == -1) {
00308         av_log(AVLOG_ERROR, "XZ: Internal error: could not find size");
00309         return -EIO;
00310     }
00311     
00312     *sizep = size;
00313     return 0;
00314 }
00315 
00316 static void xzfile_destroy(struct xzfile *fil)
00317 {
00318     AV_LOCK(xzread_lock);
00319     xzfile_scache_save(fil->id, fil->s);
00320     AV_UNLOCK(xzread_lock);
00321 }
00322 
00323 struct xzfile *av_xzfile_new(vfile *vf)
00324 {
00325     int res;
00326     struct xzfile *fil;
00327 
00328     AV_NEW_OBJ(fil, xzfile_destroy);
00329     fil->iseof = 0;
00330     fil->iserror = 0;
00331     fil->infile = vf;
00332     fil->id = 0;
00333 
00334     res = xz_new_stream(&fil->s);
00335     if(res < 0)
00336         fil->iserror = 1;
00337 
00338     return fil;
00339 }
00340 
00341 static void xzcache_destroy(struct xzcache *zc)
00342 {
00343 }
00344 
00345 struct xzcache *av_xzcache_new()
00346 {
00347     struct xzcache *zc;
00348 
00349     AV_NEW_OBJ(zc, xzcache_destroy);
00350     zc->size = -1;
00351 
00352     AV_LOCK(xzread_lock);
00353     if(xzread_nextid == 0)
00354         xzread_nextid = 1;
00355 
00356     zc->id = xzread_nextid ++;
00357     AV_UNLOCK(xzread_lock);
00358     
00359     return zc;
00360 }