001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.util;
018
019/**
020 * Utility for preventing primitive parameter values from being auto-boxed. Auto-boxing creates temporary objects
021 * which contribute to pressure on the garbage collector. With this utility users can convert primitive values directly
022 * into text without allocating temporary objects.
023 * <p>
024 * Example usage:
025 * </p><pre>
026 * import static org.apache.logging.log4j.util.Unbox.box;
027 * ...
028 * long longValue = 123456L;
029 * double doubleValue = 3.14;
030 * // prevent primitive values from being auto-boxed
031 * logger.debug("Long value={}, double value={}", box(longValue), box(doubleValue));
032 * </pre>
033 */
034@PerformanceSensitive("allocation")
035public class Unbox {
036    private static final int MASK = 16 - 1;
037
038    private static class State {
039        private final StringBuilder[] ringbuffer = new StringBuilder[16];
040        private int current;
041        State() {
042            for (int i = 0; i < ringbuffer.length; i++) {
043                ringbuffer[i] = new StringBuilder(21);
044            }
045        }
046
047        public StringBuilder getStringBuilder() {
048            final StringBuilder result = ringbuffer[MASK & current++];
049            result.setLength(0);
050            return result;
051        }
052
053        public boolean isBoxedPrimitive(final StringBuilder text) {
054            for (int i = 0; i < ringbuffer.length; i++) {
055                if (text == ringbuffer[i]) {
056                    return true;
057                }
058            }
059            return false;
060        }
061    }
062    private static ThreadLocal<State> threadLocalState = new ThreadLocal<>();
063
064    /**
065     * Returns a {@code StringBuilder} containing the text representation of the specified primitive value.
066     * This method will not allocate temporary objects.
067     *
068     * @param value the value whose text representation to return
069     * @return a {@code StringBuilder} containing the text representation of the specified primitive value
070     */
071    @PerformanceSensitive("allocation")
072    public static StringBuilder box(float value) {
073        return getSB().append(value);
074    }
075
076    /**
077     * Returns a {@code StringBuilder} containing the text representation of the specified primitive value.
078     * This method will not allocate temporary objects.
079     *
080     * @param value the value whose text representation to return
081     * @return a {@code StringBuilder} containing the text representation of the specified primitive value
082     */
083    @PerformanceSensitive("allocation")
084    public static StringBuilder box(double value) {
085        return getSB().append(value);
086    }
087
088    /**
089     * Returns a {@code StringBuilder} containing the text representation of the specified primitive value.
090     * This method will not allocate temporary objects.
091     *
092     * @param value the value whose text representation to return
093     * @return a {@code StringBuilder} containing the text representation of the specified primitive value
094     */
095    @PerformanceSensitive("allocation")
096    public static StringBuilder box(short value) {
097        return getSB().append(value);
098    }
099
100    /**
101     * Returns a {@code StringBuilder} containing the text representation of the specified primitive value.
102     * This method will not allocate temporary objects.
103     *
104     * @param value the value whose text representation to return
105     * @return a {@code StringBuilder} containing the text representation of the specified primitive value
106     */
107    @PerformanceSensitive("allocation")
108    public static StringBuilder box(int value) {
109        return getSB().append(value);
110    }
111
112    /**
113     * Returns a {@code StringBuilder} containing the text representation of the specified primitive value.
114     * This method will not allocate temporary objects.
115     *
116     * @param value the value whose text representation to return
117     * @return a {@code StringBuilder} containing the text representation of the specified primitive value
118     */
119    @PerformanceSensitive("allocation")
120    public static StringBuilder box(char value) {
121        return getSB().append(value);
122    }
123
124    /**
125     * Returns a {@code StringBuilder} containing the text representation of the specified primitive value.
126     * This method will not allocate temporary objects.
127     *
128     * @param value the value whose text representation to return
129     * @return a {@code StringBuilder} containing the text representation of the specified primitive value
130     */
131    @PerformanceSensitive("allocation")
132    public static StringBuilder box(long value) {
133        return getSB().append(value);
134    }
135
136    /**
137     * Returns a {@code StringBuilder} containing the text representation of the specified primitive value.
138     * This method will not allocate temporary objects.
139     *
140     * @param value the value whose text representation to return
141     * @return a {@code StringBuilder} containing the text representation of the specified primitive value
142     */
143    @PerformanceSensitive("allocation")
144    public static StringBuilder box(byte value) {
145        return getSB().append(value);
146    }
147
148    /**
149     * Returns a {@code StringBuilder} containing the text representation of the specified primitive value.
150     * This method will not allocate temporary objects.
151     *
152     * @param value the value whose text representation to return
153     * @return a {@code StringBuilder} containing the text representation of the specified primitive value
154     */
155    @PerformanceSensitive("allocation")
156    public static StringBuilder box(boolean value) {
157        return getSB().append(value);
158    }
159
160    public static boolean isBoxedPrimitive(final StringBuilder text) {
161        return getState().isBoxedPrimitive(text);
162    }
163
164    private static State getState() {
165        State state = threadLocalState.get();
166        if (state == null) {
167            state = new State();
168            threadLocalState.set(state);
169        }
170        return state;
171    }
172
173    private static StringBuilder getSB() {
174        return getState().getStringBuilder();
175    }
176}