|
21 | 21 | */ |
22 | 22 | package com.github.packageurl; |
23 | 23 |
|
| 24 | +import java.io.ByteArrayOutputStream; |
24 | 25 | import java.io.Serializable; |
25 | 26 | import java.net.URI; |
26 | 27 | import java.net.URISyntaxException; |
@@ -444,22 +445,25 @@ private String percentEncode(final String input) { |
444 | 445 | } |
445 | 446 |
|
446 | 447 | private static String uriEncode(String source, Charset charset) { |
447 | | - if (source == null || source.length() == 0) { |
| 448 | + if (source == null || source.isEmpty()) { |
448 | 449 | return source; |
449 | 450 | } |
450 | 451 |
|
451 | | - StringBuilder builder = new StringBuilder(); |
452 | | - for (byte b : source.getBytes(charset)) { |
| 452 | + boolean changed = false; |
| 453 | + StringBuilder builder = new StringBuilder(source.length()); |
| 454 | + byte[] bytes = source.getBytes(charset); |
| 455 | + |
| 456 | + for (byte b : bytes) { |
453 | 457 | if (isUnreserved(b)) { |
454 | 458 | builder.append((char) b); |
455 | | - } |
456 | | - else { |
457 | | - // Substitution: A '%' followed by the hexadecimal representation of the ASCII value of the replaced character |
| 459 | + } else { |
458 | 460 | builder.append('%'); |
459 | | - builder.append(Integer.toHexString(b).toUpperCase()); |
| 461 | + builder.append(Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16))); |
| 462 | + builder.append(Character.toUpperCase(Character.forDigit(b & 0xF, 16))); |
| 463 | + changed = true; |
460 | 464 | } |
461 | 465 | } |
462 | | - return builder.toString(); |
| 466 | + return changed ? builder.toString() : source; |
463 | 467 | } |
464 | 468 |
|
465 | 469 | private static boolean isUnreserved(int c) { |
@@ -525,34 +529,37 @@ private static String toLowerCase(String s) { |
525 | 529 | * @return a decoded String |
526 | 530 | */ |
527 | 531 | private String percentDecode(final String input) { |
528 | | - if (input == null) { |
529 | | - return null; |
530 | | - } |
531 | | - final String decoded = uriDecode(input); |
532 | | - if (!decoded.equals(input)) { |
533 | | - return decoded; |
534 | | - } |
535 | | - return input; |
| 532 | + return uriDecode(input); |
536 | 533 | } |
537 | 534 |
|
538 | 535 | public static String uriDecode(String source) { |
539 | | - if (source == null) { |
| 536 | + if (source == null || source.isEmpty()) { |
540 | 537 | return source; |
541 | 538 | } |
542 | | - int length = source.length(); |
543 | | - StringBuilder builder = new StringBuilder(); |
| 539 | + |
| 540 | + boolean changed = false; |
| 541 | + byte[] bytes = source.getBytes(StandardCharsets.UTF_8); |
| 542 | + int length = bytes.length; |
| 543 | + ByteArrayOutputStream buffer = new ByteArrayOutputStream(length); |
| 544 | + |
544 | 545 | for (int i = 0; i < length; i++) { |
545 | | - if (source.charAt(i) == '%') { |
546 | | - String str = source.substring(i + 1, i + 3); |
547 | | - char c = (char) Integer.parseInt(str, 16); |
548 | | - builder.append(c); |
549 | | - i += 2; |
550 | | - } |
551 | | - else { |
552 | | - builder.append(source.charAt(i)); |
| 546 | + int b = bytes[i]; |
| 547 | + |
| 548 | + if (b == '%') { |
| 549 | + if (i + 2 >= length) { |
| 550 | + return null; |
| 551 | + } |
| 552 | + |
| 553 | + int b1 = Character.digit(bytes[++i], 16); |
| 554 | + int b2 = Character.digit(bytes[++i], 16); |
| 555 | + buffer.write((char) ((b1 << 4) + b2)); |
| 556 | + changed = true; |
| 557 | + } else { |
| 558 | + buffer.write(b); |
553 | 559 | } |
554 | 560 | } |
555 | | - return builder.toString(); |
| 561 | + |
| 562 | + return changed ? new String(buffer.toByteArray(), StandardCharsets.UTF_8) : source; |
556 | 563 | } |
557 | 564 |
|
558 | 565 | /** |
|
0 commit comments