api-impl/android/net/Uri: copy encode/decode implementation from AOSP

This commit is contained in:
Mis012
2023-09-14 09:45:24 +02:00
parent 24353378be
commit 8500b57880

View File

@@ -1,6 +1,9 @@
package android.net;
import java.net.URI;
import libcore.net.UriCodec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
public class Uri {
private URI uri;
@@ -14,6 +17,124 @@ public class Uri {
return ret;
}
public static String decode(String s) {
if (s == null) {
return null;
}
return UriCodec.decode(s, false, StandardCharsets.UTF_8, false);
}
public static String encode(String s) {
return encode(s, null);
}
/**
* -- FROM ANDROID, Licensed under the Apache License, Version 2.0 --
* Returns true if the given character is allowed.
*
* @param c character to check
* @param allow characters to allow
* @return true if the character is allowed or false if it should be
* encoded
*/
private final static int NOT_FOUND = -1;
private static boolean isAllowed(char c, String allow) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || "_-!.~'()*".indexOf(c) != NOT_FOUND || (allow != null && allow.indexOf(c) != NOT_FOUND);
}
/**
* -- FROM ANDROID, Licensed under the Apache License, Version 2.0 --
* Encodes characters in the given string as '%'-escaped octets
* using the UTF-8 scheme. Leaves letters ("A-Z", "a-z"), numbers
* ("0-9"), and unreserved characters ("_-!.~'()*") intact. Encodes
* all other characters with the exception of those specified in the
* allow argument.
*
* @param s string to encode
* @param allow set of additional characters to allow in the encoded form,
* null if no characters should be skipped
* @return an encoded version of s suitable for use as a URI component,
* or null if s is null
*/
private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
private static final String DEFAULT_ENCODING = "UTF-8";
public static String encode(String s, String allow) {
if (s == null) {
return null;
}
// Lazily-initialized buffers.
StringBuilder encoded = null;
int oldLength = s.length();
// This loop alternates between copying over allowed characters and
// encoding in chunks. This results in fewer method calls and
// allocations than encoding one character at a time.
int current = 0;
while (current < oldLength) {
// Start in "copying" mode where we copy over allowed chars.
// Find the next character which needs to be encoded.
int nextToEncode = current;
while (nextToEncode < oldLength && isAllowed(s.charAt(nextToEncode), allow)) {
nextToEncode++;
}
// If there's nothing more to encode...
if (nextToEncode == oldLength) {
if (current == 0) {
// We didn't need to encode anything!
return s;
} else {
// Presumably, we've already done some encoding.
encoded.append(s, current, oldLength);
return encoded.toString();
}
}
if (encoded == null) {
encoded = new StringBuilder();
}
if (nextToEncode > current) {
// Append allowed characters leading up to this point.
encoded.append(s, current, nextToEncode);
} else {
// assert nextToEncode == current
}
// Switch to "encoding" mode.
// Find the next allowed character.
current = nextToEncode;
int nextAllowed = current + 1;
while (nextAllowed < oldLength && !isAllowed(s.charAt(nextAllowed), allow)) {
nextAllowed++;
}
// Convert the substring to bytes and encode the bytes as
// '%'-escaped octets.
String toEncode = s.substring(current, nextAllowed);
try {
byte[] bytes = toEncode.getBytes(DEFAULT_ENCODING);
int bytesLength = bytes.length;
for (int i = 0; i < bytesLength; i++) {
encoded.append('%');
encoded.append(HEX_DIGITS[(bytes[i] & 0xf0) >> 4]);
encoded.append(HEX_DIGITS[bytes[i] & 0xf]);
}
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
current = nextAllowed;
}
// Encoded could still be null at this point if s is empty.
return encoded == null ? s : encoded.toString();
}
public Builder buildUpon() {
return new Builder();
}