View Javadoc
1   /*
2    * Copyright 2021 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
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       * The maximum buffer size we support is the maximum array length generally supported by JVMs,
69       * because on-heap buffers will be backed by byte-arrays.
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      * Check the given {@code size} argument is a valid buffer size, or throw an {@link IllegalArgumentException}.
114      *
115      * @param size The size to check.
116      * @throws IllegalArgumentException if the size is not positive, or if the size is too big (over ~2 GB) for a
117      * buffer to accommodate.
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         // Iterate in reverse to account for src and dest buffer overlap.
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      * The ByteBuffer slice-with-offset-and-length method is only available from Java 13 and onwards, but we need to
174      * support Java 11.
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      * The ByteBuffer put-buffer-with-offset-and-length method is not available in Java 11.
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") // We need to cast the return type in order to invokeExact.
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         // TODO: Copy optimized writes from BufferUtil
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         // All indexes and lengths must be non-negative
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      * Compute an offset into a native address.
399      * Zero is used as a marker for when a native address is not available,
400      * and an offset into a zero address will remain zero.
401      *
402      * @param address The native address, or zero if no native address is available.
403      * @param offset The offset into the native address we wish to compute.
404      * @return An offsetted native address, or zero if no native address was available.
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      * This interface provides the fastest possible offsetted byte-access to a buffer.
433      * Used by {@link #bytesBefore(Buffer, UncheckedLoadByte, Buffer, UncheckedLoadByte)} to access memory faster.
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         // When the needle has only one byte that can be read,
468         // the Buffer.bytesBefore() method can be used
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 }