@@ -35,7 +35,7 @@ struct GzState {
35
35
// fields directly.
36
36
have : c_uint , // number of bytes available at next
37
37
next : * const Bytef , // next byte of uncompressed data
38
- pos : u64 , // current offset in uncompressed data stream
38
+ pos : i64 , // current offset in uncompressed data stream
39
39
40
40
// End of public interface:
41
41
// All fields after this point are opaque to C code using this library,
@@ -1108,7 +1108,7 @@ unsafe fn gz_read(state: &mut GzState, mut buf: *mut u8, mut len: usize) -> usiz
1108
1108
len -= n;
1109
1109
buf = unsafe { buf. add ( n) } ;
1110
1110
got += n;
1111
- state. pos += n as u64 ;
1111
+ state. pos += n as i64 ;
1112
1112
1113
1113
if len == 0 {
1114
1114
break ;
@@ -1561,7 +1561,7 @@ unsafe fn gz_write(state: &mut GzState, mut buf: *const c_void, mut len: usize)
1561
1561
// bytes, and copy is <= len.
1562
1562
unsafe { ptr:: copy ( buf, state. input . add ( have) . cast :: < c_void > ( ) , copy) } ;
1563
1563
state. stream . avail_in += copy as c_uint ;
1564
- state. pos += copy as u64 ;
1564
+ state. pos += copy as i64 ;
1565
1565
buf = unsafe { buf. add ( copy) } ;
1566
1566
len -= copy;
1567
1567
if len != 0 && gz_comp ( state, Z_NO_FLUSH ) . is_err ( ) {
@@ -1585,7 +1585,7 @@ unsafe fn gz_write(state: &mut GzState, mut buf: *const c_void, mut len: usize)
1585
1585
loop {
1586
1586
let n = cmp:: min ( len, c_uint:: MAX as usize ) as c_uint ;
1587
1587
state. stream . avail_in = n;
1588
- state. pos += n as u64 ;
1588
+ state. pos += n as i64 ;
1589
1589
if gz_comp ( state, Z_NO_FLUSH ) . is_err ( ) {
1590
1590
return 0 ;
1591
1591
}
@@ -1838,7 +1838,7 @@ pub unsafe extern "C-unwind" fn gztell(file: gzFile) -> z_off_t {
1838
1838
1839
1839
// Return position.
1840
1840
match state. seek {
1841
- true => ( state. pos + state. skip as u64 ) as z_off_t ,
1841
+ true => ( state. pos + state. skip ) as z_off_t ,
1842
1842
false => state. pos as z_off_t ,
1843
1843
}
1844
1844
}
@@ -2045,6 +2045,113 @@ pub unsafe extern "C-unwind" fn gzgetc_(file: gzFile) -> c_int {
2045
2045
unsafe { gzgetc ( file) }
2046
2046
}
2047
2047
2048
+ /// Push `c` back onto the stream for file to be read as the first character on
2049
+ /// the next read. At least one character of push-back is always allowed.
2050
+ ///
2051
+ /// `gzungetc` will fail if `c` is `-1`, and may fail if a character has been pushed
2052
+ /// but not read yet. If `gzungetc` is used immediately after [`gzopen`] or [`gzdopen`],
2053
+ /// at least the output buffer size of pushed characters is allowed. (See [`gzbuffer`].)
2054
+ ///
2055
+ /// The pushed character will be discarded if the stream is repositioned with
2056
+ /// [`gzseek`] or [`gzrewind`].
2057
+ ///
2058
+ /// # Returns
2059
+ ///
2060
+ /// - The character pushed, on success.
2061
+ /// - `-1` on failure.
2062
+ ///
2063
+ /// # Safety
2064
+ ///
2065
+ /// - `file`, if non-null, must be an open file handle obtained from [`gzopen`] or [`gzdopen`].
2066
+ #[ cfg_attr( feature = "export-symbols" , export_name = crate :: prefix!( gzungetc) ) ]
2067
+ pub unsafe extern "C-unwind" fn gzungetc ( c : c_int , file : gzFile ) -> c_int {
2068
+ let Some ( state) = ( unsafe { file. cast :: < GzState > ( ) . as_mut ( ) } ) else {
2069
+ return -1 ;
2070
+ } ;
2071
+
2072
+ // Validate the input.
2073
+ if c < 0 {
2074
+ return -1 ;
2075
+ }
2076
+
2077
+ // Check that we're reading and that there's no (serious) error.
2078
+ if state. mode != GzMode :: GZ_READ || ( state. err != Z_OK && state. err != Z_BUF_ERROR ) {
2079
+ return -1 ;
2080
+ }
2081
+
2082
+ // In case this was just opened, set up the input buffer.
2083
+ if state. how == How :: Look && state. have == 0 {
2084
+ // We have verified that `state` is valid.
2085
+ let _ = unsafe { gz_look ( state) } ;
2086
+ }
2087
+
2088
+ /* FIXME uncomment when seek support is implemented.
2089
+ // Process a skip request.
2090
+ if state.seek {
2091
+ state.seek = false;
2092
+ if gz_skip(state, state.skip) == -1 {
2093
+ return -1;
2094
+ }
2095
+ }
2096
+ */
2097
+
2098
+ // If output buffer empty, put byte at end (allows more pushing).
2099
+ if state. have == 0 {
2100
+ state. have = 1 ;
2101
+ // Safety: because `state.have` is nonzero, the `state.output` buffer has been
2102
+ // allocated. And because the buffer's size is `state.out_size`, a pointer to
2103
+ // `output + out_size - 1` points within the buffer.
2104
+ state. next = unsafe { state. output . add ( state. out_size - 1 ) } ;
2105
+ // Safety: from the addition above, `state.next` currently points within the
2106
+ // `state.output` buffer.
2107
+ unsafe { * ( state. next as * mut u8 ) = c as u8 } ;
2108
+ state. pos -= 1 ;
2109
+ state. past = false ;
2110
+ return c;
2111
+ }
2112
+
2113
+ // If no room, give up (must have already done a `gzungetc`).
2114
+ if state. have as usize == state. out_size {
2115
+ const MSG : & str = "out of room to push characters" ;
2116
+ // Safety: We have verified that `state` is valid.
2117
+ unsafe { gz_error ( state, Some ( ( Z_DATA_ERROR , MSG ) ) ) } ;
2118
+ return -1 ;
2119
+ }
2120
+
2121
+ // Slide output data if needed and insert byte before existing data.
2122
+ if state. next == state. output {
2123
+ // There are `state.have` bytes of usable content at the front of the buffer
2124
+ // `state.output`, which has capacity `state.out_size`. We want to move that
2125
+ // content to the end of the buffer, so we copy from `state.output` to
2126
+ // `state.output + (state.out_size - state.have)` and update `state.next`
2127
+ // to point to the content's new location within the buffer.
2128
+ let offset = state. out_size - state. have as usize ;
2129
+
2130
+ // Safety: `state.have` < `state.out_size`, or we would have returned in the
2131
+ // check for the == case above. Therefore, `offset`, which is `out_size - have`,
2132
+ // is in the range `1..=(out_size - 1)`. When we add that to `output`, the result
2133
+ // is within the buffer's allocation of `out_size` bytes.
2134
+ let dst = unsafe { state. output . add ( offset) } ;
2135
+
2136
+ // Safety: `state.next` points a sequence of `state.have` initialized bytes
2137
+ // within the `state.output` buffer. And because `dst` was computed as
2138
+ // `state.output + state.out_size - state.have`, we can write `state.have`
2139
+ // bytes starting at `dst` and they will all be within the buffer.
2140
+ // Note that this may be an overlapping copy.
2141
+ unsafe { ptr:: copy ( state. next , dst as _ , state. have as _ ) } ;
2142
+ state. next = dst;
2143
+ }
2144
+ state. have += 1 ;
2145
+ // Safety: `state.next` > `state.output`, due to the `state.next = dst` above, so it
2146
+ // is safe to decrease `state.next` by 1.
2147
+ state. next = unsafe { state. next . sub ( 1 ) } ;
2148
+ // Safety: `state.next` >= `state.output` following the subtraction.
2149
+ unsafe { * ( state. next as * mut u8 ) = c as u8 } ;
2150
+ state. pos -= 1 ;
2151
+ state. past = false ;
2152
+ c
2153
+ }
2154
+
2048
2155
/// Read decompressed bytes from `file` into `buf`, until `len-1` characters are
2049
2156
/// read, or until a newline character is read and transferred to `buf`, or an
2050
2157
/// end-of-file condition is encountered. If any characters are read or if `len`
@@ -2138,7 +2245,7 @@ pub unsafe extern "C-unwind" fn gzgets(file: gzFile, buf: *mut c_char, len: c_in
2138
2245
// Safety: As described above, `state.next` pointed to at least `n` readable bytes, so
2139
2246
// when we increase it by `n` it will still point into the `output` buffer.
2140
2247
state. next = unsafe { state. next . add ( n) } ;
2141
- state. pos += n as u64 ;
2248
+ state. pos += n as i64 ;
2142
2249
left -= n;
2143
2250
// Safety: `dst` pointed to at least `n` writable bytes, so when we increase it by `n`
2144
2251
// it will still point into `buf`.
0 commit comments