private static Entry < String , Integer > getManifestValue ( final byte [ ] manifest , final int startIdx ) { // See if manifest entry is split across multiple lines int curr = startIdx ; final int len = manifest . length ; while ( curr < len && manifest [ curr ] == ( byte ) ' ' ) { // Skip initial spaces curr ++ ; } final int firstNonSpaceIdx = curr ; boolean isMultiLine = false ; for ( ; curr < len && ! isMultiLine ; curr ++ ) { final byte b = manifest [ curr ] ; if ( b == ( byte ) ' ' && curr < len - 1 && manifest [ curr + 1 ] == ( byte ) ' ' ) { if ( curr < len - 2 && manifest [ curr + 2 ] == ( byte ) ' ' ) { isMultiLine = true ; } break ; } else if ( b == ( byte ) ' ' || b == ( byte ) ' ' ) { if ( curr < len - 1 && manifest [ curr + 1 ] == ( byte ) ' ' ) { isMultiLine = true ; } break ; } } String val ; if ( ! isMultiLine ) { // Fast path for single-line value val = new String ( manifest , firstNonSpaceIdx , curr - firstNonSpaceIdx , StandardCharsets . UTF_8 ) ; } else { // Skip (newline + space) sequences in multi-line values final ByteArrayOutputStream buf = new ByteArrayOutputStream ( ) ; curr = firstNonSpaceIdx ; for ( ; curr < len ; curr ++ ) { final byte b = manifest [ curr ] ; boolean isLineEnd ; if ( b == ( byte ) ' ' && curr < len - 1 && manifest [ curr + 1 ] == ( byte ) ' ' ) { // CRLF curr += 2 ; isLineEnd = true ; } else if ( b == ' ' || b == ' ' ) { // CR or LF curr += 1 ; isLineEnd = true ; } else { buf . write ( b ) ; isLineEnd = false ; } if ( isLineEnd && curr < len && manifest [ curr ] != ( byte ) ' ' ) { // Value ends if line break is not followed by a space break ; } // If line break was followed by a space, then the curr++ in the for loop header will skip it } try { val = buf . toString ( "UTF-8" ) ; } catch ( final UnsupportedEncodingException e ) { // Should not happen throw ClassGraphException . newClassGraphException ( "UTF-8 encoding unsupported" , e ) ; } } return new SimpleEntry <> ( val . endsWith ( " " ) ? val . trim ( ) : val , curr ) ; }
Extract a value from the manifest and return the value as a string along with the index after the terminating newline . Manifest files support three different line terminator types and entries can be split across lines with a line terminator followed by a space .