1
16 package okio;
17
18 import java.util.AbstractList;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.RandomAccess;
24
25
26 public final class Options extends AbstractList<ByteString> implements RandomAccess {
27 final ByteString[] byteStrings;
28 final int[] trie;
29
30 private Options(ByteString[] byteStrings, int[] trie) {
31 this.byteStrings = byteStrings;
32 this.trie = trie;
33 }
34
35 public static Options of(ByteString... byteStrings) {
36 if (byteStrings.length == 0) {
37
38 return new Options(new ByteString[0], new int[] { 0, -1 });
39 }
40
41
42
43 List<ByteString> list = new ArrayList<>(Arrays.asList(byteStrings));
44 Collections.sort(list);
45 List<Integer> indexes = new ArrayList<>();
46 for (int i = 0; i < list.size(); i++) {
47 indexes.add(-1);
48 }
49 for (int i = 0; i < list.size(); i++) {
50 int sortedIndex = Collections.binarySearch(list, byteStrings[i]);
51 indexes.set(sortedIndex, i);
52 }
53 if (list.get(0).size() == 0) {
54 throw new IllegalArgumentException("the empty byte string is not a supported option");
55 }
56
57
58
59
60 for (int a = 0; a < list.size(); a++) {
61 ByteString prefix = list.get(a);
62 for (int b = a + 1; b < list.size(); ) {
63 ByteString byteString = list.get(b);
64 if (!byteString.startsWith(prefix)) break;
65 if (byteString.size() == prefix.size()) {
66 throw new IllegalArgumentException("duplicate option: " + byteString);
67 }
68 if (indexes.get(b) > indexes.get(a)) {
69 list.remove(b);
70 indexes.remove(b);
71 } else {
72 b++;
73 }
74 }
75 }
76
77 Buffer trieBytes = new Buffer();
78 buildTrieRecursive(0L, trieBytes, 0, list, 0, list.size(), indexes);
79
80 int[] trie = new int[intCount(trieBytes)];
81 for (int i = 0; i < trie.length; i++) {
82 trie[i] = trieBytes.readInt();
83 }
84 if (!trieBytes.exhausted()) {
85 throw new AssertionError();
86 }
87
88 return new Options(byteStrings.clone() , trie);
89 }
90
91
115 private static void buildTrieRecursive(
116 long nodeOffset,
117 Buffer node,
118 int byteStringOffset,
119 List<ByteString> byteStrings,
120 int fromIndex,
121 int toIndex,
122 List<Integer> indexes) {
123 if (fromIndex >= toIndex) throw new AssertionError();
124 for (int i = fromIndex; i < toIndex; i++) {
125 if (byteStrings.get(i).size() < byteStringOffset) throw new AssertionError();
126 }
127
128 ByteString from = byteStrings.get(fromIndex);
129 ByteString to = byteStrings.get(toIndex - 1);
130 int prefixIndex = -1;
131
132
133 if (byteStringOffset == from.size()) {
134 prefixIndex = indexes.get(fromIndex);
135 fromIndex++;
136 from = byteStrings.get(fromIndex);
137 }
138
139 if (from.getByte(byteStringOffset) != to.getByte(byteStringOffset)) {
140
141 int selectChoiceCount = 1;
142 for (int i = fromIndex + 1; i < toIndex; i++) {
143 if (byteStrings.get(i - 1).getByte(byteStringOffset)
144 != byteStrings.get(i).getByte(byteStringOffset)) {
145 selectChoiceCount++;
146 }
147 }
148
149
150 long childNodesOffset = nodeOffset + intCount(node) + 2 + (selectChoiceCount * 2);
151
152 node.writeInt(selectChoiceCount);
153 node.writeInt(prefixIndex);
154
155 for (int i = fromIndex; i < toIndex; i++) {
156 byte rangeByte = byteStrings.get(i).getByte(byteStringOffset);
157 if (i == fromIndex || rangeByte != byteStrings.get(i - 1).getByte(byteStringOffset)) {
158 node.writeInt(rangeByte & 0xff);
159 }
160 }
161
162 Buffer childNodes = new Buffer();
163 int rangeStart = fromIndex;
164 while (rangeStart < toIndex) {
165 byte rangeByte = byteStrings.get(rangeStart).getByte(byteStringOffset);
166 int rangeEnd = toIndex;
167 for (int i = rangeStart + 1; i < toIndex; i++) {
168 if (rangeByte != byteStrings.get(i).getByte(byteStringOffset)) {
169 rangeEnd = i;
170 break;
171 }
172 }
173
174 if (rangeStart + 1 == rangeEnd
175 && byteStringOffset + 1 == byteStrings.get(rangeStart).size()) {
176
177 node.writeInt(indexes.get(rangeStart));
178 } else {
179
180 node.writeInt((int) (-1 * (childNodesOffset + intCount(childNodes))));
181 buildTrieRecursive(
182 childNodesOffset,
183 childNodes,
184 byteStringOffset + 1,
185 byteStrings,
186 rangeStart,
187 rangeEnd,
188 indexes);
189 }
190
191 rangeStart = rangeEnd;
192 }
193
194 node.write(childNodes, childNodes.size());
195
196 } else {
197
198 int scanByteCount = 0;
199 for (int i = byteStringOffset, max = Math.min(from.size(), to.size()); i < max; i++) {
200 if (from.getByte(i) == to.getByte(i)) {
201 scanByteCount++;
202 } else {
203 break;
204 }
205 }
206
207
208 long childNodesOffset = nodeOffset + intCount(node) + 2 + scanByteCount + 1;
209
210 node.writeInt(-scanByteCount);
211 node.writeInt(prefixIndex);
212
213 for (int i = byteStringOffset; i < byteStringOffset + scanByteCount; i++) {
214 node.writeInt(from.getByte(i) & 0xff);
215 }
216
217 if (fromIndex + 1 == toIndex) {
218
219 if (byteStringOffset + scanByteCount != byteStrings.get(fromIndex).size()) {
220 throw new AssertionError();
221 }
222 node.writeInt(indexes.get(fromIndex));
223 } else {
224
225 Buffer childNodes = new Buffer();
226 node.writeInt((int) (-1 * (childNodesOffset + intCount(childNodes))));
227 buildTrieRecursive(
228 childNodesOffset,
229 childNodes,
230 byteStringOffset + scanByteCount,
231 byteStrings,
232 fromIndex,
233 toIndex,
234 indexes);
235 node.write(childNodes, childNodes.size());
236 }
237 }
238 }
239
240 @Override public ByteString get(int i) {
241 return byteStrings[i];
242 }
243
244 @Override public final int size() {
245 return byteStrings.length;
246 }
247
248 private static int intCount(Buffer trieBytes) {
249 return (int) (trieBytes.size() / 4);
250 }
251 }
252