/* p_lzo.c -- LZO packer This file is part of the LZO real-time data compression package. Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer The LZO library and packer is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Markus F.X.J. Oberhumer markus.oberhumer@jk.uni-linz.ac.at */ #include "lzopack.h" /************************************************************************* // **************************************************************************/ #if 1 /* crc32 is not used right now */ #define lzo_crc32(a,b,c) (0) #endif /************************************************************************* // **************************************************************************/ int lzop_set_method(int m, int level) { int l = level & 0xff; if (m != 0 || l < 1 || l > 9) return -1; /* not a LZO method */ #if defined(USE_LZO1X_1_15) if (l == 1) m = M_LZO1X_1_15; else #endif #if defined(USE_LZO1X_1) if (l <= 6) { m = M_LZO1X_1; l = 5; } #endif #if defined(USE_LZO1X_999) if (l >= 7 && l <= 9) { m = M_LZO1X_999; #if (LZO_VERSION < 0x1020) l = 9; #endif } #endif if (m == 0) return 1; /* error */ opt_method = m; opt_level = l; return 0; } int lzop_get_method(header_t *h) { /* check method */ if (h->method == M_LZO1X_1) { h->method_name = "LZO1X-1"; if (h->level == 0) h->level = 5; } else if (h->method == M_LZO1X_1_15) { h->method_name = "LZO1X-1(15)"; if (h->level == 0) h->level = 1; } else if (h->method == M_LZO1X_999) { static char s[11+1] = "LZO1X-999 "; s[9] = 0; if (h->level == 0) h->level = 9; else if (h->version >= 0x0950 && h->lib_version >= 0x1020) { s[9] = '/'; s[10] = (char) (h->level + '0'); } h->method_name = s; } else return -1; /* not a LZO method */ /* check compression level */ if (h->level < 1 || h->level > 9) return 15; return 0; } void lzop_init_compress_header(header_t *h) { if (opt_checksum >= 2) h->flags |= F_ADLER32_D | F_ADLER32_C; else if (opt_checksum) h->flags |= F_ADLER32_D; } /************************************************************************* // memory setup **************************************************************************/ #if defined(__LZO_DOS16) # define BLOCK_SIZE (128*1024l) #else # define BLOCK_SIZE (256*1024l) #endif #define MAX_BLOCK_SIZE (64*1024l*1024l) /* DO NOT CHANGE */ #if defined(USE_LZO1X_999) # define WRK_LEN LZO1X_999_MEM_COMPRESS #elif defined(USE_LZO1X_1_15) # define WRK_LEN LZO1X_1_15_MEM_COMPRESS #elif defined(USE_LZO1X_1) # define WRK_LEN LZO1X_1_MEM_COMPRESS #else # error #endif #if defined(USE_MALLOC) #define ALIGN_SIZE 4096 static lzo_byte *_block1 = NULL; static lzo_byte *_block2 = NULL; static lzo_byte *_wrkmem = NULL; #else #define ALIGN_SIZE 256 static lzo_byte _block1[BLOCK_SIZE + ALIGN_SIZE]; static lzo_byte _block2[BLOCK_SIZE + BLOCK_SIZE / 64 + 16 + 3 + ALIGN_SIZE]; static lzo_byte _wrkmem[WRK_LEN + ALIGN_SIZE]; #endif #define OVERLAP_SIZE ALIGN_UP(BLOCK_SIZE / 64, ALIGN_SIZE) /* align memory blocks (cache issues) */ static lzo_byte *block1 = NULL; static lzo_byte *block2 = NULL; static lzo_byte *wrkmem = NULL; #if defined(USE_MALLOC) static lzo_bool a_alloc(lzo_byte **m1, lzo_byte **m2, lzo_uint32 s1, lzo_uint32 s2) { if (s1 <= 0) return 1; *m2 = NULL; if (*m1 == NULL) { if (s2 >= SIZE_T_MAX - ALIGN_SIZE || s2 < s1) return 0; *m1 = (lzo_byte *) malloc((size_t) (s2 + ALIGN_SIZE)); if (*m1 == NULL) return 0; } *m2 = LZO_ALIGN(*m1,ALIGN_SIZE); return 1; } #endif static lzo_bool alloc_mem(lzo_uint32 s1, lzo_uint32 s2, lzo_uint32 w) { #if defined(USE_MALLOC) int r = 1; r &= a_alloc(&_block1, &block1, s1, s1); r &= a_alloc(&_block2, &block2, s2, s2+s2/64+16+3); r &= a_alloc(&_wrkmem, &wrkmem, w, w); if (!r) { FREE(_wrkmem); FREE(_block2); FREE(_block1); return 0; } #else block1 = LZO_ALIGN(_block1,ALIGN_SIZE); block2 = LZO_ALIGN(_block2,ALIGN_SIZE); wrkmem = LZO_ALIGN(_wrkmem,ALIGN_SIZE); UNUSED(s1); UNUSED(s2); UNUSED(w); #endif return 1; } static lzo_uint block_size = 0; lzo_bool lzop_enter(const header_t *h) { int r; lzo_uint32 wrk_len; #if !defined(NDEBUG) if (h != NULL) { if (h->flags & F_ADLER32_C) { assert(h->flags & F_ADLER32_D); } if (h->flags & F_CRC32_C) { assert(h->flags & F_CRC32_D); } } #endif if (h != NULL) return 1; if (opt_method == M_LZO1X_1) wrk_len = LZO1X_1_MEM_COMPRESS; else if (opt_method == M_LZO1X_1_15) wrk_len = LZO1X_1_15_MEM_COMPRESS; else if (opt_method == M_LZO1X_999) wrk_len = LZO1X_999_MEM_COMPRESS; else wrk_len = 0; if (h == NULL) { block_size = BLOCK_SIZE; #if 0 fprintf(stderr,"%lu %lu %u %lu\n", BLOCK_SIZE, MAX_BLOCK_SIZE, ALIGN_SIZE, OVERLAP_SIZE); #endif assert(block_size <= BLOCK_SIZE); assert(block_size <= MAX_BLOCK_SIZE); assert(block_size >= 16*1024); assert((ALIGN_SIZE & (ALIGN_SIZE - 1)) == 0); assert(OVERLAP_SIZE % ALIGN_SIZE == 0); if (opt_method == M_LZO1X_999) { if (opt_checksum < 1) opt_checksum = 1; /* always compute a checksum */ if (opt_cmd == CMD_COMPRESS) opt_optimize = 1; } } assert(wrk_len <= WRK_LEN); if (opt_cmd == CMD_COMPRESS) r = alloc_mem(block_size,block_size,wrk_len); else if (opt_cmd == CMD_DECOMPRESS || opt_cmd == CMD_TEST) r = alloc_mem(block_size+2*OVERLAP_SIZE,0,0); else if (opt_cmd == CMD_LIST || opt_cmd == CMD_INFO) r = alloc_mem(block_size,0,0); else r = alloc_mem(0,0,0); return r; } void lzop_leave(const header_t *h) { if (h == NULL) { #if defined(USE_MALLOC) FREE(_wrkmem); FREE(_block2); FREE(_block1); #endif } } /************************************************************************* // compress a file **************************************************************************/ lzo_bool lzop_compress(file_t *fip, file_t *fop, const header_t *h) { int r = LZO_E_OK; lzo_uint32 src_len, dst_len; lzo_uint32 c_adler32 = 0, d_adler32 = 0; lzo_uint32 c_crc32 = 0, d_crc32 = 0; lzo_int l; lzo_bool ok = 1; for (;;) { /* read a block */ l = read_buf(fip, block1, block_size); src_len = (l > 0 ? l : 0); /* write uncompressed block size */ write32(fop,src_len); /* exit if last block */ if (src_len == 0) break; /* compute checksum of uncompressed block */ if (h->flags & F_ADLER32_D) { d_adler32 = lzo_adler32(0,NULL,0); d_adler32 = lzo_adler32(d_adler32,block1,src_len); } if (h->flags & F_CRC32_D) { d_crc32 = lzo_crc32(0,NULL,0); d_crc32 = lzo_crc32(d_crc32,block1,src_len); } x_filter(block1,src_len,h); /* compress */ if (h->method == M_LZO1X_1) r = lzo1x_1_compress(block1,src_len,block2,&dst_len,wrkmem); #if defined(USE_LZO1X_1_15) else if (h->method == M_LZO1X_1_15) r = lzo1x_1_15_compress(block1,src_len,block2,&dst_len,wrkmem); #endif #if defined(USE_LZO1X_999) else if (h->method == M_LZO1X_999) #if (LZO_VERSION < 0x1020) r = lzo1x_999_compress(block1,src_len,block2,&dst_len,wrkmem); #else r = lzo1x_999_compress_level(block1,src_len,block2,&dst_len,wrkmem, NULL,0,0,h->level); #endif #endif else fatal(fip,"Internal error"); if (r != LZO_E_OK) fatal(fip,"Internal error - compression failed"); /* optimize */ if (opt_optimize && dst_len < src_len) { lzo_uint32 new_len = src_len; r = lzo1x_optimize(block2,dst_len,block1,&new_len,NULL); if (r != LZO_E_OK || new_len != src_len) fatal(fip,"Internal error - optimization failed"); } /* write compressed block size */ if (dst_len < src_len) write32(fop,dst_len); else write32(fop,src_len); /* write checksum of uncompressed block */ if (h->flags & F_ADLER32_D) write32(fop,d_adler32); if (h->flags & F_CRC32_D) write32(fop,d_crc32); /* write checksum of compressed block */ if (dst_len < src_len && (h->flags & F_ADLER32_C)) { c_adler32 = lzo_adler32(0,NULL,0); c_adler32 = lzo_adler32(c_adler32,block2,dst_len); write32(fop,c_adler32); } if (dst_len < src_len && (h->flags & F_CRC32_C)) { c_crc32 = lzo_crc32(0,NULL,0); c_crc32 = lzo_crc32(c_crc32,block2,dst_len); write32(fop,c_crc32); } /* write compressed block data */ if (dst_len < src_len) write_buf(fop,block2,dst_len); else write_buf(fop,block1,src_len); } return ok; } /************************************************************************* // decompress a file **************************************************************************/ lzo_bool lzop_decompress(file_t *fip, file_t *fop, const header_t *h, lzo_bool skip) { int r; lzo_uint src_len, dst_len; lzo_uint32 c_adler32 = 0, d_adler32 = 0; lzo_uint32 c_crc32 = 0, d_crc32 = 0; lzo_bool ok = 1; lzo_bool use_seek; #if defined(USE_MALLOC) lzo_byte * b1; lzo_byte * const b2 = block1; #else lzo_byte * const b1 = block1; lzo_byte * const b2 = block2; #endif use_seek = skip || opt_cmd == CMD_LIST || opt_cmd == CMD_INFO; for (;;) { lzo_byte *dst; /* read uncompressed block size */ read32(fip,&dst_len); /* exit if last block */ if (dst_len == 0) break; /* error if split file (not implemented) */ if (dst_len == 0xffffffffL) { error(fip,"this file is a split lzop file"); ok = 0; break; } if (dst_len > MAX_BLOCK_SIZE) { error(fip,"lzop file corrupted"); ok = 0; break; } /* read compressed block size */ read32(fip,&src_len); if (src_len <= 0 || src_len > dst_len) { error(fip,"lzop file corrupted"); ok = 0; break; } if (dst_len > block_size) { fatal(fip,"block size too small -- recompile lzop"); ok = 0; break; } /* read checksum of uncompressed block */ if (h->flags & F_ADLER32_D) read32(fip,&d_adler32); if (h->flags & F_CRC32_D) read32(fip,&d_crc32); /* read checksum of compressed block */ if (h->flags & F_ADLER32_C) { if (src_len < dst_len) read32(fip,&c_adler32); else { assert(h->flags & F_ADLER32_D); c_adler32 = d_adler32; } } if (h->flags & F_CRC32_C) { if (src_len < dst_len) read32(fip,&c_crc32); else { assert(h->flags & F_CRC32_D); c_crc32 = d_crc32; } } #if defined(USE_MALLOC) b1 = block1; if (opt_cmd == CMD_DECOMPRESS || opt_cmd == CMD_TEST) { b1 += ALIGN_DOWN(block_size + 2*OVERLAP_SIZE - src_len, ALIGN_SIZE); assert(b1 >= block1 + OVERLAP_SIZE); assert(b1 + src_len <= block1 + block_size + 2 * OVERLAP_SIZE); } #endif /* read the block */ if (use_seek && fip->fd != STDIN_FILENO) { if (lseek(fip->fd, src_len, SEEK_CUR) == -1) read_error(fip); } else { if (read_buf(fip, b1, src_len) != (lzo_int) src_len) read_error(fip); } fip->bytes_processed += src_len; if (use_seek) { fop->bytes_processed += dst_len; continue; } /* verify checksum of compressed block */ if (opt_checksum && (h->flags & F_ADLER32_C)) { lzo_uint32 c; c = lzo_adler32(0,NULL,0); c = lzo_adler32(c,b1,src_len); if (c != c_adler32) { error(fip,"Checksum error (lzop file corrupted)"); ok = 0; break; } } if (opt_checksum && (h->flags & F_CRC32_C)) { lzo_uint32 c; c = lzo_crc32(0,NULL,0); c = lzo_crc32(c,b1,src_len); if (c != c_crc32) { error(fip,"Checksum error (lzop file corrupted)"); ok = 0; break; } } if (src_len < dst_len) { lzo_uint32 d = dst_len; /* decompress */ if (opt_decompress_safe) r = lzo1x_decompress_safe(b1,src_len,b2,&d,NULL); else r = lzo1x_decompress(b1,src_len,b2,&d,NULL); if (r != LZO_E_OK || dst_len != d) { error(fip,"Compressed data violation"); #if 0 fprintf(stderr,"%d %ld %ld\n", r, (long)dst_len, (long)d); #endif ok = 0; break; } dst = b2; } else { assert(dst_len == src_len); dst = b1; } x_filter(dst,dst_len,h); /* verify checksum of uncompressed block */ if (opt_checksum && (h->flags & F_ADLER32_D)) { lzo_uint32 c; c = lzo_adler32(0,NULL,0); c = lzo_adler32(c,dst,dst_len); if (c != d_adler32) { error(fip,"Checksum error"); ok = 0; break; } } if (opt_checksum && (h->flags & F_CRC32_D)) { lzo_uint32 c; c = lzo_crc32(0,NULL,0); c = lzo_crc32(c,dst,dst_len); if (c != d_crc32) { error(fip,"Checksum error"); ok = 0; break; } } /* write uncompressed block data */ write_buf(fop,dst,dst_len); fop->bytes_processed += dst_len; } return ok; } /* vi:ts=4 */