@@ -515,7 +515,8 @@ impl State<'_> {
515
515
let avail_out = self . writer . remaining ( ) ;
516
516
517
517
if avail_in >= INFLATE_FAST_MIN_HAVE && avail_out >= INFLATE_FAST_MIN_LEFT {
518
- inflate_fast_help ( self , 0 ) ;
518
+ // SAFETY: INFLATE_FAST_MIN_HAVE is enough bytes remaining to satisfy the precondition.
519
+ unsafe { inflate_fast_help ( self , 0 ) } ;
519
520
match self . mode {
520
521
Mode :: Len => { }
521
522
_ => return ControlFlow :: Continue ( ( ) ) ,
@@ -571,7 +572,10 @@ impl State<'_> {
571
572
// bytes
572
573
if avail_in >= INFLATE_FAST_MIN_HAVE && avail_out >= INFLATE_FAST_MIN_LEFT {
573
574
restore ! ( ) ;
574
- inflate_fast_help ( self , 0 ) ;
575
+ // SAFETY: INFLATE_FAST_MIN_HAVE >= 15.
576
+ // Note that the restore macro does not do anything that would
577
+ // reduce the number of bytes available.
578
+ unsafe { inflate_fast_help ( self , 0 ) } ;
575
579
return ControlFlow :: Continue ( ( ) ) ;
576
580
}
577
581
@@ -1817,32 +1821,52 @@ impl State<'_> {
1817
1821
}
1818
1822
}
1819
1823
1820
- fn inflate_fast_help ( state : & mut State , start : usize ) {
1824
+ /// # Safety
1825
+ ///
1826
+ /// `state.bit_reader` must have at least 15 bytes available to read, as
1827
+ /// indicated by `state.bit_reader.bytes_remaining() >= 15`
1828
+ unsafe fn inflate_fast_help ( state : & mut State , start : usize ) {
1821
1829
#[ cfg( any( target_arch = "x86_64" , target_arch = "x86" ) ) ]
1822
1830
if crate :: cpu_features:: is_enabled_avx2_and_bmi2 ( ) {
1823
- // SAFETY: we've verified the target features
1831
+ // SAFETY: we've verified the target features and the caller ensured enough bytes_remaining
1824
1832
return unsafe { inflate_fast_help_avx2 ( state, start) } ;
1825
1833
}
1826
1834
1827
- inflate_fast_help_vanilla ( state, start) ;
1835
+ // SAFETY: The caller ensured enough bytes_remaining
1836
+ unsafe { inflate_fast_help_vanilla ( state, start) } ;
1828
1837
}
1829
1838
1839
+ /// # Safety
1840
+ ///
1841
+ /// `state.bit_reader` must have at least 15 bytes available to read, as
1842
+ /// indicated by `state.bit_reader.bytes_remaining() >= 15`
1830
1843
#[ cfg( any( target_arch = "x86_64" , target_arch = "x86" ) ) ]
1831
1844
#[ target_feature( enable = "avx2" ) ]
1832
1845
#[ target_feature( enable = "bmi2" ) ]
1833
1846
#[ target_feature( enable = "bmi1" ) ]
1834
1847
unsafe fn inflate_fast_help_avx2 ( state : & mut State , start : usize ) {
1835
- inflate_fast_help_impl :: < { CpuFeatures :: AVX2 } > ( state, start) ;
1848
+ // SAFETY: `bytes_remaining` checked by our caller
1849
+ unsafe { inflate_fast_help_impl :: < { CpuFeatures :: AVX2 } > ( state, start) } ;
1836
1850
}
1837
1851
1838
- fn inflate_fast_help_vanilla ( state : & mut State , start : usize ) {
1839
- inflate_fast_help_impl :: < { CpuFeatures :: NONE } > ( state, start) ;
1852
+ /// # Safety
1853
+ ///
1854
+ /// `state.bit_reader` must have at least 15 bytes available to read, as
1855
+ /// indicated by `state.bit_reader.bytes_remaining() >= 15`
1856
+ unsafe fn inflate_fast_help_vanilla ( state : & mut State , start : usize ) {
1857
+ // SAFETY: `bytes_remaining` checked by our caller
1858
+ unsafe { inflate_fast_help_impl :: < { CpuFeatures :: NONE } > ( state, start) } ;
1840
1859
}
1841
1860
1861
+ /// # Safety
1862
+ ///
1863
+ /// `state.bit_reader` must have at least 15 bytes available to read, as
1864
+ /// indicated by `state.bit_reader.bytes_remaining() >= 15`
1842
1865
#[ inline( always) ]
1843
- fn inflate_fast_help_impl < const FEATURES : usize > ( state : & mut State , _start : usize ) {
1866
+ unsafe fn inflate_fast_help_impl < const FEATURES : usize > ( state : & mut State , _start : usize ) {
1844
1867
let mut bit_reader = BitReader :: new ( & [ ] ) ;
1845
1868
core:: mem:: swap ( & mut bit_reader, & mut state. bit_reader ) ;
1869
+ debug_assert ! ( bit_reader. bytes_remaining( ) >= 15 ) ;
1846
1870
1847
1871
let mut writer = Writer :: new ( & mut [ ] ) ;
1848
1872
core:: mem:: swap ( & mut writer, & mut state. writer ) ;
@@ -1862,15 +1886,40 @@ fn inflate_fast_help_impl<const FEATURES: usize>(state: &mut State, _start: usiz
1862
1886
let mut bad = None ;
1863
1887
1864
1888
if bit_reader. bits_in_buffer ( ) < 10 {
1865
- bit_reader. refill ( ) ;
1889
+ debug_assert ! ( bit_reader. bytes_remaining( ) >= 15 ) ;
1890
+ // Safety: Caller ensured that bit_reader has >= 15 bytes available; refill only needs 8.
1891
+ unsafe { bit_reader. refill ( ) } ;
1866
1892
}
1893
+ // We had at least 15 bytes in the slice, plus whatever was in the buffer. After filling the
1894
+ // buffer from the slice, we now have at least 8 bytes remaining in the slice, plus a full buffer.
1895
+ debug_assert ! (
1896
+ bit_reader. bytes_remaining( ) >= 8 && bit_reader. bytes_remaining_including_buffer( ) >= 15
1897
+ ) ;
1867
1898
1868
1899
' outer: loop {
1900
+ // This condition is ensured above for the first iteration of the `outer` loop. For
1901
+ // subsequent iterations, the loop continuation condition is
1902
+ // `bit_reader.bytes_remaining_including_buffer() > 15`. And because the buffer
1903
+ // contributes at most 7 bytes to the result of bit_reader.bytes_remaining_including_buffer(),
1904
+ // that means that the slice contains at least 8 bytes.
1905
+ debug_assert ! (
1906
+ bit_reader. bytes_remaining( ) >= 8
1907
+ && bit_reader. bytes_remaining_including_buffer( ) >= 15
1908
+ ) ;
1909
+
1869
1910
let mut here = {
1870
1911
let bits = bit_reader. bits_in_buffer ( ) ;
1871
1912
let hold = bit_reader. hold ( ) ;
1872
1913
1873
- bit_reader. refill ( ) ;
1914
+ // Safety: As described in the comments for the debug_assert at the start of
1915
+ // the `outer` loop, it is guaranteed that `bit_reader.bytes_remaining() >= 8` here,
1916
+ // which satisfies the safety precondition for `refill`. And, because the total
1917
+ // number of bytes in `bit_reader`'s buffer plus its slice is at least 15, and
1918
+ // `refill` moves at most 7 bytes from the slice to the buffer, the slice will still
1919
+ // contain at least 8 bytes after this `refill` call.
1920
+ unsafe { bit_reader. refill ( ) } ;
1921
+ // After the refill, there will be at least 8 bytes left in the bit_reader's slice.
1922
+ debug_assert ! ( bit_reader. bytes_remaining( ) >= 8 ) ;
1874
1923
1875
1924
// in most cases, the read can be interleaved with the logic
1876
1925
// based on benchmarks this matters in practice. wild.
@@ -1909,7 +1958,14 @@ fn inflate_fast_help_impl<const FEATURES: usize>(state: &mut State, _start: usiz
1909
1958
// we have two fast-path loads: 10+10 + 15+5 = 40,
1910
1959
// but we may need to refill here in the worst case
1911
1960
if bit_reader. bits_in_buffer ( ) < MAX_BITS + MAX_DIST_EXTRA_BITS {
1912
- bit_reader. refill ( ) ;
1961
+ debug_assert ! ( bit_reader. bytes_remaining( ) >= 8 ) ;
1962
+ // Safety: On the first iteration of the `dolen` loop, we can rely on the
1963
+ // invariant documented for the previous `refill` call above: after that
1964
+ // operation, `bit_reader.bytes_remining >= 8`, which satisfies the safety
1965
+ // precondition for this call. For subsequent iterations, this invariant
1966
+ // remains true because nothing else within the `dolen` loop consumes data
1967
+ // from the slice.
1968
+ unsafe { bit_reader. refill ( ) } ;
1913
1969
}
1914
1970
1915
1971
' dodist: loop {
0 commit comments