@@ -1159,21 +1159,51 @@ fn gzungetc_basic() {
11591159
11601160 // gzread should yield the remaining 10 bytes of uncompressed content from the file,
11611161 // preceded by the 6 bytes we just pushed with gzungetc, for a total of 16 bytes.
1162- const EXPECTED : & [ u8 ] = b"123456\n for tests" ;
1162+ const EXPECTED1 : & [ u8 ] = b"123456\n for tests" ;
11631163 // Read more than expected to make sure there's no other output following it.
1164- let mut buf = [ 0u8 ; EXPECTED . len ( ) + 1 ] ;
1164+ let mut buf = [ 0u8 ; EXPECTED1 . len ( ) + 1 ] ;
11651165 assert_eq ! (
11661166 unsafe { gzread( file, buf. as_mut_ptr( ) . cast:: <c_void>( ) , buf. len( ) as _) } ,
1167- EXPECTED . len( ) as _
1167+ EXPECTED1 . len( ) as _
11681168 ) ;
1169- assert_eq ! ( & buf[ ..EXPECTED . len( ) ] , EXPECTED ) ;
1169+ assert_eq ! ( & buf[ ..EXPECTED1 . len( ) ] , EXPECTED1 ) ;
11701170
11711171 // The 16-byte output buffer is now empty. Call gzungetc 17 times. The first
1172- // 16 calls should succeed, and the last one should fail.
1172+ // 16 calls should succeed, and the last one should fail and set an error.
1173+ let mut err = Z_OK ;
1174+ assert ! ( !unsafe { gzerror( file, & mut err) } . is_null( ) ) ;
1175+ assert_eq ! ( err, Z_OK ) ;
11731176 for _ in 0 ..16 {
11741177 assert_eq ! ( unsafe { gzungetc( '-' as c_int, file) } , '-' as c_int) ;
11751178 }
11761179 assert_eq ! ( unsafe { gzungetc( '-' as c_int, file) } , -1 ) ;
1180+ assert ! ( !unsafe { gzerror( file, & mut err) } . is_null( ) ) ;
1181+ assert_eq ! ( err, Z_DATA_ERROR ) ;
1182+
1183+ // Clear the error state, rewind to the start of the file, and read some data
1184+ // to refill the output buffer.
1185+ unsafe { gzclearerr ( file) } ;
1186+ unsafe { gzrewind ( file) } ;
1187+ assert ! ( !unsafe { gzerror( file, & mut err) } . is_null( ) ) ;
1188+ assert_eq ! ( err, Z_OK ) ;
1189+ assert_eq ! ( unsafe { gzgetc( file) } , 'g' as c_int) ;
1190+ assert_eq ! ( unsafe { gzgetc( file) } , 'z' as c_int) ;
1191+
1192+ // Push a character back into the output buffer with gzungetc, issue a seek
1193+ // request to move to another part of the output stream, and then do another
1194+ // gzungetc. gzread should then return the character pushed in the second
1195+ // gzungetc call, followed by the data at the seek target location. The
1196+ // character pushed by the first gzungetc call should be discarded.
1197+ assert_eq ! ( unsafe { gzungetc( '7' as c_int, file) } , '7' as c_int) ;
1198+ assert_eq ! ( unsafe { gzseek( file, 1 , libc:: SEEK_CUR ) } , 2 ) ;
1199+ assert_eq ! ( unsafe { gzungetc( '8' as c_int, file) } , '8' as c_int) ;
1200+ const EXPECTED2 : & [ u8 ] = b"8ip\n example" ;
1201+ let mut buf = [ 0u8 ; EXPECTED2 . len ( ) ] ;
1202+ assert_eq ! (
1203+ unsafe { gzread( file, buf. as_mut_ptr( ) . cast:: <c_void>( ) , buf. len( ) as _) } ,
1204+ EXPECTED2 . len( ) as _
1205+ ) ;
1206+ assert_eq ! ( & buf, EXPECTED2 ) ;
11771207
11781208 assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
11791209}
@@ -1649,7 +1679,6 @@ fn gzseek_read() {
16491679 assert_eq ! ( unsafe { libc:: close( fd) } , 0 ) ;
16501680
16511681 for file_name in [ direct_file_name, gzip_file_name] {
1652- eprintln ! ( "opening {}" , file_name) ;
16531682 let file = unsafe {
16541683 gzopen (
16551684 CString :: new ( file_name. as_str ( ) ) . unwrap ( ) . as_ptr ( ) ,
@@ -1743,6 +1772,159 @@ fn gzseek_read() {
17431772 }
17441773}
17451774
1775+ #[ test]
1776+ fn gzseek_write ( ) {
1777+ // Create a temporary directory that will be automatically removed when
1778+ // temp_dir goes out of scope.
1779+ let temp_dir_path = temp_base ( ) ;
1780+ let temp_dir = tempfile:: TempDir :: new_in ( temp_dir_path) . unwrap ( ) ;
1781+ let temp_path = temp_dir. path ( ) ;
1782+
1783+ // Test both compressed and direct (non-compressed) writes.
1784+ for mode in [ "w" , "wT" ] {
1785+ // Open a file handle for writing.
1786+ let file_name = path ( temp_path, "output" ) ;
1787+ let file = unsafe {
1788+ gzopen (
1789+ CString :: new ( file_name. as_str ( ) ) . unwrap ( ) . as_ptr ( ) ,
1790+ CString :: new ( mode) . unwrap ( ) . as_ptr ( ) ,
1791+ )
1792+ } ;
1793+ assert ! ( !file. is_null( ) ) ;
1794+
1795+ // Set a small buffer size to help exercise all the code paths.
1796+ const BUF_SIZE : c_uint = 8 ;
1797+ assert_eq ! ( unsafe { gzbuffer( file, BUF_SIZE ) } , 0 ) ;
1798+
1799+ // gzseek forward a few bytes immediately.
1800+ assert_eq ! ( unsafe { gzseek( file, 3 , libc:: SEEK_SET ) } , 3 ) ;
1801+ assert_eq ! ( unsafe { gztell( file) } , 3 ) ;
1802+
1803+ // Write some data, with gzseek calls interleaved. Note: Part of the internal seek
1804+ // implementation is done lazily in the next write call, so we use a combination
1805+ // of all the write functions: gzwrite, gzputc, gzputs, and gzflush. gzsetparams
1806+ // also implements the pending seek, but it is not supported in direct-mode
1807+ // (non-compressed) files, so it is tested separately in the function
1808+ // gzseek_gzsetparams.
1809+ const STRING1 : & [ u8 ] = b"0123" ;
1810+ assert_eq ! (
1811+ unsafe { gzwrite( file, STRING1 . as_ptr( ) . cast:: <c_void>( ) , STRING1 . len( ) as _) } ,
1812+ STRING1 . len( ) as _
1813+ ) ;
1814+ assert_eq ! ( unsafe { gztell( file) } , 7 ) ;
1815+ assert_eq ! ( unsafe { gzseek( file, 1 , libc:: SEEK_CUR ) } , 8 ) ;
1816+ assert_eq ! ( unsafe { gzseek( file, 12 , libc:: SEEK_SET ) } , 12 ) ;
1817+ assert_eq ! ( unsafe { gztell( file) } , 12 ) ;
1818+ const STRING2 : & [ u8 ] = b"456\0 " ;
1819+ assert_eq ! (
1820+ unsafe { gzputs( file, STRING2 . as_ptr( ) . cast:: <c_char>( ) ) } ,
1821+ ( STRING2 . len( ) - 1 ) as _
1822+ ) ;
1823+ assert_eq ! ( unsafe { gztell( file) } , 15 ) ;
1824+ assert_eq ! ( unsafe { gzseek( file, 2 , libc:: SEEK_CUR ) } , 17 ) ;
1825+ assert_eq ! ( unsafe { gzputc( file, b'7' as _) } , b'7' as _) ;
1826+ assert_eq ! ( unsafe { gztell( file) } , 18 ) ;
1827+ assert_eq ! ( unsafe { gzseek( file, 1 , libc:: SEEK_CUR ) } , 19 ) ;
1828+ assert_eq ! ( unsafe { gzflush( file, Z_SYNC_FLUSH ) } , Z_OK ) ;
1829+ assert_eq ! ( unsafe { gzputc( file, b'8' as _) } , b'8' as _) ;
1830+
1831+ // Do one more gzseek at the end, and then close the file handle. This should produce
1832+ // the specified number of zero bytes at the end of the uncompressed data stream.
1833+ assert_eq ! (
1834+ unsafe { gzseek( file, ( BUF_SIZE * 3 + 1 ) as _, libc:: SEEK_CUR ) } ,
1835+ ( 20 + BUF_SIZE * 3 + 1 ) as _
1836+ ) ;
1837+ assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
1838+
1839+ // Read the file and confirm that each of the gzeek calls produced the expected
1840+ // number of zero bytes.
1841+ let file = unsafe {
1842+ gzopen (
1843+ CString :: new ( file_name. as_str ( ) ) . unwrap ( ) . as_ptr ( ) ,
1844+ CString :: new ( "r" ) . unwrap ( ) . as_ptr ( ) ,
1845+ )
1846+ } ;
1847+ assert ! ( !file. is_null( ) ) ;
1848+ const EXPECTED1 : & [ u8 ] = b"\x00 \x00 \x00 0123\x00 \x00 \x00 \x00 \x00 456\x00 \x00 7\x00 8" ;
1849+ let mut buf = [ 127u8 ; EXPECTED1 . len ( ) ] ;
1850+ assert_eq ! (
1851+ unsafe { gzread( file, buf. as_mut_ptr( ) . cast:: <c_void>( ) , buf. len( ) as _) } ,
1852+ buf. len( ) as _
1853+ ) ;
1854+ assert_eq ! ( & buf, EXPECTED1 ) ;
1855+ const EXPECTED2 : & [ u8 ] = & [ 0u8 ; BUF_SIZE as usize * 3 + 1 ] ;
1856+ let mut buf = [ 127u8 ; EXPECTED2 . len ( ) + 1 ] ;
1857+ assert_eq ! (
1858+ unsafe { gzread( file, buf. as_mut_ptr( ) . cast:: <c_void>( ) , buf. len( ) as _) } ,
1859+ EXPECTED2 . len( ) as _
1860+ ) ;
1861+ assert_eq ! ( & buf[ ..EXPECTED2 . len( ) ] , EXPECTED2 ) ;
1862+ assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
1863+ }
1864+ }
1865+
1866+ #[ test]
1867+ fn gzseek_gzsetparams ( ) {
1868+ // Create a temporary directory that will be automatically removed when
1869+ // temp_dir goes out of scope.
1870+ let temp_dir_path = temp_base ( ) ;
1871+ let temp_dir = tempfile:: TempDir :: new_in ( temp_dir_path) . unwrap ( ) ;
1872+ let temp_path = temp_dir. path ( ) ;
1873+
1874+ // Open a file handle for writing in compressed mode.
1875+ let file_name = path ( temp_path, "output.gz" ) ;
1876+ let file = unsafe {
1877+ gzopen (
1878+ CString :: new ( file_name. as_str ( ) ) . unwrap ( ) . as_ptr ( ) ,
1879+ CString :: new ( "w" ) . unwrap ( ) . as_ptr ( ) ,
1880+ )
1881+ } ;
1882+ assert ! ( !file. is_null( ) ) ;
1883+
1884+ // Write some content to the file handle.
1885+ const STRING1 : & [ u8 ] = b"hello" ;
1886+ assert_eq ! ( unsafe { gzwrite( file, STRING1 . as_ptr( ) . cast:: <c_void>( ) , STRING1 . len( ) as _) } , STRING1 . len( ) as _) ;
1887+
1888+ // Call gzseek to schedule a pending write of some zeros to the compressed stream.
1889+ const SEEK_AMOUNT : usize = 4 ;
1890+ assert_eq ! ( unsafe { gzseek( file, SEEK_AMOUNT as _, libc:: SEEK_CUR ) } , 9 ) ;
1891+
1892+ // Before doing another write, call gzsetparams. This should write the pending zeros
1893+ // to the current gzip stream before closing the stream and starting a new one.
1894+ assert_eq ! ( unsafe { gzsetparams( file, 9 , 2 ) } , Z_OK ) ;
1895+ assert_eq ! ( unsafe { gztell( file) } , ( STRING1 . len( ) + SEEK_AMOUNT ) as _) ;
1896+
1897+ // Write some more content to the file handle. This will end up in the second gzip stream
1898+ // in the file.
1899+ const STRING2 : & [ u8 ] = b"world" ;
1900+ assert_eq ! ( unsafe { gzwrite( file, STRING2 . as_ptr( ) . cast:: <c_void>( ) , STRING2 . len( ) as _) } , STRING2 . len( ) as _) ;
1901+
1902+ // Close the file handle to flush any buffered output to the file.
1903+ assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
1904+
1905+ // Open the newly created file for reading.
1906+ let file = unsafe {
1907+ gzopen (
1908+ CString :: new ( file_name. as_str ( ) ) . unwrap ( ) . as_ptr ( ) ,
1909+ CString :: new ( "r" ) . unwrap ( ) . as_ptr ( ) ,
1910+ )
1911+ } ;
1912+ assert ! ( !file. is_null( ) ) ;
1913+
1914+ // Read back the content to validate that it was written correctly.
1915+ let mut buf = [ 127u8 ; STRING1 . len ( ) ] ;
1916+ assert_eq ! ( unsafe { gzread( file, buf. as_mut_ptr( ) . cast:: <c_void>( ) , buf. len( ) as _) } , buf. len( ) as _) ;
1917+ assert_eq ! ( & buf, STRING1 ) ;
1918+ for _ in 0 ..SEEK_AMOUNT {
1919+ assert_eq ! ( unsafe { gzgetc( file) } , 0 ) ;
1920+ }
1921+ let mut buf = [ 127u8 ; STRING2 . len ( ) + 1 ] ;
1922+ assert_eq ! ( unsafe { gzread( file, buf. as_mut_ptr( ) . cast:: <c_void>( ) , buf. len( ) as _) } , ( buf. len( ) - 1 ) as _) ;
1923+ assert_eq ! ( & buf[ ..STRING2 . len( ) ] , STRING2 ) ;
1924+
1925+ assert_eq ! ( unsafe { gzclose( file) } , Z_OK ) ;
1926+ }
1927+
17461928#[ test]
17471929fn gzseek_error ( ) {
17481930 // gzseek on a null file handle should return -1.
0 commit comments