1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.buffer.api.internal;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.buffer.api.BufferClosedException;
20 import io.netty5.buffer.api.BufferReadOnlyException;
21 import io.netty5.buffer.api.Drop;
22 import io.netty5.buffer.api.MemoryManager;
23 import io.netty5.buffer.api.ReadableComponent;
24 import io.netty5.util.AsciiString;
25 import io.netty5.util.internal.PlatformDependent;
26
27 import java.lang.invoke.MethodHandle;
28 import java.lang.invoke.MethodHandles;
29 import java.lang.invoke.MethodHandles.Lookup;
30 import java.lang.invoke.MethodType;
31 import java.lang.invoke.VarHandle;
32 import java.lang.ref.Cleaner;
33 import java.nio.ByteBuffer;
34 import java.nio.charset.Charset;
35 import java.util.Arrays;
36 import java.util.concurrent.atomic.LongAdder;
37 import java.util.function.Function;
38
39 import static io.netty5.util.CharsetUtil.US_ASCII;
40 import static io.netty5.util.internal.ObjectUtil.checkPositiveOrZero;
41 import static java.util.Objects.requireNonNull;
42
43 public interface Statics {
44 LongAdder MEM_USAGE_NATIVE = new LongAdder();
45 Cleaner CLEANER = Cleaner.create();
46 Drop<Buffer> NO_OP_DROP = new Drop<>() {
47 @Override
48 public void drop(Buffer obj) {
49 }
50
51 @Override
52 public Drop<Buffer> fork() {
53 return this;
54 }
55
56 @Override
57 public void attach(Buffer obj) {
58 }
59
60 @Override
61 public String toString() {
62 return "NO_OP_DROP";
63 }
64 };
65 MethodHandle BB_SLICE_OFFSETS = getByteBufferSliceOffsetsMethodHandle();
66 MethodHandle BB_PUT_OFFSETS = getByteBufferPutOffsetsMethodHandle();
67
68
69
70
71 int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
72
73 static MethodHandle getByteBufferSliceOffsetsMethodHandle() {
74 try {
75 Lookup lookup = MethodHandles.lookup();
76 MethodType type = MethodType.methodType(ByteBuffer.class, int.class, int.class);
77 return lookup.findVirtual(ByteBuffer.class, "slice", type);
78 } catch (Exception ignore) {
79 return null;
80 }
81 }
82
83 @SuppressWarnings("JavaLangInvokeHandleSignature")
84 static MethodHandle getByteBufferPutOffsetsMethodHandle() {
85 try {
86 Lookup lookup = MethodHandles.lookup();
87 MethodType type = MethodType.methodType(
88 ByteBuffer.class, int.class, ByteBuffer.class, int.class, int.class);
89 return lookup.findVirtual(ByteBuffer.class, "put", type);
90 } catch (Exception ignore) {
91 return null;
92 }
93 }
94
95 static Function<Drop<Buffer>, Drop<Buffer>> standardDrop(MemoryManager manager) {
96 return drop -> CleanerDrop.wrap(drop, manager);
97 }
98
99 static VarHandle findVarHandle(Lookup lookup, Class<?> recv, String name, Class<?> type) {
100 try {
101 return lookup.findVarHandle(recv, name, type);
102 } catch (Exception e) {
103 throw new ExceptionInInitializerError(e);
104 }
105 }
106
107 @SuppressWarnings("unchecked")
108 static <T, R> Drop<R> convert(Drop<T> drop) {
109 return (Drop<R>) drop;
110 }
111
112
113
114
115
116
117
118
119 static void assertValidBufferSize(long size) {
120 if (size < 0) {
121 throw new IllegalArgumentException("Buffer size must not be negative, but was " + size + '.');
122 }
123 if (size > MAX_BUFFER_SIZE) {
124 throw new IllegalArgumentException(
125 "Buffer size cannot be greater than " + MAX_BUFFER_SIZE + ", but was " + size + '.');
126 }
127 }
128
129 static void checkImplicitCapacity(int implicitCapacity, int currentCapacity) {
130 if (implicitCapacity < currentCapacity) {
131 throw new IndexOutOfBoundsException(
132 "Implicit capacity limit (" + implicitCapacity +
133 ") cannot be less than capacity (" + currentCapacity + ')');
134 }
135 if (implicitCapacity > MAX_BUFFER_SIZE) {
136 throw new IndexOutOfBoundsException(
137 "Implicit capacity limit (" + implicitCapacity +
138 ") cannot be greater than max buffer size (" + MAX_BUFFER_SIZE + ')');
139 }
140 }
141
142 static void checkLength(int length) {
143 if (length < 0) {
144 throw new IndexOutOfBoundsException("The length cannot be negative: " + length + '.');
145 }
146 }
147
148 static void copyToViaReverseLoop(Buffer src, int srcPos, Buffer dest, int destPos, int length) {
149 checkLength(length);
150 if (length == 0) {
151 return;
152 }
153
154 int i = length;
155 while (i >= Long.BYTES) {
156 i -= Long.BYTES;
157 dest.setLong(destPos + i, src.getLong(srcPos + i));
158 }
159 while (i > 0) {
160 i--;
161 dest.setByte(destPos + i, src.getByte(srcPos + i));
162 }
163 }
164
165 static ByteBuffer tryGetWritableBufferFromReadableComponent(ReadableComponent component) {
166 if (component instanceof NotReadOnlyReadableComponent) {
167 return ((NotReadOnlyReadableComponent) component).mutableReadableBuffer();
168 }
169 return null;
170 }
171
172
173
174
175
176 static ByteBuffer bbslice(ByteBuffer buffer, int fromOffset, int length) {
177 if (BB_SLICE_OFFSETS != null) {
178 return bbsliceJdk13(buffer, fromOffset, length);
179 }
180 return bbsliceFallback(buffer, fromOffset, length);
181 }
182
183 private static ByteBuffer bbsliceJdk13(ByteBuffer buffer, int fromOffset, int length) {
184 try {
185 return (ByteBuffer) BB_SLICE_OFFSETS.invokeExact(buffer, fromOffset, length);
186 } catch (RuntimeException re) {
187 throw re;
188 } catch (Throwable throwable) {
189 throw new LinkageError("Unexpected exception from ByteBuffer.slice(int,int).", throwable);
190 }
191 }
192
193 private static ByteBuffer bbsliceFallback(ByteBuffer buffer, int fromOffset, int length) {
194 if (fromOffset < 0) {
195 throw new IndexOutOfBoundsException("The fromOffset must be positive: " + fromOffset + '.');
196 }
197 int newLimit = fromOffset + length;
198 if (newLimit > buffer.capacity()) {
199 throw new IndexOutOfBoundsException(
200 "The limit of " + newLimit + " would be greater than capacity: " + buffer.capacity() + '.');
201 }
202 return buffer.duplicate().clear().position(fromOffset).limit(newLimit).slice();
203 }
204
205
206
207
208 static void bbput(ByteBuffer dest, int destPos, ByteBuffer src, int srcPos, int length) {
209 if (BB_PUT_OFFSETS != null) {
210 bbputJdk16(dest, destPos, src, srcPos, length);
211 } else {
212 bbputFallback(dest, destPos, src, srcPos, length);
213 }
214 }
215
216 private static void bbputJdk16(ByteBuffer dest, int destPos, ByteBuffer src, int srcPos, int length) {
217 try {
218 @SuppressWarnings("unused")
219 ByteBuffer ignore = (ByteBuffer) BB_PUT_OFFSETS.invokeExact(dest, destPos, src, srcPos, length);
220 } catch (RuntimeException re) {
221 throw re;
222 } catch (Throwable throwable) {
223 throw new LinkageError("Unexpected exception from ByteBuffer.put(int,ByteBuffer,int,int).", throwable);
224 }
225 }
226
227 private static void bbputFallback(ByteBuffer dest, int destPos, ByteBuffer src, int srcPos, int length) {
228 dest.position(destPos).put(bbslice(src, srcPos, length));
229 }
230
231 static void setMemory(ByteBuffer buffer, int length, byte value) {
232 if (!buffer.hasArray()) {
233 long address;
234 if (PlatformDependent.hasUnsafe() && (address = nativeAddressOfDirectByteBuffer(buffer)) != 0) {
235 PlatformDependent.setMemory(address, length, value);
236 } else {
237 final int intFillValue = (value & 0xFF) * 0x01010101;
238 final int intCount = length >>> 2;
239 for (int i = 0; i < intCount; i++) {
240 buffer.putInt(i << 2, intFillValue);
241 }
242 final int byteCount = length & 3;
243 final int bytesOffset = intCount << 2;
244 for (int i = 0; i < byteCount; i++) {
245 buffer.put(bytesOffset + i, value);
246 }
247 }
248 } else {
249 final int start = buffer.arrayOffset();
250 if (PlatformDependent.hasUnsafe()) {
251 PlatformDependent.setMemory(buffer.array(), start, length, value);
252 } else {
253 final int end = start + length;
254 Arrays.fill(buffer.array(), start, end, value);
255 }
256 }
257 }
258
259 static BufferClosedException bufferIsClosed(Buffer buffer) {
260 return new BufferClosedException("This buffer is closed: " + buffer);
261 }
262
263 static BufferReadOnlyException bufferIsReadOnly(Buffer buffer) {
264 return new BufferReadOnlyException("This buffer is read-only: " + buffer);
265 }
266
267 static IllegalStateException allocatorClosedException() {
268 return new IllegalStateException("This allocator has been closed.");
269 }
270
271 static <T> T acquire(ResourceSupport<?, ?> obj) {
272 return ResourceSupport.acquire(obj);
273 }
274
275 static boolean isOwned(ResourceSupport<?, ?> obj) {
276 return ResourceSupport.isOwned(obj);
277 }
278
279 static int countBorrows(ResourceSupport<?, ?> obj) {
280 return ResourceSupport.countBorrows(obj);
281 }
282
283 static <E extends Throwable> E attachTrace(ResourceSupport<?, ?> obj, E throwable) {
284 return ResourceSupport.getTracer(obj).attachTrace(throwable);
285 }
286
287 @SuppressWarnings({ "unchecked", "rawtypes" })
288 static void unsafeSetDrop(ResourceSupport<?, ?> obj, Drop<?> replacement) {
289 obj.unsafeSetDrop((Drop) replacement);
290 }
291
292 static CharSequence readCharSequence(Buffer source, int length, Charset charset) {
293 final CharSequence charSequence = copyToCharSequence(source, source.readerOffset(), length, charset);
294 source.skipReadableBytes(length);
295 return charSequence;
296 }
297
298 static String toString(Buffer source, Charset charset) {
299 return copyToCharSequence(source, source.readerOffset(), source.readableBytes(), charset).toString();
300 }
301
302 static CharSequence copyToCharSequence(Buffer source, int srcIdx, int length, Charset charset) {
303 byte[] data = new byte[length];
304 source.copyInto(srcIdx, data, 0, length);
305 if (US_ASCII.equals(charset)) {
306 return new AsciiString(data).toString();
307 }
308 return new String(data, 0, length, charset);
309 }
310
311 static void writeCharSequence(CharSequence source, Buffer destination, Charset charset) {
312 if (US_ASCII.equals(charset) && source instanceof AsciiString) {
313 AsciiString asciiString = (AsciiString) source;
314 destination.writeBytes(asciiString.array(), asciiString.arrayOffset(), source.length());
315 return;
316 }
317
318 byte[] bytes = source.toString().getBytes(charset);
319 destination.writeBytes(bytes);
320 }
321
322 static boolean equals(Buffer bufferA, Buffer bufferB) {
323 if (bufferA == null && bufferB != null || bufferB == null && bufferA != null) {
324 return false;
325 }
326 if (bufferA == bufferB) {
327 return true;
328 }
329 final int aLen = bufferA.readableBytes();
330 if (aLen != bufferB.readableBytes()) {
331 return false;
332 }
333 return equals(bufferA, bufferA.readerOffset(), bufferB, bufferB.readerOffset(), aLen);
334 }
335
336 static boolean equals(Buffer a, int aStartIndex, Buffer b, int bStartIndex, int length) {
337 requireNonNull(a, "a");
338 requireNonNull(b, "b");
339
340 checkPositiveOrZero(aStartIndex, "aStartIndex");
341 checkPositiveOrZero(bStartIndex, "bStartIndex");
342 checkPositiveOrZero(length, "length");
343
344 if (a.writerOffset() - length < aStartIndex || b.writerOffset() - length < bStartIndex) {
345 return false;
346 }
347
348 return equalsInner(a, aStartIndex, b, bStartIndex, length);
349 }
350
351 private static boolean equalsInner(Buffer a, int aStartIndex, Buffer b, int bStartIndex, int length) {
352 final int longCount = length >>> 3;
353 final int byteCount = length & 7;
354
355 for (int i = longCount; i > 0; i --) {
356 if (a.getLong(aStartIndex) != b.getLong(bStartIndex)) {
357 return false;
358 }
359 aStartIndex += 8;
360 bStartIndex += 8;
361 }
362
363 for (int i = byteCount; i > 0; i --) {
364 if (a.getByte(aStartIndex) != b.getByte(bStartIndex)) {
365 return false;
366 }
367 aStartIndex++;
368 bStartIndex++;
369 }
370
371 return true;
372 }
373
374 static int hashCode(Buffer buffer) {
375 final int aLen = buffer.readableBytes();
376 final int intCount = aLen >>> 2;
377 final int byteCount = aLen & 3;
378
379 int hashCode = 0;
380 int arrayIndex = buffer.readerOffset();
381 for (int i = intCount; i > 0; i --) {
382 hashCode = 31 * hashCode + buffer.getInt(arrayIndex);
383 arrayIndex += 4;
384 }
385
386 for (int i = byteCount; i > 0; i --) {
387 hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++);
388 }
389
390 if (hashCode == 0) {
391 hashCode = 1;
392 }
393
394 return hashCode;
395 }
396
397
398
399
400
401
402
403
404
405
406 static long nativeAddressWithOffset(long address, int offset) {
407 if (address == 0) {
408 return 0;
409 }
410 return address + offset;
411 }
412
413 static long nativeAddressOfDirectByteBuffer(ByteBuffer byteBuffer) {
414 if (!byteBuffer.isDirect()) {
415 return 0;
416 }
417 if (PlatformDependent.hasUnsafe()) {
418 return PlatformDependent.directBufferAddress(byteBuffer);
419 }
420 if (JniBufferAccess.IS_AVAILABLE) {
421 try {
422 return (long) JniBufferAccess.MEMORY_ADDRESS.invokeExact(byteBuffer);
423 } catch (Throwable e) {
424 throw new LinkageError("JNI bypass native memory address accessor was supposed to be available, " +
425 "but threw an exception", e);
426 }
427 }
428 return 0;
429 }
430
431
432
433
434
435 interface UncheckedLoadByte {
436 byte load(Buffer buffer, int offset);
437 }
438
439 UncheckedLoadByte UNCHECKED_LOAD_BYTE_BUFFER = Buffer::getByte;
440
441 static int bytesBefore(Buffer haystack, UncheckedLoadByte hl,
442 Buffer needle, UncheckedLoadByte nl) {
443 if (!haystack.isAccessible()) {
444 throw bufferIsClosed(haystack);
445 }
446 if (!needle.isAccessible()) {
447 throw bufferIsClosed(needle);
448 }
449
450 if (needle.readableBytes() > haystack.readableBytes()) {
451 return -1;
452 }
453
454 if (hl == null) {
455 hl = UNCHECKED_LOAD_BYTE_BUFFER;
456 }
457 if (nl == null) {
458 nl = UNCHECKED_LOAD_BYTE_BUFFER;
459 }
460
461 int haystackLen = haystack.readableBytes();
462 int needleLen = needle.readableBytes();
463 if (needleLen == 0) {
464 return 0;
465 }
466
467
468
469 if (needleLen == 1) {
470 return haystack.bytesBefore(needle.getByte(needle.readerOffset()));
471 }
472
473 int needleStart = needle.readerOffset();
474 int haystackStart = haystack.readerOffset();
475 long suffixes = maxFixes(needle, nl, needleLen, needleStart, true);
476 long prefixes = maxFixes(needle, nl, needleLen, needleStart, false);
477 int maxSuffix = Math.max((int) (suffixes >> 32), (int) (prefixes >> 32));
478 int period = Math.max((int) suffixes, (int) prefixes);
479 int length = Math.min(needleLen - period, maxSuffix + 1);
480
481 if (equalsInner(needle, needleStart, needle, needleStart + period, length)) {
482 return bytesBeforeInnerPeriodic(
483 haystack, hl, needle, nl, haystackLen, needleLen, needleStart, haystackStart, maxSuffix, period);
484 }
485 return bytesBeforeInnerNonPeriodic(
486 haystack, hl, needle, nl, haystackLen, needleLen, needleStart, haystackStart, maxSuffix);
487 }
488
489 private static int bytesBeforeInnerPeriodic(Buffer haystack, UncheckedLoadByte hl,
490 Buffer needle, UncheckedLoadByte nl,
491 int haystackLen, int needleLen, int needleStart, int haystackStart,
492 int maxSuffix, int period) {
493 int j = 0;
494 int memory = -1;
495 while (j <= haystackLen - needleLen) {
496 int i = Math.max(maxSuffix, memory) + 1;
497 while (i < needleLen && nl.load(needle, i + needleStart) == hl.load(haystack, i + j + haystackStart)) {
498 ++i;
499 }
500 if (i > haystackLen) {
501 return -1;
502 }
503 if (i >= needleLen) {
504 i = maxSuffix;
505 while (i > memory && nl.load(needle, i + needleStart) == hl.load(haystack, i + j + haystackStart)) {
506 --i;
507 }
508 if (i <= memory) {
509 return j;
510 }
511 j += period;
512 memory = needleLen - period - 1;
513 } else {
514 j += i - maxSuffix;
515 memory = -1;
516 }
517 }
518 return -1;
519 }
520
521 private static int bytesBeforeInnerNonPeriodic(Buffer haystack, UncheckedLoadByte hl,
522 Buffer needle, UncheckedLoadByte nl,
523 int haystackLen, int needleLen, int needleStart, int haystackStart,
524 int maxSuffix) {
525 int j = 0;
526 int period = Math.max(maxSuffix + 1, needleLen - maxSuffix - 1) + 1;
527 while (j <= haystackLen - needleLen) {
528 int i = maxSuffix + 1;
529 while (i < needleLen && nl.load(needle, i + needleStart) == hl.load(haystack, i + j + haystackStart)) {
530 ++i;
531 }
532 if (i > haystackLen) {
533 return -1;
534 }
535 if (i >= needleLen) {
536 i = maxSuffix;
537 while (i >= 0 && nl.load(needle, i + needleStart) == hl.load(haystack, i + j + haystackStart)) {
538 --i;
539 }
540 if (i < 0) {
541 return j;
542 }
543 j += period;
544 } else {
545 j += i - maxSuffix;
546 }
547 }
548 return -1;
549 }
550
551 private static long maxFixes(Buffer needle, UncheckedLoadByte nl, int needleLen, int start, boolean isSuffix) {
552 int period = 1;
553 int maxSuffix = -1;
554 int lastRest = start;
555 int k = 1;
556 while (lastRest + k < needleLen) {
557 byte a = nl.load(needle, lastRest + k);
558 byte b = nl.load(needle, maxSuffix + k);
559 boolean suffix = isSuffix ? a < b : a > b;
560 if (suffix) {
561 lastRest += k;
562 k = 1;
563 period = lastRest - maxSuffix;
564 } else if (a == b) {
565 if (k != period) {
566 ++k;
567 } else {
568 lastRest += period;
569 k = 1;
570 }
571 } else {
572 maxSuffix = lastRest;
573 lastRest = maxSuffix + 1;
574 k = period = 1;
575 }
576 }
577 return ((long) maxSuffix << 32) + period;
578 }
579 }