|
37 | 37 |
|
38 | 38 | import java.io.UnsupportedEncodingException;
|
39 | 39 | import java.net.URLDecoder;
|
| 40 | +import java.nio.charset.StandardCharsets; |
40 | 41 | import java.util.Random;
|
41 | 42 | import java.util.concurrent.TimeUnit;
|
42 | 43 |
|
|
51 | 52 | @Fork(value = 3)
|
52 | 53 | public class URLEncodeDecode {
|
53 | 54 |
|
54 |
| - @Param("1024") |
55 |
| - public int count; |
| 55 | + private static final int COUNT = 1024; |
56 | 56 |
|
57 | 57 | @Param("1024")
|
58 | 58 | public int maxLength;
|
59 | 59 |
|
60 |
| - @Param("3") |
61 |
| - public long mySeed; |
| 60 | + /** |
| 61 | + * Percentage of strings that will remain unchanged by an encoding/decoding (0-100) |
| 62 | + */ |
| 63 | + @Param({"0", "75", "100"}) |
| 64 | + public int unchanged; |
| 65 | + |
| 66 | + /** |
| 67 | + * Percentage of chars in changed strings that cause encoding/decoding to happen (0-100) |
| 68 | + */ |
| 69 | + @Param({"6"}) |
| 70 | + public int encodeChars; |
62 | 71 |
|
63 | 72 | public String[] testStringsEncode;
|
64 | 73 | public String[] testStringsDecode;
|
65 | 74 | public String[] toStrings;
|
66 | 75 |
|
67 |
| - @Setup |
| 76 | + @Setup() |
68 | 77 | public void setupStrings() {
|
69 |
| - char[] tokens = new char[((int) 'Z' - (int) 'A' + 1) + ((int) 'z' - (int) 'a' + 1) + ((int) '9' - (int) '1' + 1) + 5]; |
| 78 | + char[] encodeTokens = new char[] { '[', '(', ' ', '\u00E4', '\u00E5', '\u00F6', ')', '='}; |
| 79 | + char[] tokens = new char[('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 1) + 4]; |
70 | 80 | int n = 0;
|
71 |
| - tokens[n++] = '0'; |
72 |
| - for (int i = (int) '1'; i <= (int) '9'; i++) { |
73 |
| - tokens[n++] = (char) i; |
| 81 | + for (char c = '0'; c <= '9'; c++) { |
| 82 | + tokens[n++] = c; |
74 | 83 | }
|
75 |
| - for (int i = (int) 'A'; i <= (int) 'Z'; i++) { |
76 |
| - tokens[n++] = (char) i; |
| 84 | + for (char c = 'A'; c <= 'Z'; c++) { |
| 85 | + tokens[n++] = c; |
77 | 86 | }
|
78 |
| - for (int i = (int) 'a'; i <= (int) '<'; i++) { |
79 |
| - tokens[n++] = (char) i; |
| 87 | + for (char c = 'a'; c <= 'z'; c++) { |
| 88 | + tokens[n++] = c; |
80 | 89 | }
|
81 | 90 | tokens[n++] = '-';
|
82 | 91 | tokens[n++] = '_';
|
83 | 92 | tokens[n++] = '.';
|
84 |
| - tokens[n++] = '*'; |
| 93 | + tokens[n] = '*'; |
85 | 94 |
|
86 |
| - Random r = new Random(mySeed); |
87 |
| - testStringsEncode = new String[count]; |
88 |
| - testStringsDecode = new String[count]; |
89 |
| - toStrings = new String[count]; |
90 |
| - for (int i = 0; i < count; i++) { |
| 95 | + Random r = new Random(3); |
| 96 | + testStringsEncode = new String[COUNT]; |
| 97 | + testStringsDecode = new String[COUNT]; |
| 98 | + toStrings = new String[COUNT]; |
| 99 | + for (int i = 0; i < COUNT; i++) { |
91 | 100 | int l = r.nextInt(maxLength);
|
| 101 | + boolean needEncoding = r.nextInt(100) >= unchanged; |
92 | 102 | StringBuilder sb = new StringBuilder();
|
| 103 | + boolean hasEncoded = false; |
93 | 104 | for (int j = 0; j < l; j++) {
|
94 |
| - int c = r.nextInt(tokens.length); |
95 |
| - sb.append(tokens[c]); |
| 105 | + if (needEncoding && r.nextInt(100) < encodeChars) { |
| 106 | + addToken(encodeTokens, r, sb); |
| 107 | + hasEncoded = true; |
| 108 | + } else { |
| 109 | + addToken(tokens, r, sb); |
| 110 | + } |
| 111 | + } |
| 112 | + if (needEncoding && !hasEncoded) { |
| 113 | + addToken(encodeTokens, r, sb); |
96 | 114 | }
|
97 | 115 | testStringsEncode[i] = sb.toString();
|
98 | 116 | }
|
| 117 | + int countUnchanged = 0; |
| 118 | + for (String s : testStringsEncode) { |
| 119 | + if (s.equals(java.net.URLEncoder.encode(s, StandardCharsets.UTF_8))) { |
| 120 | + countUnchanged++; |
| 121 | + } else { |
| 122 | + if (unchanged == 100) { |
| 123 | + System.out.println("Unexpectedly needs encoding action: "); |
| 124 | + System.out.println("\t" + s); |
| 125 | + System.out.println("\t" + java.net.URLEncoder.encode(s, StandardCharsets.UTF_8)); |
| 126 | + } |
| 127 | + } |
| 128 | + } |
| 129 | + System.out.println(); |
| 130 | + System.out.println("Generated " + testStringsEncode.length + " encodable strings, " + countUnchanged + " of which does not need encoding action"); |
99 | 131 |
|
100 |
| - for (int i = 0; i < count; i++) { |
| 132 | + for (int i = 0; i < COUNT; i++) { |
101 | 133 | int l = r.nextInt(maxLength);
|
| 134 | + boolean needDecoding = r.nextInt(100) >= unchanged; |
102 | 135 | StringBuilder sb = new StringBuilder();
|
| 136 | + boolean hasDecoded = false; |
103 | 137 | for (int j = 0; j < l; j++) {
|
104 |
| - int c = r.nextInt(tokens.length + 5); |
105 |
| - if (c >= tokens.length) { |
106 |
| - sb.append("%").append(tokens[r.nextInt(16)]).append(tokens[r.nextInt(16)]); |
| 138 | + if (needDecoding && r.nextInt(100) < encodeChars) { |
| 139 | + addDecodableChar(tokens, r, sb); |
| 140 | + hasDecoded = true; |
107 | 141 | } else {
|
108 |
| - sb.append(tokens[c]); |
| 142 | + addToken(tokens, r, sb); |
109 | 143 | }
|
110 | 144 | }
|
| 145 | + if (needDecoding && !hasDecoded) { |
| 146 | + addDecodableChar(tokens, r, sb); |
| 147 | + } |
111 | 148 | testStringsDecode[i] = sb.toString();
|
112 | 149 | }
|
| 150 | + countUnchanged = 0; |
| 151 | + for (String s : testStringsDecode) { |
| 152 | + if (s.equals(java.net.URLDecoder.decode(s, StandardCharsets.UTF_8))) { |
| 153 | + countUnchanged++; |
| 154 | + } else { |
| 155 | + if (unchanged == 100) { |
| 156 | + System.out.println("Unexpectedly needs encoding action: "); |
| 157 | + System.out.println("\t" + s); |
| 158 | + System.out.println("\t" + java.net.URLDecoder.decode(s, StandardCharsets.UTF_8)); |
| 159 | + } |
| 160 | + } |
| 161 | + } |
| 162 | + System.out.println("Generated " + testStringsDecode.length + " decodable strings, " + countUnchanged + " of which does not need decoding action"); |
| 163 | + } |
| 164 | + |
| 165 | + private static void addToken(char[] tokens, Random r, StringBuilder sb) { |
| 166 | + int c = r.nextInt(tokens.length); |
| 167 | + sb.append(tokens[c]); |
| 168 | + } |
| 169 | + |
| 170 | + private static void addDecodableChar(char[] tokens, Random r, StringBuilder sb) { |
| 171 | + if (r.nextInt(100) < 15) { |
| 172 | + sb.append('+'); // exercise '+' -> ' ' decoding paths. |
| 173 | + } else { |
| 174 | + sb.append("%").append(tokens[r.nextInt(16)]).append(tokens[r.nextInt(16)]); |
| 175 | + } |
113 | 176 | }
|
114 | 177 |
|
115 | 178 | @Benchmark
|
116 | 179 | public void testEncodeUTF8(Blackhole bh) throws UnsupportedEncodingException {
|
117 | 180 | for (String s : testStringsEncode) {
|
118 |
| - bh.consume(java.net.URLEncoder.encode(s, "UTF-8")); |
| 181 | + bh.consume(java.net.URLEncoder.encode(s, StandardCharsets.UTF_8)); |
119 | 182 | }
|
120 | 183 | }
|
121 | 184 |
|
122 | 185 | @Benchmark
|
123 | 186 | public void testDecodeUTF8(Blackhole bh) throws UnsupportedEncodingException {
|
124 | 187 | for (String s : testStringsDecode) {
|
125 |
| - bh.consume(URLDecoder.decode(s, "UTF-8")); |
| 188 | + bh.consume(URLDecoder.decode(s, StandardCharsets.UTF_8)); |
126 | 189 | }
|
127 | 190 | }
|
128 | 191 |
|
|
0 commit comments