1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.buffer;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.buffer.api.BufferAllocator;
20 import io.netty5.buffer.api.DefaultBufferAllocators;
21 import io.netty5.buffer.api.internal.Statics;
22 import io.netty5.util.concurrent.FastThreadLocal;
23 import io.netty5.util.internal.PlatformDependent;
24 import io.netty5.util.internal.StringUtil;
25 import io.netty5.util.internal.SystemPropertyUtil;
26 import io.netty5.util.internal.logging.InternalLogger;
27 import io.netty5.util.internal.logging.InternalLoggerFactory;
28
29 import java.nio.charset.StandardCharsets;
30
31 import static io.netty5.util.internal.MathUtil.isOutOfBounds;
32 import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero;
33 import static io.netty5.util.internal.StringUtil.NEWLINE;
34
35
36
37
38
39 public final class BufferUtil {
40
41 private static final InternalLogger logger = InternalLoggerFactory.getInstance(BufferUtil.class);
42
43 private static final int MAX_CHAR_BUFFER_SIZE;
44 private static final int THREAD_LOCAL_BUFFER_SIZE;
45
46 static {
47 THREAD_LOCAL_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty5.threadLocalDirectBufferSize", 0);
48 logger.debug("-Dio.netty5.threadLocalDirectBufferSize: {}", THREAD_LOCAL_BUFFER_SIZE);
49
50 MAX_CHAR_BUFFER_SIZE = SystemPropertyUtil.getInt("io.netty5.maxThreadLocalCharBufferSize", 16 * 1024);
51 logger.debug("-Dio.netty5.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE);
52 }
53
54
55
56
57
58
59 public static int reverseUnsignedShort(int value) {
60 return Integer.reverseBytes(value) >>> Short.SIZE;
61 }
62
63
64
65
66
67
68 public static int reverseMedium(int value) {
69 return Integer.reverseBytes(value) >> Byte.SIZE;
70 }
71
72
73
74
75
76
77 public static int reverseUnsignedMedium(int value) {
78 return Integer.reverseBytes(value) >>> Byte.SIZE;
79 }
80
81
82
83
84
85
86 public static long reverseUnsignedInt(long value) {
87 return Long.reverseBytes(value) >>> Integer.SIZE;
88 }
89
90
91
92
93
94 public static String hexDump(Buffer buffer) {
95 return hexDump(buffer, buffer.readerOffset(), buffer.readableBytes());
96 }
97
98
99
100
101
102 public static String hexDump(Buffer buffer, int fromIndex, int length) {
103 return HexUtil.hexDump(buffer, fromIndex, length);
104 }
105
106
107
108
109
110 public static String hexDump(byte[] array) {
111 return hexDump(array, 0, array.length);
112 }
113
114
115
116
117
118 public static String hexDump(byte[] array, int fromIndex, int length) {
119 return HexUtil.hexDump(array, fromIndex, length);
120 }
121
122
123
124
125
126
127
128
129
130 public static Buffer writeAscii(BufferAllocator alloc, CharSequence seq) {
131 return alloc.copyOf(seq.toString(), StandardCharsets.US_ASCII);
132 }
133
134
135
136
137
138 public static byte[] getBytes(Buffer buf) {
139 return getBytes(buf, buf.readerOffset(), buf.readableBytes());
140 }
141
142
143
144
145
146 public static byte[] getBytes(Buffer buf, int start, int length) {
147 int capacity = buf.capacity();
148 if (isOutOfBounds(start, length, capacity)) {
149 throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
150 + ") <= " + "buf.capacity(" + capacity + ')');
151 }
152
153 byte[] bytes = PlatformDependent.allocateUninitializedArray(length);
154 buf.copyInto(start, bytes, 0, length);
155 return bytes;
156 }
157
158
159
160
161
162
163
164
165
166 public static boolean equals(Buffer first, int firstReaderOffset, Buffer second, int secondReaderOffset,
167 int length) {
168 return Statics.equals(first, firstReaderOffset, second, secondReaderOffset, length);
169 }
170
171
172
173
174
175 public static void appendPrettyHexDump(StringBuilder dump, Buffer buf) {
176 appendPrettyHexDump(dump, buf, buf.readerOffset(), buf.readableBytes());
177 }
178
179
180
181
182
183
184 public static void appendPrettyHexDump(StringBuilder dump, Buffer buf, int offset, int length) {
185 HexUtil.appendPrettyHexDump(dump, buf, offset, length);
186 }
187
188
189
190
191
192
193 public static Buffer threadLocalDirectBuffer() {
194 if (THREAD_LOCAL_BUFFER_SIZE <= 0) {
195 return null;
196 }
197
198 return ThreadLocalDirectBufferHolder.BUFFER.get();
199 }
200
201
202 private static final class HexUtil {
203
204 private static final char[] BYTE2CHAR = new char[256];
205 private static final char[] HEXDUMP_TABLE = new char[256 * 4];
206 private static final String[] HEXPADDING = new String[16];
207 private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];
208 private static final String[] BYTE2HEX = new String[256];
209 private static final String[] BYTEPADDING = new String[16];
210
211 static {
212 final char[] DIGITS = "0123456789abcdef".toCharArray();
213 for (int i = 0; i < 256; i++) {
214 HEXDUMP_TABLE[i << 1] = DIGITS[i >>> 4 & 0x0F];
215 HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i & 0x0F];
216 }
217
218 int i;
219
220
221 for (i = 0; i < HEXPADDING.length; i++) {
222 int padding = HEXPADDING.length - i;
223 HEXPADDING[i] = " ".repeat(padding);
224 }
225
226
227 for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i++) {
228 StringBuilder buf = new StringBuilder(12);
229 buf.append(NEWLINE);
230 buf.append(Long.toHexString(i << 4L & 0xFFFFFFFFL | 0x100000000L));
231 buf.setCharAt(buf.length() - 9, '|');
232 buf.append('|');
233 HEXDUMP_ROWPREFIXES[i] = buf.toString();
234 }
235
236
237 for (i = 0; i < BYTE2HEX.length; i++) {
238 BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);
239 }
240
241
242 for (i = 0; i < BYTEPADDING.length; i++) {
243 int padding = BYTEPADDING.length - i;
244 BYTEPADDING[i] = " ".repeat(padding);
245 }
246
247
248 for (i = 0; i < BYTE2CHAR.length; i++) {
249 if (i <= 0x1f || i >= 0x7f) {
250 BYTE2CHAR[i] = '.';
251 } else {
252 BYTE2CHAR[i] = (char) i;
253 }
254 }
255 }
256
257 private static String hexDump(Buffer buffer, int fromIndex, int length) {
258 checkPositiveOrZero(length, "length");
259 if (length == 0) {
260 return "";
261 }
262
263 int endIndex = fromIndex + length;
264 char[] buf = new char[length << 1];
265
266 int srcIdx = fromIndex;
267 int dstIdx = 0;
268 for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
269 System.arraycopy(
270 HEXDUMP_TABLE, buffer.getUnsignedByte(srcIdx) << 1,
271 buf, dstIdx, 2);
272 }
273
274 return new String(buf);
275 }
276
277 private static String hexDump(byte[] array, int fromIndex, int length) {
278 checkPositiveOrZero(length, "length");
279 if (length == 0) {
280 return "";
281 }
282
283 int endIndex = fromIndex + length;
284 char[] buf = new char[length << 1];
285
286 int srcIdx = fromIndex;
287 int dstIdx = 0;
288 for (; srcIdx < endIndex; srcIdx ++, dstIdx += 2) {
289 System.arraycopy(
290 HEXDUMP_TABLE, (array[srcIdx] & 0xFF) << 1,
291 buf, dstIdx, 2);
292 }
293
294 return new String(buf);
295 }
296
297 private static void appendPrettyHexDump(StringBuilder dump, Buffer buf, int offset, int length) {
298 if (isOutOfBounds(offset, length, buf.capacity())) {
299 throw new IndexOutOfBoundsException(
300 "expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length
301 + ") <= " + "buf.capacity(" + buf.capacity() + ')');
302 }
303 if (length == 0) {
304 return;
305 }
306 dump.append(
307 " +-------------------------------------------------+" +
308 NEWLINE + " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |" +
309 NEWLINE + "+--------+-------------------------------------------------+----------------+");
310
311 final int fullRows = length >>> 4;
312 final int remainder = length & 0xF;
313
314
315 for (int row = 0; row < fullRows; row ++) {
316 int rowStartIndex = (row << 4) + offset;
317
318
319 appendHexDumpRowPrefix(dump, row, rowStartIndex);
320
321
322 int rowEndIndex = rowStartIndex + 16;
323 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
324 dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
325 }
326 dump.append(" |");
327
328
329 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
330 dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
331 }
332 dump.append('|');
333 }
334
335
336 if (remainder != 0) {
337 int rowStartIndex = (fullRows << 4) + offset;
338 appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
339
340
341 int rowEndIndex = rowStartIndex + remainder;
342 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
343 dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
344 }
345 dump.append(HEXPADDING[remainder]);
346 dump.append(" |");
347
348
349 for (int j = rowStartIndex; j < rowEndIndex; j ++) {
350 dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
351 }
352 dump.append(BYTEPADDING[remainder]);
353 dump.append('|');
354 }
355
356 dump.append(NEWLINE +
357 "+--------+-------------------------------------------------+----------------+");
358 }
359
360 private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {
361 if (row < HEXDUMP_ROWPREFIXES.length) {
362 dump.append(HEXDUMP_ROWPREFIXES[row]);
363 } else {
364 dump.append(NEWLINE);
365 dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));
366 dump.setCharAt(dump.length() - 9, '|');
367 dump.append('|');
368 }
369 }
370 }
371
372
373 private static final class ThreadLocalDirectBufferHolder {
374 static final FastThreadLocal<Buffer> BUFFER = new FastThreadLocal<>() {
375 @Override
376 protected Buffer initialValue() throws Exception {
377 return DefaultBufferAllocators.offHeapAllocator().allocate(1024);
378 }
379
380 @Override
381 protected void onRemoval(Buffer value) throws Exception {
382 if (value.isAccessible()) {
383 value.close();
384 }
385 super.onRemoval(value);
386 }
387 };
388 }
389
390 private BufferUtil() { }
391 }