1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.resolver.dns;
17
18 import io.netty.util.internal.MathUtil;
19 import io.netty.util.internal.PlatformDependent;
20
21 import java.util.Random;
22
23
24
25
26
27
28 final class DnsQueryIdSpace {
29 private static final int MAX_ID = 65535;
30 private static final int BUCKETS = 4;
31
32 private static final int BUCKET_SIZE = (MAX_ID + 1) / BUCKETS;
33
34
35 private static final int BUCKET_DROP_THRESHOLD = 500;
36 private final DnsQueryIdRange[] idBuckets = new DnsQueryIdRange[BUCKETS];
37
38 DnsQueryIdSpace() {
39 assert idBuckets.length == MathUtil.findNextPositivePowerOfTwo(idBuckets.length);
40
41 idBuckets[0] = newBucket(0);
42 }
43
44 private static DnsQueryIdRange newBucket(int idBucketsIdx) {
45 return new DnsQueryIdRange(BUCKET_SIZE, idBucketsIdx * BUCKET_SIZE);
46 }
47
48
49
50
51
52
53 int nextId() {
54 int freeIdx = -1;
55 for (int bucketIdx = 0; bucketIdx < idBuckets.length; bucketIdx++) {
56 DnsQueryIdRange bucket = idBuckets[bucketIdx];
57 if (bucket != null) {
58 int id = bucket.nextId();
59 if (id != -1) {
60 return id;
61 }
62 } else if (freeIdx == -1 ||
63
64 PlatformDependent.threadLocalRandom().nextBoolean()) {
65
66 freeIdx = bucketIdx;
67 }
68 }
69 if (freeIdx == -1) {
70
71 return -1;
72 }
73
74
75 DnsQueryIdRange bucket = newBucket(freeIdx);
76 idBuckets[freeIdx] = bucket;
77 int id = bucket.nextId();
78 assert id >= 0;
79 return id;
80 }
81
82
83
84
85
86
87 void pushId(int id) {
88 int bucketIdx = id / BUCKET_SIZE;
89 if (bucketIdx >= idBuckets.length) {
90 throw new IllegalArgumentException("id too large: " + id);
91 }
92 DnsQueryIdRange bucket = idBuckets[bucketIdx];
93 assert bucket != null;
94 bucket.pushId(id);
95
96 if (bucket.usableIds() == bucket.maxUsableIds()) {
97
98
99 for (int idx = 0; idx < idBuckets.length; idx++) {
100 if (idx != bucketIdx) {
101 DnsQueryIdRange otherBucket = idBuckets[idx];
102 if (otherBucket != null && otherBucket.usableIds() > BUCKET_DROP_THRESHOLD) {
103
104
105 idBuckets[bucketIdx] = null;
106 return;
107 }
108 }
109 }
110 }
111 }
112
113
114
115
116
117
118 int usableIds() {
119 int usableIds = 0;
120 for (DnsQueryIdRange bucket: idBuckets) {
121
122 usableIds += bucket == null ? BUCKET_SIZE : bucket.usableIds();
123 }
124 return usableIds;
125 }
126
127
128
129
130
131
132 int maxUsableIds() {
133 return BUCKET_SIZE * idBuckets.length;
134 }
135
136
137
138
139 private static final class DnsQueryIdRange {
140
141
142 private final short[] ids;
143 private final int startId;
144 private int count;
145
146 DnsQueryIdRange(int bucketSize, int startId) {
147 this.ids = new short[bucketSize];
148 this.startId = startId;
149 for (int v = startId; v < bucketSize + startId; v++) {
150 pushId(v);
151 }
152 }
153
154
155
156
157
158
159 int nextId() {
160 assert count >= 0;
161 if (count == 0) {
162 return -1;
163 }
164 short id = ids[count - 1];
165 count--;
166
167 return id & 0xFFFF;
168 }
169
170
171
172
173
174
175 void pushId(int id) {
176 if (count == ids.length) {
177 throw new IllegalStateException("overflow");
178 }
179 assert id <= startId + ids.length && id >= startId;
180
181 Random random = PlatformDependent.threadLocalRandom();
182 int insertionPosition = random.nextInt(count + 1);
183 ids[count] = ids[insertionPosition];
184 ids[insertionPosition] = (short) id;
185 count++;
186 }
187
188
189
190
191
192
193 int usableIds() {
194 return count;
195 }
196
197
198
199
200
201
202 int maxUsableIds() {
203 return ids.length;
204 }
205 }
206 }