[AD] bug with Allegro packfile compression |
[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]
The bug was introduced here: http://www.allegro.cc/forums/thread/587170 I think I have a fix. I would appreciate help looking it over (see the comments for detailed explanation of the problem) and stress testing it, since Evert is planning to release 4.2.1 this weekend. In addition to the test case in the thread, I've attached a shell script which produces files using /dev/zero and /dev/urandom, compresses and decompresses them with the `pack' tool and checks they are the same. I've tried to minimise the number of changes and maintaining PACKFILE semantics, in particular that pack_feof() returns TRUE immediately after the last byte has been read (unlike feof() which returns true once you try to read _past_ the last byte). Peter
--- include/allegro/internal/aintern.h (revision 7523) +++ include/allegro/internal/aintern.h (local) @@ -136,6 +136,8 @@ AL_VAR(int, _packfile_datasize); AL_VAR(int, _packfile_type); AL_FUNC(PACKFILE *, _pack_fdopen, (int fd, AL_CONST char *mode)); +AL_FUNC(int, _al_lzss_incomplete_state, (AL_CONST LZSS_UNPACK_DATA *dat)); + /* config stuff */ void _reload_config(void); --- src/file.c (revision 7523) +++ src/file.c (local) @@ -2688,6 +2688,28 @@ static int normal_fclose(void *_f) +/* normal_no_more_input: + * Return non-zero if the number of bytes remaining in the file (todo) is + * less than or equal to zero. + * + * However, there is a special case. If we are reading from a LZSS + * compressed file, the latest call to lzss_read() may have suspended while + * writing out a sequence of bytes due to the output buffer being too small. + * In that case the `todo' count would be decremented (possibly to zero), + * but the output isn't yet completely written out. + */ +static INLINE int normal_no_more_input(PACKFILE *f) +{ + /* see normal_refill_buffer() to see when lzss_read() is called */ + if (f->normal.parent && (f->normal.flags & PACKFILE_FLAG_PACK) && + _al_lzss_incomplete_state(f->normal.unpack_data)) + return 0; + + return (f->normal.todo <= 0); +} + + + static int normal_getc(void *_f) { PACKFILE *f = _f; @@ -2697,7 +2719,7 @@ static int normal_getc(void *_f) return *(f->normal.buf_pos++); if (f->normal.buf_size == 0) { - if (f->normal.todo <= 0) + if (normal_no_more_input(f)) f->normal.flags |= PACKFILE_FLAG_EOF; return *(f->normal.buf_pos++); } @@ -2790,7 +2812,7 @@ static int normal_fseek(void *_f, int of f->normal.buf_size -= i; f->normal.buf_pos += i; offset -= i; - if ((f->normal.buf_size <= 0) && (f->normal.todo <= 0)) + if ((f->normal.buf_size <= 0) && normal_no_more_input(f)) f->normal.flags |= PACKFILE_FLAG_EOF; } @@ -2815,7 +2837,7 @@ static int normal_fseek(void *_f, int of lseek(f->normal.hndl, i, SEEK_CUR); } f->normal.todo -= i; - if (f->normal.todo <= 0) + if (normal_no_more_input(f)) f->normal.flags |= PACKFILE_FLAG_EOF; } } @@ -2857,7 +2879,7 @@ static int normal_refill_buffer(PACKFILE if (f->normal.flags & PACKFILE_FLAG_EOF) return EOF; - if (f->normal.todo <= 0) { + if (normal_no_more_input(f)) { f->normal.flags |= PACKFILE_FLAG_EOF; return EOF; } @@ -2908,7 +2930,7 @@ static int normal_refill_buffer(PACKFILE f->normal.buf_pos = f->normal.buf; f->normal.buf_size--; if (f->normal.buf_size <= 0) - if (f->normal.todo <= 0) + if (normal_no_more_input(f)) f->normal.flags |= PACKFILE_FLAG_EOF; if (f->normal.buf_size < 0) --- src/lzss.c (revision 7523) +++ src/lzss.c (local) @@ -547,3 +547,15 @@ int lzss_read(PACKFILE *file, LZSS_UNPAC return size; } + + + +/* _al_lzss_incomplete_state: + * Return non-zero if the previous lzss_read() call was in the middle of + * unpacking a sequence of bytes into the supplied buffer, but had to suspend + * because the buffer wasn't big enough. + */ +int _al_lzss_incomplete_state(AL_CONST LZSS_UNPACK_DATA *dat) +{ + return dat->state == 2; +}
Attachment:
stress.sh
Description: Bourne shell script
Mail converted by MHonArc 2.6.19+ | http://listengine.tuxfamily.org/ |