2121 */
2222package com .github .packageurl ;
2323
24+ import java .io .ByteArrayOutputStream ;
2425import java .io .Serializable ;
2526import java .net .URI ;
2627import java .net .URISyntaxException ;
@@ -441,22 +442,26 @@ private String percentEncode(final String input) {
441442 }
442443
443444 private static String uriEncode (String source , Charset charset ) {
444- if (source == null || source .length () == 0 ) {
445+ if (source == null || source .isEmpty () ) {
445446 return source ;
446447 }
447448
449+ boolean changed = false ;
448450 StringBuilder builder = new StringBuilder ();
449- for (byte b : source .getBytes (charset )) {
451+ byte [] bytes = source .getBytes (charset );
452+
453+ for (byte b : bytes ) {
450454 if (isUnreserved (b )) {
451455 builder .append ((char ) b );
452456 }
453457 else {
454- // Substitution: A '%' followed by the hexadecimal representation of the ASCII value of the replaced character
455458 builder .append ('%' );
456- builder .append (Integer .toHexString (b ).toUpperCase ());
459+ builder .append (Character .toUpperCase (Character .forDigit ((b >> 4 ) & 0xF , 16 )));
460+ builder .append (Character .toUpperCase (Character .forDigit (b & 0xF , 16 )));
461+ changed = true ;
457462 }
458463 }
459- return builder .toString ();
464+ return changed ? builder .toString () : source ;
460465 }
461466
462467 private static boolean isUnreserved (int c ) {
@@ -479,34 +484,37 @@ private static boolean isDigit(int c) {
479484 * @return a decoded String
480485 */
481486 private String percentDecode (final String input ) {
482- if (input == null ) {
483- return null ;
484- }
485- final String decoded = uriDecode (input );
486- if (!decoded .equals (input )) {
487- return decoded ;
488- }
489- return input ;
487+ return uriDecode (input );
490488 }
491489
492490 public static String uriDecode (String source ) {
493491 if (source == null ) {
494- return source ;
492+ return null ;
495493 }
496- int length = source .length ();
497- StringBuilder builder = new StringBuilder ();
498- for (int i = 0 ; i < length ; i ++) {
499- if (source .charAt (i ) == '%' ) {
500- String str = source .substring (i + 1 , i + 3 );
501- char c = (char ) Integer .parseInt (str , 16 );
502- builder .append (c );
503- i += 2 ;
504- }
505- else {
506- builder .append (source .charAt (i ));
494+
495+ boolean changed = false ;
496+ byte [] bytes = source .getBytes (StandardCharsets .UTF_8 );
497+ ByteArrayOutputStream buffer = new ByteArrayOutputStream (bytes .length );
498+
499+ for (int i = 0 ; i < bytes .length ; i ++) {
500+ int b = bytes [i ];
501+
502+ if (b == '%' ) {
503+ if (i + 2 >= bytes .length ) {
504+ return source ;
505+ }
506+
507+ int b1 = Character .digit (bytes [++i ], 16 );
508+ int b2 = Character .digit (bytes [++i ], 16 );
509+ buffer .write ((char ) ((b1 << 4 ) + b2 ));
510+ changed = true ;
511+ } else {
512+ buffer .write (b );
507513 }
508514 }
509- return builder .toString ();
515+
516+ byte [] b = buffer .toByteArray ();
517+ return changed ? new String (b , StandardCharsets .UTF_8 ) : source ;
510518 }
511519
512520 /**
0 commit comments