@@ -6,7 +6,7 @@ pub use zlib_rs::c_api::*;
66use crate :: { deflateEnd, inflateEnd, inflateInit2, inflateReset} ;
77use core:: ffi:: { c_char, c_int, c_uint, CStr } ;
88use core:: ptr;
9- use libc:: { size_t , O_APPEND , O_CREAT , O_EXCL , O_RDONLY , O_TRUNC , O_WRONLY , SEEK_CUR , SEEK_END } ;
9+ use libc:: { O_APPEND , O_CREAT , O_EXCL , O_RDONLY , O_TRUNC , O_WRONLY , SEEK_CUR , SEEK_END } ;
1010use zlib_rs:: deflate:: Strategy ;
1111use zlib_rs:: MAX_WBITS ;
1212
@@ -754,20 +754,20 @@ pub unsafe extern "C-unwind" fn gzdirect(file: gzFile) -> c_int {
754754//
755755// # Returns
756756//
757- // * 0 on success
758- // * -1 on failure
757+ // * `Ok` on success
758+ // * `Err` on failure
759759//
760760// # Safety
761761//
762762// `state` must have been properly initialized, e.g. by [`gzopen_help`].
763- unsafe fn gz_look ( state : & mut GzState ) -> c_int {
763+ unsafe fn gz_look ( state : & mut GzState ) -> Result < ( ) , ( ) > {
764764 // Allocate buffers if needed.
765765 if state. input . is_null ( ) {
766766 state. in_size = state. want ;
767767 let Some ( input) = ALLOCATOR . allocate_slice_raw :: < u8 > ( state. in_size ) else {
768768 // Safety:
769769 unsafe { gz_error ( state, Some ( ( Z_MEM_ERROR , "out of memory" ) ) ) } ;
770- return - 1 ;
770+ return Err ( ( ) ) ;
771771 } ;
772772 state. input = input. as_ptr ( ) ;
773773 }
@@ -779,7 +779,7 @@ unsafe fn gz_look(state: &mut GzState) -> c_int {
779779 unsafe { free_buffers ( state) } ;
780780 // Safety: The caller confirmed the validity of state.
781781 unsafe { gz_error ( state, Some ( ( Z_MEM_ERROR , "out of memory" ) ) ) } ;
782- return - 1 ;
782+ return Err ( ( ) ) ;
783783 } ;
784784 state. output = output. as_ptr ( ) ;
785785 }
@@ -795,17 +795,17 @@ unsafe fn gz_look(state: &mut GzState) -> c_int {
795795 unsafe { free_buffers ( state) } ;
796796 // Safety: The caller confirmed the validity of `state`.
797797 unsafe { gz_error ( state, Some ( ( Z_MEM_ERROR , "out of memory" ) ) ) } ;
798- return - 1 ;
798+ return Err ( ( ) ) ;
799799 }
800800
801801 // Get at least the magic bytes in the input buffer.
802802 if state. stream . avail_in < 2 {
803803 // Safety: The caller confirmed the validity of `state`.
804- if unsafe { gz_avail ( state) } == - 1 {
805- return - 1 ;
806- }
804+ let Ok ( _ ) = ( unsafe { gz_avail ( state) } ) else {
805+ return Err ( ( ) ) ;
806+ } ;
807807 if state. stream . avail_in == 0 {
808- return 0 ;
808+ return Ok ( ( ) ) ;
809809 }
810810 }
811811
@@ -819,16 +819,17 @@ unsafe fn gz_look(state: &mut GzState) -> c_int {
819819 unsafe { inflateReset ( & mut state. stream as * mut z_stream ) } ;
820820 state. how = How :: Gzip ;
821821 state. direct = false ;
822- return 0 ;
822+ return Ok ( ( ) ) ;
823823 }
824824
825825 // No gzip header. If we were decoding gzip before, the remaining bytes
826826 // are trailing garbage that can be ignored.
827+ // FIXME: Add test coverage for this case (which may require the remaining decompression logic).
827828 if !state. direct {
828829 state. stream . avail_in = 0 ;
829830 state. eof = true ;
830831 state. have = 0 ;
831- return 0 ;
832+ return Ok ( ( ) ) ;
832833 }
833834
834835 // The file is not in gzip format, so enable direct mode, and copy all
@@ -837,7 +838,7 @@ unsafe fn gz_look(state: &mut GzState) -> c_int {
837838 // * `state.output` was allocated above.
838839 // * `gz_avail` ensures that `next_in` points to at least `avail_in` readable bytes.
839840 unsafe {
840- ptr:: copy_nonoverlapping (
841+ ptr:: copy (
841842 state. stream . next_in ,
842843 state. output ,
843844 state. stream . avail_in as usize ,
@@ -849,59 +850,74 @@ unsafe fn gz_look(state: &mut GzState) -> c_int {
849850 state. how = How :: Copy ;
850851 state. direct = true ;
851852
852- 0
853+ Ok ( ( ) )
853854}
854855
855856// Load data into the input buffer and set the eof flag if the last of the data has been
856857// loaded.
857858//
858859// # Returns
859860//
860- // * -1 if an error occurs.
861- // * 0 otherwise.
861+ // * `Ok(n)` on success, where `n` is the number of bytes available (`state.stream.avail_in`)
862+ // * `Err` on error
862863//
863864// # Safety
864865//
865866// `state` must have been properly initialized, e.g. by [`gzopen_help`].
866- unsafe fn gz_avail ( state : & mut GzState ) -> c_int {
867+ unsafe fn gz_avail ( state : & mut GzState ) -> Result < usize , ( ) > {
867868 if state. err != Z_OK && state. err != Z_BUF_ERROR {
868- return - 1 ;
869+ return Err ( ( ) ) ;
869870 }
870871 if !state. eof {
871872 if state. stream . avail_in != 0 {
872873 // Copy any remaining input to the start.
873874 unsafe {
874- ptr:: copy_nonoverlapping (
875+ ptr:: copy (
875876 state. stream . next_in ,
876877 state. input ,
877878 state. stream . avail_in as usize ,
878879 )
879880 } ;
880881 }
881- let Ok ( got) = ( unsafe {
882+ let got = unsafe {
882883 gz_load (
883884 state,
884885 state. input . add ( state. stream . avail_in as usize ) ,
885886 state. in_size - state. stream . avail_in as usize ,
886887 )
887- } ) else {
888- return -1 ;
889- } ;
888+ } ?;
890889 state. stream . avail_in += got as uInt ;
891890 state. stream . next_in = state. input ;
892891 }
893- 0
892+ Ok ( state . stream . avail_in as usize )
894893}
895894
896- unsafe fn gz_load ( state : & mut GzState , buf : * mut u8 , len : size_t ) -> Result < size_t , ( ) > {
895+ // Read data from `state`'s underlying file descriptor into a buffer.
896+ //
897+ // # Returns
898+ //
899+ // * `Ok(n)` on success, where `n` is the number of bytes read.
900+ // * `Err` on error
901+ //
902+ // # Arguments
903+ //
904+ // * `state` - gzip file handle.
905+ // * `buf` - address at which the data read from the file should be stored.
906+ // * `size` - number of bytes to read
907+ //
908+ // # Safety
909+ //
910+ // * `state` must have been properly initialized, e.g. by [`gzopen_help`].
911+ // * `buf` mut point to a writable block of at least `len` bytes.
912+ unsafe fn gz_load ( state : & mut GzState , buf : * mut u8 , len : usize ) -> Result < usize , ( ) > {
897913 let mut have = 0 ;
898914 let mut ret = 0 ;
899915 while have < len {
900916 ret = unsafe { libc:: read ( state. fd , buf. add ( have) . cast :: < _ > ( ) , ( len - have) as _ ) } ;
901917 if ret <= 0 {
902918 break ;
903919 }
904- have += ret as size_t ;
920+ have += ret as usize ;
905921 }
906922 if ret < 0 {
907923 unsafe { gz_error ( state, Some ( ( Z_ERRNO , "read error" ) ) ) } ; // FIXME implement `zstrerror`
@@ -973,6 +989,19 @@ unsafe fn gz_strcat(strings: &[&str]) -> *mut c_char {
973989#[ cfg( test) ]
974990mod tests {
975991 use super :: * ;
992+ use std:: ffi:: CString ;
993+ use std:: path:: Path ;
994+
995+ // Generate a file path relative to the project's root
996+ fn crate_path ( file : & str ) -> String {
997+ path ( Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) , file)
998+ }
999+
1000+ fn path ( prefix : & Path , file : & str ) -> String {
1001+ let mut path_buf = prefix. to_path_buf ( ) ;
1002+ path_buf. push ( file) ;
1003+ path_buf. as_path ( ) . to_str ( ) . unwrap ( ) . to_owned ( )
1004+ }
9761005
9771006 #[ test]
9781007 fn test_configure ( ) {
@@ -1102,6 +1131,9 @@ mod tests {
11021131 #[ test]
11031132 #[ cfg_attr( not( target_os = "linux" ) , ignore = "lseek is not implemented" ) ]
11041133 fn test_gz_error ( ) {
1134+ // gzerror(null) should return null.
1135+ assert ! ( unsafe { gzerror( ptr:: null_mut( ) , ptr:: null_mut( ) ) } . is_null( ) ) ;
1136+
11051137 // Open a gzip stream with an invalid file handle. Initially, no error
11061138 // status should be set.
11071139 let handle = unsafe { gzdopen ( -2 , c ! ( b"r\0 " ) ) } ;
@@ -1270,4 +1302,111 @@ mod tests {
12701302 assert_eq ! ( unsafe { gzclose( handle) } , Z_ERRNO ) ;
12711303 }
12721304 }
1305+
1306+ #[ test]
1307+ fn test_gzdirect ( ) {
1308+ // gzdirect(null) should return 0.
1309+ assert_eq ! ( unsafe { gzdirect( ptr:: null_mut( ) ) } , 0 ) ;
1310+
1311+ // Open a gzip stream from an invalid file descriptor. gzdirect should return 1, but
1312+ // it should cause an error condition to be set within the stream.
1313+ let file = unsafe { gzdopen ( -2 , CString :: new ( "r" ) . unwrap ( ) . as_ptr ( ) ) } ;
1314+ assert ! ( !file. is_null( ) ) ;
1315+ assert_eq ! ( unsafe { gzdirect( file) } , 1 ) ;
1316+ let mut err = Z_OK ;
1317+ let msg = unsafe { gzerror ( file, & mut err as * mut c_int ) } ;
1318+ assert ! ( !msg. is_null( ) ) ;
1319+ assert_eq ! ( err, Z_ERRNO ) ;
1320+ assert_eq ! ( unsafe { gzclose( file) } , Z_ERRNO ) ;
1321+
1322+ // Open a gzip file for reading. gzdirect should return 0.
1323+ let file = unsafe {
1324+ gzopen (
1325+ CString :: new ( crate_path ( "src/test-data/example.gz" ) )
1326+ . unwrap ( )
1327+ . as_ptr ( ) ,
1328+ CString :: new ( "r" ) . unwrap ( ) . as_ptr ( ) ,
1329+ )
1330+ } ;
1331+ assert ! ( !file. is_null( ) ) ;
1332+ // Set a smaller read batch size to exercise the buffer management code paths.
1333+ const FILE_SIZE : usize = 48 ; // size of test-data/example.gz
1334+ const BLOCK_SIZE : usize = 40 ;
1335+ unsafe { file. cast :: < GzState > ( ) . as_mut ( ) . unwrap ( ) . want = BLOCK_SIZE } ;
1336+ assert_eq ! ( unsafe { gzdirect( file) } , 0 ) ;
1337+ // gzdirect should have pulled the first `BLOCK_SIZE` bytes of the file into `file`'s internal input buffer.
1338+ assert_eq ! ( unsafe { file. cast:: <GzState >( ) . as_ref( ) . unwrap( ) . have } , 0 ) ;
1339+ assert_eq ! (
1340+ unsafe { file. cast:: <GzState >( ) . as_ref( ) . unwrap( ) . stream. avail_in } ,
1341+ BLOCK_SIZE as uInt
1342+ ) ;
1343+ // Consume some of the buffered input and call gz_avail. It should move the remaining
1344+ // input to the front of the input buffer.
1345+ unsafe {
1346+ let state = file. cast :: < GzState > ( ) . as_mut ( ) . unwrap ( ) ;
1347+ const CONSUME : usize = 10 ;
1348+ state. stream . next_in = state. stream . next_in . add ( CONSUME ) ;
1349+ state. stream . avail_in -= CONSUME as uInt ;
1350+ let expected_avail = BLOCK_SIZE - CONSUME + ( FILE_SIZE - BLOCK_SIZE ) ;
1351+ assert_eq ! ( gz_avail( state) , Ok ( expected_avail) ) ;
1352+ assert_eq ! ( state. stream. avail_in as usize , expected_avail) ;
1353+ } ;
1354+ assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
1355+
1356+ // Open a non-gzip file for reading. gzdirect should return 1.
1357+ let file = unsafe {
1358+ gzopen (
1359+ CString :: new ( crate_path ( "src/test-data/example.txt" ) )
1360+ . unwrap ( )
1361+ . as_ptr ( ) ,
1362+ CString :: new ( "r" ) . unwrap ( ) . as_ptr ( ) ,
1363+ )
1364+ } ;
1365+ assert ! ( !file. is_null( ) ) ;
1366+ assert_eq ! ( unsafe { gzdirect( file) } , 1 ) ;
1367+ // gzdirect should have pulled the entire contents of the file (which is smaller than
1368+ // GZBUFSIZE) into `file`'s internal output buffer.
1369+ assert_eq ! ( unsafe { file. cast:: <GzState >( ) . as_ref( ) . unwrap( ) . have } , 20 ) ;
1370+ assert_eq ! (
1371+ unsafe { file. cast:: <GzState >( ) . as_ref( ) . unwrap( ) . stream. avail_in } ,
1372+ 0
1373+ ) ;
1374+ assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
1375+
1376+ // Open a file containing only the gzip magic number. gzdirect should return 0.
1377+ let file = unsafe {
1378+ gzopen (
1379+ CString :: new ( crate_path ( "src/test-data/magic-only.gz" ) )
1380+ . unwrap ( )
1381+ . as_ptr ( ) ,
1382+ CString :: new ( "r" ) . unwrap ( ) . as_ptr ( ) ,
1383+ )
1384+ } ;
1385+ assert ! ( !file. is_null( ) ) ;
1386+ assert_eq ! ( unsafe { gzdirect( file) } , 0 ) ;
1387+ assert_eq ! ( unsafe { file. cast:: <GzState >( ) . as_ref( ) . unwrap( ) . have } , 0 ) ;
1388+ assert_eq ! (
1389+ unsafe { file. cast:: <GzState >( ) . as_ref( ) . unwrap( ) . stream. avail_in } ,
1390+ 2
1391+ ) ;
1392+ assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
1393+
1394+ // Open a file containing only the first byte of the gzip magic number. gzdirect should return 1.
1395+ let file = unsafe {
1396+ gzopen (
1397+ CString :: new ( crate_path ( "src/test-data/incomplete-magic.gz" ) )
1398+ . unwrap ( )
1399+ . as_ptr ( ) ,
1400+ CString :: new ( "r" ) . unwrap ( ) . as_ptr ( ) ,
1401+ )
1402+ } ;
1403+ assert ! ( !file. is_null( ) ) ;
1404+ assert_eq ! ( unsafe { gzdirect( file) } , 1 ) ;
1405+ assert_eq ! ( unsafe { file. cast:: <GzState >( ) . as_ref( ) . unwrap( ) . have } , 1 ) ;
1406+ assert_eq ! (
1407+ unsafe { file. cast:: <GzState >( ) . as_ref( ) . unwrap( ) . stream. avail_in } ,
1408+ 0
1409+ ) ;
1410+ assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
1411+ }
12731412}
0 commit comments