lzlib 0.1.0.0 → 0.1.1.0
raw patch · 10 files changed
+1016/−120 lines, 10 filesdep +bytestringdep +directorydep +filepathdep ~base
Dependencies added: bytestring, directory, filepath, filepattern, hspec, lzlib
Dependency ranges changed: base
Files
- CHANGELOG.md +6/−0
- README.md +1/−1
- cbits/lzlib.c +601/−0
- cbits/lzlib.h +106/−0
- lzlib.cabal +36/−4
- src/Codec/Lzip.chs +0/−114
- src/Codec/Lzip.hs +120/−0
- src/Codec/Lzip/Raw.chs +120/−0
- stack.yaml +1/−1
- test/Spec.hs +25/−0
CHANGELOG.md view
@@ -1,5 +1,11 @@ # lzlib +## 0.1.1.0++ * Add a higher-level API+ * Add `Show` and `Eq` instances to `LZErrno` type+ * Bundle with `lzlib` version 1.11+ ## 0.1.0.0 Initial release
README.md view
@@ -1,3 +1,3 @@ # lzlib -Low-level bindings to [lzlib](https://www.nongnu.org/lzip/lzlib.html).+Haskell bindings to [lzlib](https://www.nongnu.org/lzip/lzlib.html).
+ cbits/lzlib.c view
@@ -0,0 +1,601 @@+/* Lzlib - Compression library for the lzip format+ Copyright (C) 2009-2019 Antonio Diaz Diaz.++ This library is free software. Redistribution and use in source and+ binary forms, with or without modification, are permitted provided+ that the following conditions are met:++ 1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ 2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++ This library 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.+*/++#include <stdbool.h>+#include <stdint.h>+#include <stdlib.h>+#include <string.h>++#include "lzlib.h"+#include "lzip.h"+#include "cbuffer.c"+#include "decoder.h"+#include "decoder.c"+#include "encoder_base.h"+#include "encoder_base.c"+#include "encoder.h"+#include "encoder.c"+#include "fast_encoder.h"+#include "fast_encoder.c"+++struct LZ_Encoder+ {+ unsigned long long partial_in_size;+ unsigned long long partial_out_size;+ struct LZ_encoder_base * lz_encoder_base; /* these 3 pointers make a */+ struct LZ_encoder * lz_encoder; /* polymorphic encoder */+ struct FLZ_encoder * flz_encoder;+ enum LZ_Errno lz_errno;+ bool fatal;+ };++static void LZ_Encoder_init( struct LZ_Encoder * const e )+ {+ e->partial_in_size = 0;+ e->partial_out_size = 0;+ e->lz_encoder_base = 0;+ e->lz_encoder = 0;+ e->flz_encoder = 0;+ e->lz_errno = LZ_ok;+ e->fatal = false;+ }+++struct LZ_Decoder+ {+ unsigned long long partial_in_size;+ unsigned long long partial_out_size;+ struct Range_decoder * rdec;+ struct LZ_decoder * lz_decoder;+ enum LZ_Errno lz_errno;+ Lzip_header member_header; /* header of current member */+ bool fatal;+ bool first_header; /* true until first header is read */+ bool seeking;+ };++static void LZ_Decoder_init( struct LZ_Decoder * const d )+ {+ int i;+ d->partial_in_size = 0;+ d->partial_out_size = 0;+ d->rdec = 0;+ d->lz_decoder = 0;+ d->lz_errno = LZ_ok;+ for( i = 0; i < Lh_size; ++i ) d->member_header[i] = 0;+ d->fatal = false;+ d->first_header = true;+ d->seeking = false;+ }+++static bool verify_encoder( struct LZ_Encoder * const e )+ {+ if( !e ) return false;+ if( !e->lz_encoder_base || ( !e->lz_encoder && !e->flz_encoder ) ||+ ( e->lz_encoder && e->flz_encoder ) )+ { e->lz_errno = LZ_bad_argument; return false; }+ return true;+ }+++static bool verify_decoder( struct LZ_Decoder * const d )+ {+ if( !d ) return false;+ if( !d->rdec )+ { d->lz_errno = LZ_bad_argument; return false; }+ return true;+ }+++/*------------------------- Misc Functions -------------------------*/++const char * LZ_version( void ) { return LZ_version_string; }+++const char * LZ_strerror( const enum LZ_Errno lz_errno )+ {+ switch( lz_errno )+ {+ case LZ_ok : return "ok";+ case LZ_bad_argument : return "Bad argument";+ case LZ_mem_error : return "Not enough memory";+ case LZ_sequence_error: return "Sequence error";+ case LZ_header_error : return "Header error";+ case LZ_unexpected_eof: return "Unexpected EOF";+ case LZ_data_error : return "Data error";+ case LZ_library_error : return "Library error";+ }+ return "Invalid error code";+ }+++int LZ_min_dictionary_bits( void ) { return min_dictionary_bits; }+int LZ_min_dictionary_size( void ) { return min_dictionary_size; }+int LZ_max_dictionary_bits( void ) { return max_dictionary_bits; }+int LZ_max_dictionary_size( void ) { return max_dictionary_size; }+int LZ_min_match_len_limit( void ) { return min_match_len_limit; }+int LZ_max_match_len_limit( void ) { return max_match_len; }+++/*---------------------- Compression Functions ----------------------*/++struct LZ_Encoder * LZ_compress_open( const int dictionary_size,+ const int match_len_limit,+ const unsigned long long member_size )+ {+ Lzip_header header;+ struct LZ_Encoder * const e =+ (struct LZ_Encoder *)malloc( sizeof (struct LZ_Encoder) );+ if( !e ) return 0;+ LZ_Encoder_init( e );+ if( !Lh_set_dictionary_size( header, dictionary_size ) ||+ match_len_limit < min_match_len_limit ||+ match_len_limit > max_match_len ||+ member_size < min_dictionary_size )+ e->lz_errno = LZ_bad_argument;+ else+ {+ if( dictionary_size == 65535 && match_len_limit == 16 )+ {+ e->flz_encoder = (struct FLZ_encoder *)malloc( sizeof (struct FLZ_encoder) );+ if( e->flz_encoder && FLZe_init( e->flz_encoder, member_size ) )+ { e->lz_encoder_base = &e->flz_encoder->eb; return e; }+ free( e->flz_encoder ); e->flz_encoder = 0;+ }+ else+ {+ e->lz_encoder = (struct LZ_encoder *)malloc( sizeof (struct LZ_encoder) );+ if( e->lz_encoder && LZe_init( e->lz_encoder, Lh_get_dictionary_size( header ),+ match_len_limit, member_size ) )+ { e->lz_encoder_base = &e->lz_encoder->eb; return e; }+ free( e->lz_encoder ); e->lz_encoder = 0;+ }+ e->lz_errno = LZ_mem_error;+ }+ e->fatal = true;+ return e;+ }+++int LZ_compress_close( struct LZ_Encoder * const e )+ {+ if( !e ) return -1;+ if( e->lz_encoder_base )+ { LZeb_free( e->lz_encoder_base );+ free( e->lz_encoder ); free( e->flz_encoder ); }+ free( e );+ return 0;+ }+++int LZ_compress_finish( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) || e->fatal ) return -1;+ Mb_finish( &e->lz_encoder_base->mb );+ /* if (open --> write --> finish) use same dictionary size as lzip. */+ /* this does not save any memory. */+ if( Mb_data_position( &e->lz_encoder_base->mb ) == 0 &&+ Re_member_position( &e->lz_encoder_base->renc ) == Lh_size )+ {+ Mb_adjust_dictionary_size( &e->lz_encoder_base->mb );+ Lh_set_dictionary_size( e->lz_encoder_base->renc.header,+ e->lz_encoder_base->mb.dictionary_size );+ e->lz_encoder_base->renc.cb.buffer[5] = e->lz_encoder_base->renc.header[5];+ }+ return 0;+ }+++int LZ_compress_restart_member( struct LZ_Encoder * const e,+ const unsigned long long member_size )+ {+ if( !verify_encoder( e ) || e->fatal ) return -1;+ if( !LZeb_member_finished( e->lz_encoder_base ) )+ { e->lz_errno = LZ_sequence_error; return -1; }+ if( member_size < min_dictionary_size )+ { e->lz_errno = LZ_bad_argument; return -1; }++ e->partial_in_size += Mb_data_position( &e->lz_encoder_base->mb );+ e->partial_out_size += Re_member_position( &e->lz_encoder_base->renc );++ if( e->lz_encoder ) LZe_reset( e->lz_encoder, member_size );+ else FLZe_reset( e->flz_encoder, member_size );+ e->lz_errno = LZ_ok;+ return 0;+ }+++int LZ_compress_sync_flush( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) || e->fatal ) return -1;+ if( !Mb_flushing_or_end( &e->lz_encoder_base->mb ) )+ e->lz_encoder_base->mb.flushing = true;+ return 0;+ }+++int LZ_compress_read( struct LZ_Encoder * const e,+ uint8_t * const buffer, const int size )+ {+ int out_size = 0;+ if( !verify_encoder( e ) || e->fatal ) return -1;+ if( size < 0 ) return 0;+ do {+ if( ( e->flz_encoder && !FLZe_encode_member( e->flz_encoder ) ) ||+ ( e->lz_encoder && !LZe_encode_member( e->lz_encoder ) ) )+ { e->lz_errno = LZ_library_error; e->fatal = true; return -1; }+ if( e->lz_encoder_base->mb.flushing &&+ Mb_available_bytes( &e->lz_encoder_base->mb ) <= 0 &&+ LZeb_sync_flush( e->lz_encoder_base ) )+ e->lz_encoder_base->mb.flushing = false;+ out_size += Re_read_data( &e->lz_encoder_base->renc,+ buffer + out_size, size - out_size );+ }+ while( e->lz_encoder_base->mb.flushing && out_size < size &&+ Mb_enough_available_bytes( &e->lz_encoder_base->mb ) &&+ Re_enough_free_bytes( &e->lz_encoder_base->renc ) );+ return out_size;+ }+++int LZ_compress_write( struct LZ_Encoder * const e,+ const uint8_t * const buffer, const int size )+ {+ if( !verify_encoder( e ) || e->fatal ) return -1;+ return Mb_write_data( &e->lz_encoder_base->mb, buffer, size );+ }+++int LZ_compress_write_size( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) || e->fatal ) return -1;+ return Mb_free_bytes( &e->lz_encoder_base->mb );+ }+++enum LZ_Errno LZ_compress_errno( struct LZ_Encoder * const e )+ {+ if( !e ) return LZ_bad_argument;+ return e->lz_errno;+ }+++int LZ_compress_finished( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) ) return -1;+ return ( Mb_data_finished( &e->lz_encoder_base->mb ) &&+ LZeb_member_finished( e->lz_encoder_base ) );+ }+++int LZ_compress_member_finished( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) ) return -1;+ return LZeb_member_finished( e->lz_encoder_base );+ }+++unsigned long long LZ_compress_data_position( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) ) return 0;+ return Mb_data_position( &e->lz_encoder_base->mb );+ }+++unsigned long long LZ_compress_member_position( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) ) return 0;+ return Re_member_position( &e->lz_encoder_base->renc );+ }+++unsigned long long LZ_compress_total_in_size( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) ) return 0;+ return e->partial_in_size + Mb_data_position( &e->lz_encoder_base->mb );+ }+++unsigned long long LZ_compress_total_out_size( struct LZ_Encoder * const e )+ {+ if( !verify_encoder( e ) ) return 0;+ return e->partial_out_size + Re_member_position( &e->lz_encoder_base->renc );+ }+++/*--------------------- Decompression Functions ---------------------*/++struct LZ_Decoder * LZ_decompress_open( void )+ {+ struct LZ_Decoder * const d =+ (struct LZ_Decoder *)malloc( sizeof (struct LZ_Decoder) );+ if( !d ) return 0;+ LZ_Decoder_init( d );++ d->rdec = (struct Range_decoder *)malloc( sizeof (struct Range_decoder) );+ if( !d->rdec || !Rd_init( d->rdec ) )+ {+ if( d->rdec ) { Rd_free( d->rdec ); free( d->rdec ); d->rdec = 0; }+ d->lz_errno = LZ_mem_error; d->fatal = true;+ }+ return d;+ }+++int LZ_decompress_close( struct LZ_Decoder * const d )+ {+ if( !d ) return -1;+ if( d->lz_decoder )+ { LZd_free( d->lz_decoder ); free( d->lz_decoder ); }+ if( d->rdec ) { Rd_free( d->rdec ); free( d->rdec ); }+ free( d );+ return 0;+ }+++int LZ_decompress_finish( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) || d->fatal ) return -1;+ if( d->seeking )+ { d->seeking = false; d->partial_in_size += Rd_purge( d->rdec ); }+ else Rd_finish( d->rdec );+ return 0;+ }+++int LZ_decompress_reset( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) ) return -1;+ if( d->lz_decoder )+ { LZd_free( d->lz_decoder ); free( d->lz_decoder ); d->lz_decoder = 0; }+ d->partial_in_size = 0;+ d->partial_out_size = 0;+ Rd_reset( d->rdec );+ d->lz_errno = LZ_ok;+ d->fatal = false;+ d->first_header = true;+ d->seeking = false;+ return 0;+ }+++int LZ_decompress_sync_to_member( struct LZ_Decoder * const d )+ {+ unsigned skipped = 0;+ if( !verify_decoder( d ) ) return -1;+ if( d->lz_decoder )+ { LZd_free( d->lz_decoder ); free( d->lz_decoder ); d->lz_decoder = 0; }+ if( Rd_find_header( d->rdec, &skipped ) ) d->seeking = false;+ else+ {+ if( !d->rdec->at_stream_end ) d->seeking = true;+ else { d->seeking = false; d->partial_in_size += Rd_purge( d->rdec ); }+ }+ d->partial_in_size += skipped;+ d->lz_errno = LZ_ok;+ d->fatal = false;+ return 0;+ }+++int LZ_decompress_read( struct LZ_Decoder * const d,+ uint8_t * const buffer, const int size )+ {+ int result;+ if( !verify_decoder( d ) ) return -1;+ if( d->fatal ) /* don't return error until pending bytes are read */+ { if( d->lz_decoder && !Cb_empty( &d->lz_decoder->cb ) ) goto get_data;+ return -1; }+ if( d->seeking || size < 0 ) return 0;++ if( d->lz_decoder && LZd_member_finished( d->lz_decoder ) )+ {+ d->partial_out_size += LZd_data_position( d->lz_decoder );+ LZd_free( d->lz_decoder ); free( d->lz_decoder ); d->lz_decoder = 0;+ }+ if( !d->lz_decoder )+ {+ int rd;+ d->partial_in_size += d->rdec->member_position;+ d->rdec->member_position = 0;+ if( Rd_available_bytes( d->rdec ) < Lh_size + 5 &&+ !d->rdec->at_stream_end ) return 0;+ if( Rd_finished( d->rdec ) && !d->first_header ) return 0;+ rd = Rd_read_data( d->rdec, d->member_header, Lh_size );+ if( rd < Lh_size || Rd_finished( d->rdec ) ) /* End Of File */+ {+ if( rd <= 0 || Lh_verify_prefix( d->member_header, rd ) )+ d->lz_errno = LZ_unexpected_eof;+ else+ d->lz_errno = LZ_header_error;+ d->fatal = true;+ return -1;+ }+ if( !Lh_verify_magic( d->member_header ) )+ {+ /* unreading the header prevents sync_to_member from skipping a member+ if leading garbage is shorter than a full header; "lgLZIP\x01\x0C" */+ if( Rd_unread_data( d->rdec, rd ) )+ {+ if( d->first_header || !Lh_verify_corrupt( d->member_header ) )+ d->lz_errno = LZ_header_error;+ else+ d->lz_errno = LZ_data_error; /* corrupt header */+ }+ else+ d->lz_errno = LZ_library_error;+ d->fatal = true;+ return -1;+ }+ if( !Lh_verify_version( d->member_header ) ||+ !isvalid_ds( Lh_get_dictionary_size( d->member_header ) ) )+ {+ /* Skip a possible "LZIP" leading garbage; "LZIPLZIP\x01\x0C".+ Leave member_pos pointing to the first error. */+ if( Rd_unread_data( d->rdec, 1 + !Lh_verify_version( d->member_header ) ) )+ d->lz_errno = LZ_data_error; /* bad version or bad dict size */+ else+ d->lz_errno = LZ_library_error;+ d->fatal = true;+ return -1;+ }+ d->first_header = false;+ if( Rd_available_bytes( d->rdec ) < 5 )+ {+ /* set position at EOF */+ d->rdec->member_position += Cb_used_bytes( &d->rdec->cb );+ Cb_reset( &d->rdec->cb );+ d->lz_errno = LZ_unexpected_eof;+ d->fatal = true;+ return -1;+ }+ d->lz_decoder = (struct LZ_decoder *)malloc( sizeof (struct LZ_decoder) );+ if( !d->lz_decoder || !LZd_init( d->lz_decoder, d->rdec,+ Lh_get_dictionary_size( d->member_header ) ) )+ { /* not enough free memory */+ if( d->lz_decoder )+ { LZd_free( d->lz_decoder ); free( d->lz_decoder ); d->lz_decoder = 0; }+ d->lz_errno = LZ_mem_error;+ d->fatal = true;+ return -1;+ }+ d->rdec->reload_pending = true;+ }+ result = LZd_decode_member( d->lz_decoder );+ if( result != 0 )+ {+ if( result == 2 ) /* set input position at EOF */+ { d->rdec->member_position += Cb_used_bytes( &d->rdec->cb );+ Cb_reset( &d->rdec->cb );+ d->lz_errno = LZ_unexpected_eof; }+ else if( result == 5 ) d->lz_errno = LZ_library_error;+ else d->lz_errno = LZ_data_error;+ d->fatal = true;+ if( Cb_empty( &d->lz_decoder->cb ) ) return -1;+ }+get_data:+ return Cb_read_data( &d->lz_decoder->cb, buffer, size );+ }+++int LZ_decompress_write( struct LZ_Decoder * const d,+ const uint8_t * const buffer, const int size )+ {+ int result;+ if( !verify_decoder( d ) || d->fatal ) return -1;+ if( size < 0 ) return 0;++ result = Rd_write_data( d->rdec, buffer, size );+ while( d->seeking )+ {+ int size2;+ unsigned skipped = 0;+ if( Rd_find_header( d->rdec, &skipped ) ) d->seeking = false;+ d->partial_in_size += skipped;+ if( result >= size ) break;+ size2 = Rd_write_data( d->rdec, buffer + result, size - result );+ if( size2 > 0 ) result += size2;+ else break;+ }+ return result;+ }+++int LZ_decompress_write_size( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) || d->fatal ) return -1;+ return Rd_free_bytes( d->rdec );+ }+++enum LZ_Errno LZ_decompress_errno( struct LZ_Decoder * const d )+ {+ if( !d ) return LZ_bad_argument;+ return d->lz_errno;+ }+++int LZ_decompress_finished( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) || d->fatal ) return -1;+ return ( Rd_finished( d->rdec ) &&+ ( !d->lz_decoder || LZd_member_finished( d->lz_decoder ) ) );+ }+++int LZ_decompress_member_finished( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) || d->fatal ) return -1;+ return ( d->lz_decoder && LZd_member_finished( d->lz_decoder ) );+ }+++int LZ_decompress_member_version( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) ) return -1;+ return Lh_version( d->member_header );+ }+++int LZ_decompress_dictionary_size( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) ) return -1;+ return Lh_get_dictionary_size( d->member_header );+ }+++unsigned LZ_decompress_data_crc( struct LZ_Decoder * const d )+ {+ if( verify_decoder( d ) && d->lz_decoder )+ return LZd_crc( d->lz_decoder );+ return 0;+ }+++unsigned long long LZ_decompress_data_position( struct LZ_Decoder * const d )+ {+ if( verify_decoder( d ) && d->lz_decoder )+ return LZd_data_position( d->lz_decoder );+ return 0;+ }+++unsigned long long LZ_decompress_member_position( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) ) return 0;+ return d->rdec->member_position;+ }+++unsigned long long LZ_decompress_total_in_size( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) ) return 0;+ return d->partial_in_size + d->rdec->member_position;+ }+++unsigned long long LZ_decompress_total_out_size( struct LZ_Decoder * const d )+ {+ if( !verify_decoder( d ) ) return 0;+ if( d->lz_decoder )+ return d->partial_out_size + LZd_data_position( d->lz_decoder );+ return d->partial_out_size;+ }
+ cbits/lzlib.h view
@@ -0,0 +1,106 @@+/* Lzlib - Compression library for the lzip format+ Copyright (C) 2009-2019 Antonio Diaz Diaz.++ This library is free software. Redistribution and use in source and+ binary forms, with or without modification, are permitted provided+ that the following conditions are met:++ 1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ 2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++ This library 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.+*/++#ifdef __cplusplus+extern "C" {+#endif++#define LZ_API_VERSION 1++static const char * const LZ_version_string = "1.11";++enum LZ_Errno { LZ_ok = 0, LZ_bad_argument, LZ_mem_error,+ LZ_sequence_error, LZ_header_error, LZ_unexpected_eof,+ LZ_data_error, LZ_library_error };+++const char * LZ_version( void );+const char * LZ_strerror( const enum LZ_Errno lz_errno );++int LZ_min_dictionary_bits( void );+int LZ_min_dictionary_size( void );+int LZ_max_dictionary_bits( void );+int LZ_max_dictionary_size( void );+int LZ_min_match_len_limit( void );+int LZ_max_match_len_limit( void );+++/*---------------------- Compression Functions ----------------------*/++struct LZ_Encoder;++struct LZ_Encoder * LZ_compress_open( const int dictionary_size,+ const int match_len_limit,+ const unsigned long long member_size );+int LZ_compress_close( struct LZ_Encoder * const encoder );++int LZ_compress_finish( struct LZ_Encoder * const encoder );+int LZ_compress_restart_member( struct LZ_Encoder * const encoder,+ const unsigned long long member_size );+int LZ_compress_sync_flush( struct LZ_Encoder * const encoder );++int LZ_compress_read( struct LZ_Encoder * const encoder,+ uint8_t * const buffer, const int size );+int LZ_compress_write( struct LZ_Encoder * const encoder,+ const uint8_t * const buffer, const int size );+int LZ_compress_write_size( struct LZ_Encoder * const encoder );++enum LZ_Errno LZ_compress_errno( struct LZ_Encoder * const encoder );+int LZ_compress_finished( struct LZ_Encoder * const encoder );+int LZ_compress_member_finished( struct LZ_Encoder * const encoder );++unsigned long long LZ_compress_data_position( struct LZ_Encoder * const encoder );+unsigned long long LZ_compress_member_position( struct LZ_Encoder * const encoder );+unsigned long long LZ_compress_total_in_size( struct LZ_Encoder * const encoder );+unsigned long long LZ_compress_total_out_size( struct LZ_Encoder * const encoder );+++/*--------------------- Decompression Functions ---------------------*/++struct LZ_Decoder;++struct LZ_Decoder * LZ_decompress_open( void );+int LZ_decompress_close( struct LZ_Decoder * const decoder );++int LZ_decompress_finish( struct LZ_Decoder * const decoder );+int LZ_decompress_reset( struct LZ_Decoder * const decoder );+int LZ_decompress_sync_to_member( struct LZ_Decoder * const decoder );++int LZ_decompress_read( struct LZ_Decoder * const decoder,+ uint8_t * const buffer, const int size );+int LZ_decompress_write( struct LZ_Decoder * const decoder,+ const uint8_t * const buffer, const int size );+int LZ_decompress_write_size( struct LZ_Decoder * const decoder );++enum LZ_Errno LZ_decompress_errno( struct LZ_Decoder * const decoder );+int LZ_decompress_finished( struct LZ_Decoder * const decoder );+int LZ_decompress_member_finished( struct LZ_Decoder * const decoder );++int LZ_decompress_member_version( struct LZ_Decoder * const decoder );+int LZ_decompress_dictionary_size( struct LZ_Decoder * const decoder );+unsigned LZ_decompress_data_crc( struct LZ_Decoder * const decoder );++unsigned long long LZ_decompress_data_position( struct LZ_Decoder * const decoder );+unsigned long long LZ_decompress_member_position( struct LZ_Decoder * const decoder );+unsigned long long LZ_decompress_total_in_size( struct LZ_Decoder * const decoder );+unsigned long long LZ_decompress_total_out_size( struct LZ_Decoder * const decoder );++#ifdef __cplusplus+}+#endif
lzlib.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: lzlib-version: 0.1.0.0+version: 0.1.1.0 license: BSD3 license-file: LICENSE copyright: Copyright: (c) 2019 Vanessa McHale@@ -9,6 +9,7 @@ synopsis: lzlib bindings description: Lzlib bindings via [c2hs](http://hackage.haskell.org/package/c2hs).+ Includes a bundled copy of lzlib category: Codec, Compression build-type: Simple@@ -27,15 +28,46 @@ manual: True library- exposed-modules: Codec.Lzip+ exposed-modules:+ Codec.Lzip+ Codec.Lzip.Raw++ c-sources: cbits/lzlib.c hs-source-dirs: src default-language: Haskell2010- extra-libraries: lz+ other-extensions: BangPatterns+ include-dirs: cbits+ install-includes: cbits/lzlib.h ghc-options: -Wall- build-depends: base >=4.3 && <5+ build-depends:+ base >=4.9 && <5,+ bytestring -any if !flag(cross) build-tool-depends: c2hs:c2hs -any++ if impl(ghc >=8.0)+ ghc-options:+ -Wincomplete-uni-patterns -Wincomplete-record-updates+ -Wredundant-constraints++ if impl(ghc >=8.4)+ ghc-options: -Wmissing-export-lists++test-suite lzlib-test+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: test+ default-language: Haskell2010+ ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall+ build-depends:+ base -any,+ lzlib -any,+ hspec -any,+ bytestring -any,+ filepattern -any,+ filepath -any,+ directory -any if impl(ghc >=8.0) ghc-options:
− src/Codec/Lzip.chs
@@ -1,114 +0,0 @@-module Codec.Lzip ( -- * Prolegomena- LZErrno (..)- , lZVersion- , lZStrerror- , lZMinDictionaryBits- , lZMinDictionarySize- , lZMaxDictionaryBits- , lZMaxDictionarySize- , lZMinMatchLenLimit- , lZMaxMatchLenLimit- , UInt8- -- * Compression functions- , LZEncoder- , LZEncoderPtr- , lZCompressOpen- , lZCompressClose- , lZCompressFinish- , lZCompressRestartMember- , lZCompressSyncFlush- , lZCompressRead- , lZCompressWrite- , lZCompressWriteSize- , lZCompressErrno- , lZCompressFinished- , lZCompressMemberFinished- , lZCompressDataPosition- , lZCompressMemberPosition- , lZCompressTotalInSize- , lZCompressTotalOutSize- -- * Decompression functions- , LZDecoder- , LZDecoderPtr- , lZDecompressOpen- , lZDecompressClose- , lZDecompressFinish- , lZDecompressReset- , lZDecompressSyncToMember- , lZDecompressRead- , lZDecompressWrite- , lZDecompressWriteSize- , lZDecompressErrno- , lZDecompressFinished- , lZDecompressMemberFinished- , lZDecompressDictionarySize- , lZDecompressDataCrc- , lZDecompressDataPosition- , lZDecompressMemberPosition- , lZDecompressTotalInSize- , lZDecompressTotalOutSize- ) where--import Foreign.C.String-import Foreign.C.Types-import Foreign.Ptr (Ptr)--#include <stdint.h>-#include <lzlib.h>--type UInt8 = {# type uint8_t #}--{# enum LZ_Errno as LZErrno {underscoreToCase} #}--{# fun LZ_version as ^ {} -> `CString' #}-{# fun LZ_strerror as ^ { `LZErrno' } -> `CString' #}-{# fun LZ_min_dictionary_bits as ^ {} -> `CInt' #}-{# fun LZ_min_dictionary_size as ^ {} -> `CInt' #}-{# fun LZ_max_dictionary_bits as ^ {} -> `CInt' #}-{# fun LZ_max_dictionary_size as ^ {} -> `CInt' #}-{# fun LZ_min_match_len_limit as ^ {} -> `CInt' #}-{# fun LZ_max_match_len_limit as ^ {} -> `CInt' #}---- | Abstract data type-data LZEncoder--{# pointer *LZ_Encoder as LZEncoderPtr -> LZEncoder #}--{# fun LZ_compress_open as ^ { `CInt', `CInt', id `CULLong' } -> `LZEncoderPtr' #}-{# fun LZ_compress_close as ^ { `LZEncoderPtr' } -> `CInt' #}-{# fun LZ_compress_finish as ^ { `LZEncoderPtr' } -> `CInt' #}-{# fun LZ_compress_restart_member as ^ { `LZEncoderPtr', id `CULLong' } -> `CInt' #}-{# fun LZ_compress_sync_flush as ^ { `LZEncoderPtr' } -> `CInt' #}-{# fun LZ_compress_read as ^ { `LZEncoderPtr', id `Ptr UInt8', `CInt' } -> `CInt' #}-{# fun LZ_compress_write as ^ { `LZEncoderPtr', id `Ptr UInt8', `CInt' } -> `CInt' #}-{# fun LZ_compress_write_size as ^ { `LZEncoderPtr' } -> `CInt' #}-{# fun LZ_compress_errno as ^ { `LZEncoderPtr' } -> `LZErrno' #}-{# fun LZ_compress_finished as ^ { `LZEncoderPtr' } -> `CInt' #}-{# fun LZ_compress_member_finished as ^ { `LZEncoderPtr' } -> `CInt' #}-{# fun LZ_compress_data_position as ^ { `LZEncoderPtr' } -> `CULLong' id #}-{# fun LZ_compress_member_position as ^ { `LZEncoderPtr' } -> `CULLong' id #}-{# fun LZ_compress_total_in_size as ^ { `LZEncoderPtr' } -> `CULLong' id #}-{# fun LZ_compress_total_out_size as ^ { `LZEncoderPtr' } -> `CULLong' id #}---- | Abstract data type-data LZDecoder--{# pointer *LZ_Decoder as LZDecoderPtr -> LZDecoder #}--{# fun LZ_decompress_open as ^ {} -> `LZDecoderPtr' #}-{# fun LZ_decompress_close as ^ { `LZDecoderPtr' } -> `CInt' #}-{# fun LZ_decompress_finish as ^ { `LZDecoderPtr' } -> `CInt' #}-{# fun LZ_decompress_reset as ^ { `LZDecoderPtr' } -> `CInt' #}-{# fun LZ_decompress_sync_to_member as ^ { `LZDecoderPtr' } -> `CInt' #}-{# fun LZ_decompress_read as ^ { `LZDecoderPtr', id `Ptr UInt8', `CInt' } -> `CInt' #}-{# fun LZ_decompress_write as ^ { `LZDecoderPtr', id `Ptr UInt8', `CInt' } -> `CInt' #}-{# fun LZ_decompress_write_size as ^ { `LZDecoderPtr' } -> `CInt' #}-{# fun LZ_decompress_errno as ^ { `LZDecoderPtr' } -> `LZErrno' #}-{# fun LZ_decompress_finished as ^ { `LZDecoderPtr' } -> `CInt' #}-{# fun LZ_decompress_member_finished as ^ { `LZDecoderPtr' } -> `CInt' #}-{# fun LZ_decompress_dictionary_size as ^ { `LZDecoderPtr' } -> `CInt' #}-{# fun LZ_decompress_data_crc as ^ { `LZDecoderPtr' } -> `CUInt' #}-{# fun LZ_decompress_data_position as ^ { `LZDecoderPtr' } -> `CULLong' id #}-{# fun LZ_decompress_member_position as ^ { `LZDecoderPtr' } -> `CULLong' id #}-{# fun LZ_decompress_total_in_size as ^ { `LZDecoderPtr' } -> `CULLong' id #}-{# fun LZ_decompress_total_out_size as ^ { `LZDecoderPtr' } -> `CULLong' id #}
+ src/Codec/Lzip.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE BangPatterns #-}++module Codec.Lzip ( compressStrict+ , compressWithStrict+ , decompressStrict+ , CompressionLevel (..)+ -- * Low-level bindings+ , module Codec.Lzip.Raw+ ) where++import Codec.Lzip.Raw+import Control.Monad (unless, void, when)+import Data.Bits (shiftL)+import qualified Data.ByteString as BS+import Data.Functor (($>))+import Data.Int (Int64)+import Data.Maybe (fromMaybe)+import Data.Semigroup+import Foreign.Marshal.Alloc (free, mallocBytes)+import Foreign.Ptr (Ptr, castPtr, plusPtr)+import System.IO.Unsafe (unsafePerformIO)++data CompressionLevel = Zero+ | One+ | Two+ | Three+ | Four+ | Five+ | Six+ | Seven+ | Eight+ | Nine++data LzOptions = LzOptions { _dictionarySize :: !Int+ , _matchLenLimit :: !Int+ }++encoderOptions :: CompressionLevel -> LzOptions+encoderOptions Zero = LzOptions 65535 16+encoderOptions One = LzOptions (1 `shiftL` 20) 5+encoderOptions Two = LzOptions (3 `shiftL` 19) 6+encoderOptions Three = LzOptions (1 `shiftL` 21) 8+encoderOptions Four = LzOptions (3 `shiftL` 20) 12+encoderOptions Five = LzOptions (1 `shiftL` 22) 20+encoderOptions Six = LzOptions (1 `shiftL` 23) 36+encoderOptions Seven = LzOptions (1 `shiftL` 24) 68+encoderOptions Eight = LzOptions (3 `shiftL` 23) 132+encoderOptions Nine = LzOptions (1 `shiftL` 25) 273++-- | This does not do any error recovery; for that you should use+-- [lziprecover](https://www.nongnu.org/lzip/lziprecover.html).+{-# NOINLINE decompressStrict #-}+decompressStrict :: BS.ByteString -> BS.ByteString+decompressStrict bs = unsafePerformIO $ BS.useAsCStringLen bs $ \(bytes, sz) -> do++ decoder <- lZDecompressOpen+ maxSz <- lZDecompressWriteSize decoder++ let bufMax = min (fromIntegral maxSz) sz+ buf <- mallocBytes sz+ res <- loop decoder (buf, bufMax) (castPtr bytes, sz) 0 sz mempty++ void $ lZDecompressClose decoder+ free buf++ pure res++ where+ loop :: LZDecoderPtr -> (Ptr UInt8, Int) -> (Ptr UInt8, Int) -> Int -> Int -> BS.ByteString -> IO BS.ByteString+ loop decoder (buf, bufSz) (bytes, sz) offset total !acc = do+ let toWrite = min bufSz (total - offset)+ bytesWritten <- if offset < total+ then Just <$> lZDecompressWrite decoder (bytes `plusPtr` offset) (fromIntegral toWrite)+ else Nothing <$ lZDecompressFinish decoder+ res <- lZDecompressFinished decoder+ if res == 1+ then pure acc+ else do+ let newOffset = offset + fromIntegral (fromMaybe 0 bytesWritten)+ bytesRead <- lZDecompressRead decoder buf (fromIntegral bufSz)+ bsActual <- BS.packCStringLen (castPtr buf, fromIntegral bytesRead)+ loop decoder (buf, bufSz) (bytes, sz) newOffset total (acc <> bsActual)++{-# NOINLINE compressStrict #-}+compressStrict :: BS.ByteString -> BS.ByteString+compressStrict = compressWithStrict Nine++-- TODO: memory use+{-# NOINLINE compressWithStrict #-}+compressWithStrict :: CompressionLevel -> BS.ByteString -> BS.ByteString+compressWithStrict level bs = unsafePerformIO $ BS.useAsCStringLen bs $ \(bytes, sz) -> do++ encoder <- lZCompressOpen (fromIntegral dictionarySize) (fromIntegral matchLenLimit) (fromIntegral memberSize)++ void $ lZCompressWrite encoder (castPtr bytes) (fromIntegral sz)+ void $ lZCompressFinish encoder++ newBytes <- mallocBytes sz+ res <- readLoop encoder (newBytes, sz) 0 mempty++ void $ lZCompressClose encoder++ pure res++ where++ readLoop :: LZEncoderPtr -> (Ptr UInt8, Int) -> Int -> BS.ByteString -> IO BS.ByteString+ readLoop encoder (buf, sz) bytesRead acc = do+ bytesActual <- lZCompressRead encoder buf (fromIntegral sz)+ res <- lZCompressFinished encoder+ bsActual <- BS.packCStringLen (castPtr buf, fromIntegral bytesActual)+ if res == 1+ then pure (acc <> bsActual)+ else readLoop encoder (buf, sz) (bytesRead + fromIntegral bytesActual) (acc <> bsActual)++ memberSize :: Int64+ memberSize = maxBound++ dictionarySize = _dictionarySize $ encoderOptions level+ matchLenLimit = _matchLenLimit $ encoderOptions level
+ src/Codec/Lzip/Raw.chs view
@@ -0,0 +1,120 @@+-- | Consult the lzlib [documentation](https://www.nongnu.org/lzip/manual/lzlib_manual.html)+-- for more details+module Codec.Lzip.Raw ( -- * Prolegomena+ LZErrno (..)+ , lZVersion+ , lZStrerror+ , lZMinDictionaryBits+ , lZMinDictionarySize+ , lZMaxDictionaryBits+ , lZMaxDictionarySize+ , lZMinMatchLenLimit+ , lZMaxMatchLenLimit+ , UInt8+ -- * Compression functions+ , LZEncoder+ , LZEncoderPtr+ , lZCompressOpen+ , lZCompressClose+ , lZCompressFinish+ , lZCompressRestartMember+ , lZCompressSyncFlush+ , lZCompressRead+ , lZCompressWrite+ , lZCompressWriteSize+ , lZCompressErrno+ , lZCompressFinished+ , lZCompressMemberFinished+ , lZCompressDataPosition+ , lZCompressMemberPosition+ , lZCompressTotalInSize+ , lZCompressTotalOutSize+ -- * Decompression functions+ , LZDecoder+ , LZDecoderPtr+ , lZDecompressOpen+ , lZDecompressClose+ , lZDecompressFinish+ , lZDecompressReset+ , lZDecompressSyncToMember+ , lZDecompressRead+ , lZDecompressWrite+ , lZDecompressWriteSize+ , lZDecompressErrno+ , lZDecompressFinished+ , lZDecompressMemberFinished+ , lZDecompressDictionarySize+ , lZDecompressDataCrc+ , lZDecompressDataPosition+ , lZDecompressMemberPosition+ , lZDecompressTotalInSize+ , lZDecompressTotalOutSize+ ) where++import Foreign.C.Types+import Foreign.Ptr (Ptr)++#include <stdint.h>+#include <lzlib.h>++type UInt8 = {# type uint8_t #}+{#typedef uint8_t UInt8#}+{#default in `Ptr UInt8' [uint8_t *] id#}++{# enum LZ_Errno as LZErrno {underscoreToCase} deriving (Eq) #}++{# fun pure LZ_version as ^ {} -> `String' #}+{# fun pure LZ_strerror as ^ { `LZErrno' } -> `String' #}+{# fun LZ_min_dictionary_bits as ^ {} -> `CInt' #}+{# fun LZ_min_dictionary_size as ^ {} -> `CInt' #}+{# fun LZ_max_dictionary_bits as ^ {} -> `CInt' #}+{# fun LZ_max_dictionary_size as ^ {} -> `CInt' #}+{# fun LZ_min_match_len_limit as ^ {} -> `CInt' #}+{# fun LZ_max_match_len_limit as ^ {} -> `CInt' #}++instance Show LZErrno where+ show = lZStrerror++-- | Abstract data type+data LZEncoder++{# pointer *LZ_Encoder as LZEncoderPtr -> LZEncoder #}++{# fun LZ_compress_open as ^ { `CInt', `CInt', id `CULLong' } -> `LZEncoderPtr' #}+{# fun LZ_compress_close as ^ { `LZEncoderPtr' } -> `CInt' #}+{# fun LZ_compress_finish as ^ { `LZEncoderPtr' } -> `CInt' #}+{# fun LZ_compress_restart_member as ^ { `LZEncoderPtr', id `CULLong' } -> `CInt' #}+{# fun LZ_compress_sync_flush as ^ { `LZEncoderPtr' } -> `CInt' #}+{# fun LZ_compress_read as ^ { `LZEncoderPtr', `Ptr UInt8', `CInt' } -> `CInt' #}+{# fun LZ_compress_write as ^ { `LZEncoderPtr', `Ptr UInt8', `CInt' } -> `CInt' #}+{# fun LZ_compress_write_size as ^ { `LZEncoderPtr' } -> `CInt' #}+{# fun LZ_compress_errno as ^ { `LZEncoderPtr' } -> `LZErrno' #}+{# fun LZ_compress_finished as ^ { `LZEncoderPtr' } -> `CInt' #}+{# fun LZ_compress_member_finished as ^ { `LZEncoderPtr' } -> `CInt' #}+{# fun LZ_compress_data_position as ^ { `LZEncoderPtr' } -> `CULLong' id #}+{# fun LZ_compress_member_position as ^ { `LZEncoderPtr' } -> `CULLong' id #}+{# fun LZ_compress_total_in_size as ^ { `LZEncoderPtr' } -> `CULLong' id #}+{# fun LZ_compress_total_out_size as ^ { `LZEncoderPtr' } -> `CULLong' id #}++-- | Abstract data type+data LZDecoder++{# pointer *LZ_Decoder as LZDecoderPtr -> LZDecoder #}++{# fun LZ_decompress_open as ^ {} -> `LZDecoderPtr' #}+{# fun LZ_decompress_close as ^ { `LZDecoderPtr' } -> `CInt' #}+{# fun LZ_decompress_finish as ^ { `LZDecoderPtr' } -> `CInt' #}+{# fun LZ_decompress_reset as ^ { `LZDecoderPtr' } -> `CInt' #}+{# fun LZ_decompress_sync_to_member as ^ { `LZDecoderPtr' } -> `CInt' #}+{# fun LZ_decompress_read as ^ { `LZDecoderPtr', `Ptr UInt8', `CInt' } -> `CInt' #}+{# fun LZ_decompress_write as ^ { `LZDecoderPtr', `Ptr UInt8', `CInt' } -> `CInt' #}+{# fun LZ_decompress_write_size as ^ { `LZDecoderPtr' } -> `CInt' #}+{# fun LZ_decompress_errno as ^ { `LZDecoderPtr' } -> `LZErrno' #}+{# fun LZ_decompress_finished as ^ { `LZDecoderPtr' } -> `CInt' #}+{# fun LZ_decompress_member_finished as ^ { `LZDecoderPtr' } -> `CInt' #}+{# fun LZ_decompress_dictionary_size as ^ { `LZDecoderPtr' } -> `CInt' #}+{# fun LZ_decompress_data_crc as ^ { `LZDecoderPtr' } -> `CUInt' #}+{# fun LZ_decompress_data_position as ^ { `LZDecoderPtr' } -> `CULLong' id #}+{# fun LZ_decompress_member_position as ^ { `LZDecoderPtr' } -> `CULLong' id #}+{# fun LZ_decompress_total_in_size as ^ { `LZDecoderPtr' } -> `CULLong' id #}+{# fun LZ_decompress_total_out_size as ^ { `LZDecoderPtr' } -> `CULLong' id #}
stack.yaml view
@@ -1,4 +1,4 @@ ----resolver: lts-14.4+resolver: lts-14.5 packages: - '.'
+ test/Spec.hs view
@@ -0,0 +1,25 @@+module Main ( main ) where++import Codec.Lzip+import qualified Data.ByteString as BS+import Data.Foldable (traverse_)+import System.Directory (doesDirectoryExist)+import System.FilePath ((</>))+import System.FilePattern.Directory (getDirectoryFiles)+import Test.Hspec++compressFile :: FilePath -> Spec+compressFile fp = parallel $+ it "roundtrip should be identity" $ do+ str <- BS.readFile fp+ decompressStrict (compressStrict str) `shouldBe` str++main :: IO ()+main = do+ b <- doesDirectoryExist "dist-newstyle"+ libs <- if b+ then getDirectoryFiles "dist-newstyle" ["**/*.so", "**/*.dll"]+ else pure []+ hspec $ do+ describe "decompress/compress" $+ traverse_ compressFile (("dist-newstyle" </>) <$> libs)