PercentEncoding.java
package com.shengzhiai.yugu.internal;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
/** RFC 3986 percent encoding: everything except unreserved characters is encoded. */
public final class PercentEncoding {
private static final char[] HEX = "0123456789ABCDEF".toCharArray();
private PercentEncoding() {
}
/**
* @param s text
* @return encoded text, safe for a path segment or a query component
*/
public static String encode(String s) {
byte[] b = s.getBytes(StandardCharsets.UTF_8);
StringBuilder sb = new StringBuilder(b.length * 3 / 2);
for (byte x : b) {
int c = x & 0xFF;
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')
|| c == '-' || c == '.' || c == '_' || c == '~') {
sb.append((char) c);
} else {
sb.append('%').append(HEX[c >> 4]).append(HEX[c & 0xF]);
}
}
return sb.toString();
}
/**
* @param s encoded text; {@code +} stays a plus sign
* @return decoded text
*/
public static String decode(String s) {
ByteArrayOutputStream out = new ByteArrayOutputStream(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '%' && i + 2 < s.length()) {
int hi = Character.digit(s.charAt(i + 1), 16);
int lo = Character.digit(s.charAt(i + 2), 16);
if (hi >= 0 && lo >= 0) {
out.write(hi * 16 + lo);
i += 2;
continue;
}
}
int cp = s.codePointAt(i);
byte[] b = new String(Character.toChars(cp)).getBytes(StandardCharsets.UTF_8);
out.write(b, 0, b.length);
i += Character.charCount(cp) - 1;
}
return new String(out.toByteArray(), StandardCharsets.UTF_8);
}
}