1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.util.function.pooled;
18 
19 import android.annotation.Nullable;
20 import android.os.Message;
21 import android.text.TextUtils;
22 import android.util.Log;
23 import android.util.Pools;
24 
25 import com.android.internal.util.ArrayUtils;
26 import com.android.internal.util.BitUtils;
27 import com.android.internal.util.Preconditions;
28 import com.android.internal.util.function.HeptConsumer;
29 import com.android.internal.util.function.HeptFunction;
30 import com.android.internal.util.function.HeptPredicate;
31 import com.android.internal.util.function.HexConsumer;
32 import com.android.internal.util.function.HexFunction;
33 import com.android.internal.util.function.HexPredicate;
34 import com.android.internal.util.function.NonaConsumer;
35 import com.android.internal.util.function.NonaFunction;
36 import com.android.internal.util.function.NonaPredicate;
37 import com.android.internal.util.function.OctConsumer;
38 import com.android.internal.util.function.OctFunction;
39 import com.android.internal.util.function.OctPredicate;
40 import com.android.internal.util.function.QuadConsumer;
41 import com.android.internal.util.function.QuadFunction;
42 import com.android.internal.util.function.QuadPredicate;
43 import com.android.internal.util.function.QuintConsumer;
44 import com.android.internal.util.function.QuintFunction;
45 import com.android.internal.util.function.QuintPredicate;
46 import com.android.internal.util.function.TriConsumer;
47 import com.android.internal.util.function.TriFunction;
48 import com.android.internal.util.function.TriPredicate;
49 
50 import java.util.Arrays;
51 import java.util.Objects;
52 import java.util.function.BiConsumer;
53 import java.util.function.BiFunction;
54 import java.util.function.BiPredicate;
55 import java.util.function.Consumer;
56 import java.util.function.Function;
57 import java.util.function.Predicate;
58 import java.util.function.Supplier;
59 
60 /**
61  * @see PooledLambda
62  * @hide
63  */
64 final class PooledLambdaImpl<R> extends OmniFunction<Object,
65         Object, Object, Object, Object, Object, Object, Object, Object, R> {
66 
67     private static final boolean DEBUG = false;
68     private static final String LOG_TAG = "PooledLambdaImpl";
69 
70     private static final int MAX_ARGS = 9;
71 
72     private static final int MAX_POOL_SIZE = 50;
73 
74     static class Pool extends Pools.SynchronizedPool<PooledLambdaImpl> {
75 
Pool(Object lock)76         public Pool(Object lock) {
77             super(MAX_POOL_SIZE, lock);
78         }
79     }
80 
81     static final Pool sPool = new Pool(new Object());
82     static final Pool sMessageCallbacksPool = new Pool(Message.sPoolSync);
83 
PooledLambdaImpl()84     private PooledLambdaImpl() {}
85 
86     /**
87      * The function reference to be invoked
88      *
89      * May be the return value itself in case when an immediate result constant is provided instead
90      */
91     Object mFunc;
92 
93     /**
94      * A primitive result value to be immediately returned on invocation instead of calling
95      * {@link #mFunc}
96      */
97     long mConstValue;
98 
99     /**
100      * Arguments for {@link #mFunc}
101      */
102     @Nullable Object[] mArgs = null;
103 
104     /**
105      * Flag for {@link #mFlags}
106      *
107      * Indicates whether this instance is recycled
108      */
109     private static final int FLAG_RECYCLED = 1 << MAX_ARGS;
110 
111     /**
112      * Flag for {@link #mFlags}
113      *
114      * Indicates whether this instance should be immediately recycled on invocation
115      * (as requested via {@link PooledLambda#recycleOnUse()}) or not(default)
116      */
117     private static final int FLAG_RECYCLE_ON_USE = 1 << (MAX_ARGS + 1);
118 
119     /**
120      * Flag for {@link #mFlags}
121      *
122      * Indicates that this instance was acquired from {@link #sMessageCallbacksPool} as opposed to
123      * {@link #sPool}
124      */
125     private static final int FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL = 1 << (MAX_ARGS + 2);
126 
127     /** @see #mFlags */
128     static final int MASK_EXPOSED_AS = LambdaType.MASK << (MAX_ARGS + 3);
129 
130     /** @see #mFlags */
131     static final int MASK_FUNC_TYPE = LambdaType.MASK <<
132             (MAX_ARGS + 3 + LambdaType.MASK_BIT_COUNT);
133 
134     /**
135      * Bit schema:
136      * AAAAAAAAABCDEEEEEEFFFFFF
137      *
138      * Where:
139      * A - whether {@link #mArgs arg} at corresponding index was specified at
140      * {@link #acquire creation time} (0) or {@link #invoke invocation time} (1)
141      * B - {@link #FLAG_RECYCLED}
142      * C - {@link #FLAG_RECYCLE_ON_USE}
143      * D - {@link #FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL}
144      * E - {@link LambdaType} representing the type of the lambda returned to the caller from a
145      * factory method
146      * F - {@link LambdaType} of {@link #mFunc} as resolved when calling a factory method
147      */
148     int mFlags = 0;
149 
150 
151     @Override
recycle()152     public void recycle() {
153         if (DEBUG) Log.i(LOG_TAG, this + ".recycle()");
154         if (!isRecycled()) doRecycle();
155     }
156 
doRecycle()157     private void doRecycle() {
158         if (DEBUG) Log.i(LOG_TAG, this + ".doRecycle()");
159         Pool pool = (mFlags & FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL) != 0
160                 ? PooledLambdaImpl.sMessageCallbacksPool
161                 : PooledLambdaImpl.sPool;
162 
163         mFunc = null;
164         if (mArgs != null) Arrays.fill(mArgs, null);
165         mFlags = FLAG_RECYCLED;
166         mConstValue = 0L;
167 
168         pool.release(this);
169     }
170 
171     @Override
invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7, Object a8, Object a9)172     R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7,
173             Object a8, Object a9) {
174         checkNotRecycled();
175         if (DEBUG) {
176             Log.i(LOG_TAG, this + ".invoke("
177                     + commaSeparateFirstN(
178                             new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9 },
179                             LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS)))
180                     + ")");
181         }
182         final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4)
183                 && fillInArg(a5) && fillInArg(a6) && fillInArg(a7) && fillInArg(a8)
184                 && fillInArg(a9);
185         int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE));
186         if (argCount != LambdaType.MASK_ARG_COUNT) {
187             for (int i = 0; i < argCount; i++) {
188                 if (mArgs[i] == ArgumentPlaceholder.INSTANCE) {
189                     throw new IllegalStateException("Missing argument #" + i + " among "
190                             + Arrays.toString(mArgs));
191                 }
192             }
193         }
194         try {
195             return doInvoke();
196         } finally {
197             if (isRecycleOnUse()) doRecycle();
198             if (!isRecycled()) {
199                 int argsSize = ArrayUtils.size(mArgs);
200                 for (int i = 0; i < argsSize; i++) {
201                     popArg(i);
202                 }
203             }
204         }
205     }
206 
fillInArg(Object invocationArg)207     private boolean fillInArg(Object invocationArg) {
208         int argsSize = ArrayUtils.size(mArgs);
209         for (int i = 0; i < argsSize; i++) {
210             if (mArgs[i] == ArgumentPlaceholder.INSTANCE) {
211                 mArgs[i] = invocationArg;
212                 mFlags |= BitUtils.bitAt(i);
213                 return true;
214             }
215         }
216         if (invocationArg != null && invocationArg != ArgumentPlaceholder.INSTANCE) {
217             throw new IllegalStateException("No more arguments expected for provided arg "
218                     + invocationArg + " among " + Arrays.toString(mArgs));
219         }
220         return false;
221     }
222 
checkNotRecycled()223     private void checkNotRecycled() {
224         if (isRecycled()) throw new IllegalStateException("Instance is recycled: " + this);
225     }
226 
227     @SuppressWarnings("unchecked")
doInvoke()228     private R doInvoke() {
229         final int funcType = getFlags(MASK_FUNC_TYPE);
230         final int argCount = LambdaType.decodeArgCount(funcType);
231         final int returnType = LambdaType.decodeReturnType(funcType);
232 
233         switch (argCount) {
234             case LambdaType.MASK_ARG_COUNT: {
235                 switch (returnType) {
236                     case LambdaType.ReturnType.INT: return (R) (Integer) getAsInt();
237                     case LambdaType.ReturnType.LONG: return (R) (Long) getAsLong();
238                     case LambdaType.ReturnType.DOUBLE: return (R) (Double) getAsDouble();
239                     default: return (R) mFunc;
240                 }
241             }
242             case 0: {
243                 switch (returnType) {
244                     case LambdaType.ReturnType.VOID: {
245                         ((Runnable) mFunc).run();
246                         return null;
247                     }
248                     case LambdaType.ReturnType.BOOLEAN:
249                     case LambdaType.ReturnType.OBJECT: {
250                         return (R) ((Supplier) mFunc).get();
251                     }
252                 }
253             } break;
254             case 1: {
255                 switch (returnType) {
256                     case LambdaType.ReturnType.VOID: {
257                         ((Consumer) mFunc).accept(popArg(0));
258                         return null;
259                     }
260                     case LambdaType.ReturnType.BOOLEAN: {
261                         return (R) (Object) ((Predicate) mFunc).test(popArg(0));
262                     }
263                     case LambdaType.ReturnType.OBJECT: {
264                         return (R) ((Function) mFunc).apply(popArg(0));
265                     }
266                 }
267             } break;
268             case 2: {
269                 switch (returnType) {
270                     case LambdaType.ReturnType.VOID: {
271                         ((BiConsumer) mFunc).accept(popArg(0), popArg(1));
272                         return null;
273                     }
274                     case LambdaType.ReturnType.BOOLEAN: {
275                         return (R) (Object) ((BiPredicate) mFunc).test(popArg(0), popArg(1));
276                     }
277                     case LambdaType.ReturnType.OBJECT: {
278                         return (R) ((BiFunction) mFunc).apply(popArg(0), popArg(1));
279                     }
280                 }
281             } break;
282             case 3: {
283                 switch (returnType) {
284                     case LambdaType.ReturnType.VOID: {
285                         ((TriConsumer) mFunc).accept(popArg(0), popArg(1), popArg(2));
286                         return null;
287                     }
288                     case LambdaType.ReturnType.BOOLEAN: {
289                         return (R) (Object) ((TriPredicate) mFunc).test(
290                                 popArg(0), popArg(1), popArg(2));
291                     }
292                     case LambdaType.ReturnType.OBJECT: {
293                         return (R) ((TriFunction) mFunc).apply(popArg(0), popArg(1), popArg(2));
294                     }
295                 }
296             } break;
297             case 4: {
298                 switch (returnType) {
299                     case LambdaType.ReturnType.VOID: {
300                         ((QuadConsumer) mFunc).accept(popArg(0), popArg(1), popArg(2), popArg(3));
301                         return null;
302                     }
303                     case LambdaType.ReturnType.BOOLEAN: {
304                         return (R) (Object) ((QuadPredicate) mFunc).test(
305                                 popArg(0), popArg(1), popArg(2), popArg(3));
306                     }
307                     case LambdaType.ReturnType.OBJECT: {
308                         return (R) ((QuadFunction) mFunc).apply(
309                                 popArg(0), popArg(1), popArg(2), popArg(3));
310                     }
311                 }
312             } break;
313 
314             case 5: {
315                 switch (returnType) {
316                     case LambdaType.ReturnType.VOID: {
317                         ((QuintConsumer) mFunc).accept(popArg(0), popArg(1),
318                                 popArg(2), popArg(3), popArg(4));
319                         return null;
320                     }
321                     case LambdaType.ReturnType.BOOLEAN: {
322                         return (R) (Object) ((QuintPredicate) mFunc).test(
323                                 popArg(0), popArg(1), popArg(2), popArg(3), popArg(4));
324                     }
325                     case LambdaType.ReturnType.OBJECT: {
326                         return (R) ((QuintFunction) mFunc).apply(
327                                 popArg(0), popArg(1), popArg(2), popArg(3),  popArg(4));
328                     }
329                 }
330             } break;
331 
332             case 6: {
333                 switch (returnType) {
334                     case LambdaType.ReturnType.VOID: {
335                         ((HexConsumer) mFunc).accept(popArg(0), popArg(1),
336                                 popArg(2), popArg(3), popArg(4), popArg(5));
337                         return null;
338                     }
339                     case LambdaType.ReturnType.BOOLEAN: {
340                         return (R) (Object) ((HexPredicate) mFunc).test(popArg(0),
341                                 popArg(1), popArg(2), popArg(3), popArg(4), popArg(5));
342                     }
343                     case LambdaType.ReturnType.OBJECT: {
344                         return (R) ((HexFunction) mFunc).apply(popArg(0), popArg(1),
345                                 popArg(2), popArg(3), popArg(4), popArg(5));
346                     }
347                 }
348             } break;
349 
350             case 7: {
351                 switch (returnType) {
352                     case LambdaType.ReturnType.VOID: {
353                         ((HeptConsumer) mFunc).accept(popArg(0), popArg(1),
354                                 popArg(2), popArg(3), popArg(4),
355                                 popArg(5), popArg(6));
356                         return null;
357                     }
358                     case LambdaType.ReturnType.BOOLEAN: {
359                         return (R) (Object) ((HeptPredicate) mFunc).test(popArg(0),
360                                 popArg(1), popArg(2), popArg(3),
361                                 popArg(4), popArg(5), popArg(6));
362                     }
363                     case LambdaType.ReturnType.OBJECT: {
364                         return (R) ((HeptFunction) mFunc).apply(popArg(0), popArg(1),
365                                 popArg(2), popArg(3), popArg(4),
366                                 popArg(5), popArg(6));
367                     }
368                 }
369             } break;
370 
371             case 8: {
372                 switch (returnType) {
373                     case LambdaType.ReturnType.VOID: {
374                         ((OctConsumer) mFunc).accept(popArg(0), popArg(1),
375                                 popArg(2), popArg(3), popArg(4),
376                                 popArg(5), popArg(6), popArg(7));
377                         return null;
378                     }
379                     case LambdaType.ReturnType.BOOLEAN: {
380                         return (R) (Object) ((OctPredicate) mFunc).test(popArg(0),
381                                 popArg(1), popArg(2), popArg(3),
382                                 popArg(4), popArg(5), popArg(6), popArg(7));
383                     }
384                     case LambdaType.ReturnType.OBJECT: {
385                         return (R) ((OctFunction) mFunc).apply(popArg(0), popArg(1),
386                                 popArg(2), popArg(3), popArg(4),
387                                 popArg(5), popArg(6), popArg(7));
388                     }
389                 }
390             } break;
391 
392             case 9: {
393                 switch (returnType) {
394                     case LambdaType.ReturnType.VOID: {
395                         ((NonaConsumer) mFunc).accept(popArg(0), popArg(1),
396                                 popArg(2), popArg(3), popArg(4), popArg(5),
397                                 popArg(6), popArg(7), popArg(8));
398                         return null;
399                     }
400                     case LambdaType.ReturnType.BOOLEAN: {
401                         return (R) (Object) ((NonaPredicate) mFunc).test(popArg(0),
402                                 popArg(1), popArg(2), popArg(3), popArg(4),
403                                 popArg(5), popArg(6), popArg(7), popArg(8));
404                     }
405                     case LambdaType.ReturnType.OBJECT: {
406                         return (R) ((NonaFunction) mFunc).apply(popArg(0), popArg(1),
407                                 popArg(2), popArg(3), popArg(4), popArg(5),
408                                 popArg(6), popArg(7), popArg(8));
409                     }
410                 }
411             } break;
412         }
413         throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
414     }
415 
isConstSupplier()416     private boolean isConstSupplier() {
417         return LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)) == LambdaType.MASK_ARG_COUNT;
418     }
419 
popArg(int index)420     private Object popArg(int index) {
421         Object result = mArgs[index];
422         if (isInvocationArgAtIndex(index)) {
423             mArgs[index] = ArgumentPlaceholder.INSTANCE;
424             mFlags &= ~BitUtils.bitAt(index);
425         }
426         return result;
427     }
428 
429     @Override
toString()430     public String toString() {
431         if (isRecycled()) return "<recycled PooledLambda@" + hashCodeHex(this) + ">";
432 
433         StringBuilder sb = new StringBuilder();
434         if (isConstSupplier()) {
435             sb.append(getFuncTypeAsString()).append("(").append(doInvoke()).append(")");
436         } else {
437             Object func = mFunc;
438             if (func instanceof PooledLambdaImpl) {
439                 sb.append(func);
440             } else {
441                 sb.append(getFuncTypeAsString()).append("@").append(hashCodeHex(func));
442             }
443             sb.append("(");
444             sb.append(commaSeparateFirstN(mArgs,
445                     LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE))));
446             sb.append(")");
447         }
448         return sb.toString();
449     }
450 
commaSeparateFirstN(@ullable Object[] arr, int n)451     private String commaSeparateFirstN(@Nullable Object[] arr, int n) {
452         if (arr == null) return "";
453         return TextUtils.join(",", Arrays.copyOf(arr, n));
454     }
455 
hashCodeHex(Object o)456     private static String hashCodeHex(Object o) {
457         return Integer.toHexString(Objects.hashCode(o));
458     }
459 
getFuncTypeAsString()460     private String getFuncTypeAsString() {
461         if (isRecycled()) return "<recycled>";
462         if (isConstSupplier()) return "supplier";
463         String name = LambdaType.toString(getFlags(MASK_EXPOSED_AS));
464         if (name.endsWith("Consumer")) return "consumer";
465         if (name.endsWith("Function")) return "function";
466         if (name.endsWith("Predicate")) return "predicate";
467         if (name.endsWith("Supplier")) return "supplier";
468         if (name.endsWith("Runnable")) return "runnable";
469         return name;
470     }
471 
472     /**
473      * Internal non-typesafe factory method for {@link PooledLambdaImpl}
474      */
acquire(Pool pool, Object func, int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c, Object d, Object e, Object f, Object g, Object h, Object i)475     static <E extends PooledLambda> E acquire(Pool pool, Object func,
476             int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
477             Object d, Object e, Object f, Object g, Object h, Object i) {
478         PooledLambdaImpl r = acquire(pool);
479         if (DEBUG) {
480             Log.i(LOG_TAG,
481                     "acquire(this = @" + hashCodeHex(r)
482                             + ", func = " + func
483                             + ", fNumArgs = " + fNumArgs
484                             + ", numPlaceholders = " + numPlaceholders
485                             + ", fReturnType = " + LambdaType.ReturnType.toString(fReturnType)
486                             + ", a = " + a
487                             + ", b = " + b
488                             + ", c = " + c
489                             + ", d = " + d
490                             + ", e = " + e
491                             + ", f = " + f
492                             + ", g = " + g
493                             + ", h = " + h
494                             + ", i = " + i
495                             + ")");
496         }
497         r.mFunc = Preconditions.checkNotNull(func);
498         r.setFlags(MASK_FUNC_TYPE, LambdaType.encode(fNumArgs, fReturnType));
499         r.setFlags(MASK_EXPOSED_AS, LambdaType.encode(numPlaceholders, fReturnType));
500         if (ArrayUtils.size(r.mArgs) < fNumArgs) r.mArgs = new Object[fNumArgs];
501         setIfInBounds(r.mArgs, 0, a);
502         setIfInBounds(r.mArgs, 1, b);
503         setIfInBounds(r.mArgs, 2, c);
504         setIfInBounds(r.mArgs, 3, d);
505         setIfInBounds(r.mArgs, 4, e);
506         setIfInBounds(r.mArgs, 5, f);
507         setIfInBounds(r.mArgs, 6, g);
508         setIfInBounds(r.mArgs, 7, h);
509         setIfInBounds(r.mArgs, 8, i);
510         return (E) r;
511     }
512 
acquireConstSupplier(int type)513     static PooledLambdaImpl acquireConstSupplier(int type) {
514         PooledLambdaImpl r = acquire(PooledLambdaImpl.sPool);
515         int lambdaType = LambdaType.encode(LambdaType.MASK_ARG_COUNT, type);
516         r.setFlags(PooledLambdaImpl.MASK_FUNC_TYPE, lambdaType);
517         r.setFlags(PooledLambdaImpl.MASK_EXPOSED_AS, lambdaType);
518         return r;
519     }
520 
acquire(Pool pool)521     static PooledLambdaImpl acquire(Pool pool) {
522         PooledLambdaImpl r = pool.acquire();
523         if (r == null) r = new PooledLambdaImpl();
524         r.mFlags &= ~FLAG_RECYCLED;
525         r.setFlags(FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL,
526                 pool == sMessageCallbacksPool ? 1 : 0);
527         return r;
528     }
529 
setIfInBounds(Object[] array, int i, Object a)530     private static void setIfInBounds(Object[] array, int i, Object a) {
531         if (i < ArrayUtils.size(array)) array[i] = a;
532     }
533 
534     @Override
535     public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
negate()536             R> negate() {
537         throw new UnsupportedOperationException();
538     }
539 
540     @Override
541     public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
andThen(Function<? super R, ? extends V> after)542             V> andThen(Function<? super R, ? extends V> after) {
543         throw new UnsupportedOperationException();
544     }
545 
546     @Override
getAsDouble()547     public double getAsDouble() {
548         return Double.longBitsToDouble(mConstValue);
549     }
550 
551     @Override
getAsInt()552     public int getAsInt() {
553         return (int) mConstValue;
554     }
555 
556     @Override
getAsLong()557     public long getAsLong() {
558         return mConstValue;
559     }
560 
561     @Override
562     public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
recycleOnUse()563             R> recycleOnUse() {
564         if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()");
565         mFlags |= FLAG_RECYCLE_ON_USE;
566         return this;
567     }
568 
isRecycled()569     private boolean isRecycled() {
570         return (mFlags & FLAG_RECYCLED) != 0;
571     }
572 
isRecycleOnUse()573     private boolean isRecycleOnUse() {
574         return (mFlags & FLAG_RECYCLE_ON_USE) != 0;
575     }
576 
isInvocationArgAtIndex(int argIndex)577     private boolean isInvocationArgAtIndex(int argIndex) {
578         return (mFlags & (1 << argIndex)) != 0;
579     }
580 
getFlags(int mask)581     int getFlags(int mask) {
582         return unmask(mask, mFlags);
583     }
584 
setFlags(int mask, int value)585     void setFlags(int mask, int value) {
586         mFlags &= ~mask;
587         mFlags |= mask(mask, value);
588     }
589 
590     /**
591      * 0xFF000, 0xAB -> 0xAB000
592      */
mask(int mask, int value)593     private static int mask(int mask, int value) {
594         return (value << Integer.numberOfTrailingZeros(mask)) & mask;
595     }
596 
597     /**
598      * 0xFF000, 0xAB123 -> 0xAB
599      */
unmask(int mask, int bits)600     private static int unmask(int mask, int bits) {
601         return (bits & mask) / (1 << Integer.numberOfTrailingZeros(mask));
602     }
603 
604     /**
605      * Contract for encoding a supported lambda type in {@link #MASK_BIT_COUNT} bits
606      */
607     static class LambdaType {
608         public static final int MASK_ARG_COUNT = 0b1111;
609         public static final int MASK_RETURN_TYPE = 0b1110000;
610         public static final int MASK = MASK_ARG_COUNT | MASK_RETURN_TYPE;
611         public static final int MASK_BIT_COUNT = 7;
612 
encode(int argCount, int returnType)613         static int encode(int argCount, int returnType) {
614             return mask(MASK_ARG_COUNT, argCount) | mask(MASK_RETURN_TYPE, returnType);
615         }
616 
decodeArgCount(int type)617         static int decodeArgCount(int type) {
618             return type & MASK_ARG_COUNT;
619         }
620 
decodeReturnType(int type)621         static int decodeReturnType(int type) {
622             return unmask(MASK_RETURN_TYPE, type);
623         }
624 
toString(int type)625         static String toString(int type) {
626             int argCount = decodeArgCount(type);
627             int returnType = decodeReturnType(type);
628             if (argCount == 0) {
629                 if (returnType == ReturnType.VOID) return "Runnable";
630                 if (returnType == ReturnType.OBJECT || returnType == ReturnType.BOOLEAN) {
631                     return "Supplier";
632                 }
633             }
634             return argCountPrefix(argCount) + ReturnType.lambdaSuffix(returnType);
635         }
636 
argCountPrefix(int argCount)637         private static String argCountPrefix(int argCount) {
638             switch (argCount) {
639                 case MASK_ARG_COUNT: return "";
640                 case 0: return "";
641                 case 1: return "";
642                 case 2: return "Bi";
643                 case 3: return "Tri";
644                 case 4: return "Quad";
645                 case 5: return "Quint";
646                 case 6: return "Hex";
647                 case 7: return "Hept";
648                 case 8: return "Oct";
649                 case 9: return "Nona";
650                 default: return "" + argCount + "arg";
651             }
652         }
653 
654         static class ReturnType {
655             public static final int VOID = 1;
656             public static final int BOOLEAN = 2;
657             public static final int OBJECT = 3;
658             public static final int INT = 4;
659             public static final int LONG = 5;
660             public static final int DOUBLE = 6;
661 
toString(int returnType)662             static String toString(int returnType) {
663                 switch (returnType) {
664                     case VOID: return "VOID";
665                     case BOOLEAN: return "BOOLEAN";
666                     case OBJECT: return "OBJECT";
667                     case INT: return "INT";
668                     case LONG: return "LONG";
669                     case DOUBLE: return "DOUBLE";
670                     default: return "" + returnType;
671                 }
672             }
673 
lambdaSuffix(int type)674             static String lambdaSuffix(int type) {
675                 return prefix(type) + suffix(type);
676             }
677 
prefix(int type)678             private static String prefix(int type) {
679                 switch (type) {
680                     case INT: return "Int";
681                     case LONG: return "Long";
682                     case DOUBLE: return "Double";
683                     default: return "";
684                 }
685             }
686 
suffix(int type)687             private static String suffix(int type) {
688                 switch (type) {
689                     case VOID: return "Consumer";
690                     case BOOLEAN: return "Predicate";
691                     case OBJECT: return "Function";
692                     default: return "Supplier";
693                 }
694             }
695         }
696     }
697 }
698