View Javadoc
1   /*
2    * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this program; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17   *
18   * Additionally licensed with:
19   *
20   * Licensed under the Apache License, Version 2.0 (the "License");
21   * you may not use this file except in compliance with the License.
22   * You may obtain a copy of the License at
23   *
24   *      http://www.apache.org/licenses/LICENSE-2.0
25   *
26   * Unless required by applicable law or agreed to in writing, software
27   * distributed under the License is distributed on an "AS IS" BASIS,
28   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29   * See the License for the specific language governing permissions and
30   * limitations under the License.
31   */
32  package org.spf4j.base;
33  
34  import java.lang.invoke.MethodHandle;
35  import java.lang.invoke.MethodHandles;
36  import java.lang.reflect.Constructor;
37  import java.lang.reflect.Field;
38  import java.security.AccessController;
39  import java.security.PrivilegedAction;
40  import java.util.logging.Level;
41  import java.util.logging.Logger;
42  
43  /**
44   * Lower lever unsafe hacks for performance improvement.
45   * These optimizations will work only up to including JDK 8.
46   * @author Zoltan Farkas
47   */
48  public final class UnsafeString {
49  
50      /**
51     * This field is byte[] in JDK11.
52     */
53    private static final MethodHandle CHARS_FIELD_GET;
54  
55    //String(char[] value, boolean share) {
56    private static final MethodHandle PROTECTED_STR_CONSTR_HANDLE;
57    private static final Class<?>[] PROTECTED_STR_CONSTR_PARAM_TYPES;
58  
59    static {
60      Field charsField;
61      charsField = AccessController.doPrivileged(new PrivilegedAction<Field>() {
62        @Override
63        public Field run() {
64          Field charsField;
65          try {
66            charsField = String.class.getDeclaredField("value");
67            Class<?> type = charsField.getType();
68            if (type.isArray() && type.getComponentType() == char.class) {
69              charsField.setAccessible(true);
70            } else {
71              Logger logger = Logger.getLogger(Strings.class.getName());
72              logger.warning("char array stealing from String not supported");
73              charsField = null;
74            }
75          } catch (NoSuchFieldException ex) {
76            Logger logger = Logger.getLogger(Strings.class.getName());
77            logger.warning("char array stealing from String not supported");
78            logger.log(Level.FINEST, "Exception detail", ex);
79            charsField = null;
80          }
81          return charsField;
82        }
83      });
84      MethodHandles.Lookup lookup = MethodHandles.lookup();
85      if (charsField != null && char[].class == charsField.getType()) {
86        try {
87          CHARS_FIELD_GET = lookup.unreflectGetter(charsField);
88        } catch (IllegalAccessException ex) {
89          throw new ExceptionInInitializerError(ex);
90        }
91      } else {
92        CHARS_FIELD_GET = null;
93      }
94  
95      // up until u45 String(int offset, int count, char value[]) {
96      // u45 reverted to: String(char[] value, boolean share) {
97      Constructor<String> prConstr = AccessController.doPrivileged(
98              new PrivilegedAction<Constructor<String>>() {
99        @Override
100       public Constructor<String> run() {
101         Constructor<String> constr;
102         try {
103           constr = String.class.getDeclaredConstructor(char[].class, boolean.class);
104           constr.setAccessible(true);
105         } catch (NoSuchMethodException ex) {
106           try {
107             constr = String.class.getDeclaredConstructor(int.class, int.class, char[].class);
108             constr.setAccessible(true);
109           } catch (NoSuchMethodException ex2) {
110             ex2.addSuppressed(ex);
111             Logger logger = Logger.getLogger(Strings.class.getName());
112             logger.log(Level.FINE, "Building String from char[] without copy not supported");
113             logger.log(Level.FINEST, "Exception detail", ex2);
114             constr = null;
115           }
116         }
117         return constr;
118       }
119     });
120 
121     if (prConstr == null) {
122       PROTECTED_STR_CONSTR_PARAM_TYPES = null;
123       PROTECTED_STR_CONSTR_HANDLE = null;
124     } else {
125       PROTECTED_STR_CONSTR_PARAM_TYPES = prConstr.getParameterTypes();
126       try {
127         PROTECTED_STR_CONSTR_HANDLE = lookup.unreflectConstructor(prConstr);
128       } catch (IllegalAccessException ex) {
129         throw new ExceptionInInitializerError(ex);
130       }
131     }
132   }
133 
134 
135 
136   private UnsafeString() { }
137 
138   /**
139    * Steal the underlying character array of a String.
140    *
141    * @param str
142    * @return
143    */
144   public static char[] steal(final String str) {
145     if (CHARS_FIELD_GET == null) {
146       return str.toCharArray();
147     } else {
148       try {
149         return (char[]) CHARS_FIELD_GET.invokeExact(str);
150       } catch (Throwable ex) {
151         UnknownError err = new UnknownError("Error while stealing String char array");
152         err.addSuppressed(ex);
153         throw err;
154       }
155     }
156   }
157 
158    /**
159    * Create a String based on the provided character array. No copy of the array is made.
160    *
161    * @param chars
162    * @return
163    */
164   public static String wrap(final char[] chars) {
165     if (PROTECTED_STR_CONSTR_PARAM_TYPES != null) {
166       try {
167         if (PROTECTED_STR_CONSTR_PARAM_TYPES.length == 3) {
168           return (String) PROTECTED_STR_CONSTR_HANDLE.invokeExact(0, chars.length, chars);
169         } else {
170           return (String) PROTECTED_STR_CONSTR_HANDLE.invokeExact(chars, true);
171         }
172       } catch (Throwable ex) {
173         UnknownError err = new UnknownError("Error while wrapping String char array");
174         err.addSuppressed(ex);
175         throw err;
176       }
177     } else {
178       return new String(chars);
179     }
180   }
181 
182 }