@@ -151,6 +151,10 @@ pub struct Pos {
151151// line ranges for empty character ranges in the middle of a line...
152152impl < C : ConfigType > StringOffsets < C > {
153153 /// Create a new converter to work with offsets into the given string.
154+ ///
155+ /// # Panics
156+ ///
157+ /// Panics if `content.len() >= u32::MAX as usize`.
154158 pub fn new ( content : & str ) -> Self {
155159 new_converter ( content. as_bytes ( ) )
156160 }
@@ -159,6 +163,10 @@ impl<C: ConfigType> StringOffsets<C> {
159163 ///
160164 /// If `content` is UTF-8, this is just like [`StringOffsets::new`]. Otherwise, the
161165 /// conversion methods will produce unspecified (but memory-safe) results.
166+ ///
167+ /// # Panics
168+ ///
169+ /// Panics if `content.len() >= u32::MAX as usize`.
162170 pub fn from_bytes ( content : & [ u8 ] ) -> Self {
163171 new_converter ( content)
164172 }
@@ -190,7 +198,7 @@ impl<C: ConfigType<HasLines = True>> StringOffsets<C> {
190198
191199 /// UTF-8 offset of the first character of a line.
192200 pub fn line_to_utf8_end ( & self , line_number : usize ) -> usize {
193- self . line_to_utf8_begin ( line_number + 1 )
201+ self . line_to_utf8_begin ( line_number. saturating_add ( 1 ) )
194202 }
195203
196204 /// Return the zero-based line number of the line containing the specified UTF-8 offset.
@@ -288,7 +296,9 @@ impl<C: ConfigType<HasWhitespace = True>> StringOffsets<C> {
288296impl < C : ConfigType < HasChars = True > > StringOffsets < C > {
289297 /// Converts a UTF-8 offset to a UTF-32 offset.
290298 pub fn utf8_to_char ( & self , byte_number : usize ) -> usize {
291- self . utf8_to_char . rank ( byte_number + 1 ) - 1
299+ self . utf8_to_char
300+ . rank ( byte_number. saturating_add ( 1 ) )
301+ . saturating_sub ( 1 )
292302 }
293303
294304 /// Converts a UTF-32 offset to a UTF-8 offset.
@@ -369,8 +379,17 @@ impl<C: ConfigType<HasChars = True, HasLines = True, HasUtf16 = True>> StringOff
369379 }
370380}
371381
382+ /// Creates a [`StringOffsets`] converter for `content`.
383+ ///
384+ /// # Panics
385+ ///
386+ /// Panics if `content.len() >= u32::MAX as usize`.
372387fn new_converter < C : ConfigType > ( content : & [ u8 ] ) -> StringOffsets < C > {
373388 let n = content. len ( ) ;
389+ assert ! (
390+ n < u32 :: MAX as usize ,
391+ "content length + 1 must fit in u32 (got {n})"
392+ ) ;
374393 let mut utf8_builder =
375394 BitRankBuilder :: with_capacity ( if C :: HasChars :: VALUE { n + 1 } else { 0 } ) ;
376395 let mut utf16_builder = BitRankBuilder :: with_capacity ( if C :: HasUtf16 :: VALUE { n } else { 0 } ) ;
@@ -649,4 +668,16 @@ line1
649668 let lines: StringOffsets = StringOffsets :: from_bytes ( & content) ;
650669 assert_eq ! ( lines. utf8_to_utf16_pos( 16384 ) , pos( 1 , 0 ) ) ;
651670 }
671+
672+ #[ test]
673+ fn test_saturating_usize_max_offsets ( ) {
674+ let content = "hello\n world" ;
675+ let offsets: StringOffsets = StringOffsets :: new ( content) ;
676+ assert_eq ! ( offsets. utf8_to_char( usize :: MAX ) , content. chars( ) . count( ) ) ;
677+ assert_eq ! (
678+ offsets. utf8_to_utf16( usize :: MAX ) ,
679+ content. encode_utf16( ) . count( )
680+ ) ;
681+ assert_eq ! ( offsets. line_to_utf8_end( usize :: MAX ) , content. len( ) ) ;
682+ }
652683}
0 commit comments