View Javadoc
1   /*
2    * Copyright 2024 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.netty.util;
17  
18  import io.netty.util.internal.PlatformDependent;
19  import io.netty.util.internal.SWARUtil;
20  
21  /**
22   * A collection of utility methods that is related with handling {@link AsciiString}.
23   */
24  final class AsciiStringUtil {
25  
26      /**
27       * Convert the {@link AsciiString} to a lower case.
28       *
29       * @param string the {@link AsciiString} to convert
30       * @return the new {@link AsciiString} in lower case
31       */
32      static AsciiString toLowerCase(final AsciiString string) {
33          final byte[] byteArray = string.array();
34          final int offset = string.arrayOffset();
35          final int length = string.length();
36          if (!containsUpperCase(byteArray, offset, length)) {
37              return string;
38          }
39          final byte[] newByteArray = PlatformDependent.allocateUninitializedArray(length);
40          toLowerCase(byteArray, offset, newByteArray);
41          return new AsciiString(newByteArray, false);
42      }
43  
44      private static boolean containsUpperCase(final byte[] byteArray, int offset, final int length) {
45          if (!PlatformDependent.isUnaligned()) {
46              return linearContainsUpperCase(byteArray, offset, length);
47          }
48  
49          final int longCount = length >>> 3;
50          for (int i = 0; i < longCount; ++i) {
51              final long word = PlatformDependent.getLong(byteArray, offset);
52              if (SWARUtil.containsUpperCase(word)) {
53                  return true;
54              }
55              offset += Long.BYTES;
56          }
57          return unrolledContainsUpperCase(byteArray, offset, length & 7);
58      }
59  
60      private static boolean linearContainsUpperCase(final byte[] byteArray, final int offset, final int length) {
61          final int end = offset + length;
62          for (int idx = offset; idx < end; ++idx) {
63              if (isUpperCase(byteArray[idx])) {
64                  return true;
65              }
66          }
67          return false;
68      }
69  
70      private static boolean unrolledContainsUpperCase(final byte[] byteArray, int offset, final int byteCount) {
71          assert byteCount >= 0 && byteCount < 8;
72          if ((byteCount & Integer.BYTES) != 0) {
73              final int word = PlatformDependent.getInt(byteArray, offset);
74              if (SWARUtil.containsUpperCase(word)) {
75                  return true;
76              }
77              offset += Integer.BYTES;
78          }
79          if ((byteCount & Short.BYTES) != 0) {
80              if (isUpperCase(PlatformDependent.getByte(byteArray, offset))) {
81                  return true;
82              }
83              if (isUpperCase(PlatformDependent.getByte(byteArray, offset + 1))) {
84                  return true;
85              }
86              offset += Short.BYTES;
87          }
88          if ((byteCount & Byte.BYTES) != 0) {
89              return isUpperCase(PlatformDependent.getByte(byteArray, offset));
90          }
91          return false;
92      }
93  
94      private static void toLowerCase(final byte[] src, final int srcOffset, final byte[] dst) {
95          if (!PlatformDependent.isUnaligned()) {
96              linearToLowerCase(src, srcOffset, dst);
97              return;
98          }
99  
100         final int length = dst.length;
101         final int longCount = length >>> 3;
102         int offset = 0;
103         for (int i = 0; i < longCount; ++i) {
104             final long word = PlatformDependent.getLong(src, srcOffset + offset);
105             PlatformDependent.putLong(dst, offset, SWARUtil.toLowerCase(word));
106             offset += Long.BYTES;
107         }
108         unrolledToLowerCase(src, srcOffset + offset, dst, offset, length & 7);
109     }
110 
111     private static void linearToLowerCase(final byte[] src, final int srcOffset, final byte[] dst) {
112         for (int i = 0; i < dst.length; ++i) {
113             dst[i] = toLowerCase(src[srcOffset + i]);
114         }
115     }
116 
117     private static void unrolledToLowerCase(final byte[] src, int srcPos,
118                                             final byte[] dst, int dstOffset, final int byteCount) {
119         assert byteCount >= 0 && byteCount < 8;
120         int offset = 0;
121         if ((byteCount & Integer.BYTES) != 0) {
122             final int word = PlatformDependent.getInt(src, srcPos + offset);
123             PlatformDependent.putInt(dst, dstOffset + offset, SWARUtil.toLowerCase(word));
124             offset += Integer.BYTES;
125         }
126 
127         if ((byteCount & Short.BYTES) != 0) {
128             final short word = PlatformDependent.getShort(src, srcPos + offset);
129             final short result = (short) ((toLowerCase((byte) (word >>> 8)) << 8) | toLowerCase((byte) word));
130             PlatformDependent.putShort(dst, dstOffset + offset, result);
131             offset += Short.BYTES;
132         }
133 
134         // this is equivalent to byteCount >= Byte.BYTES (i.e. whether byteCount is odd)
135         if ((byteCount & Byte.BYTES) != 0) {
136             PlatformDependent.putByte(dst, dstOffset + offset,
137                                       toLowerCase(PlatformDependent.getByte(src, srcPos + offset)));
138         }
139     }
140 
141     /**
142      * Convert the {@link AsciiString} to a upper case.
143      *
144      * @param string the {@link AsciiString} to convert
145      * @return the {@link AsciiString} in upper case
146      */
147     static AsciiString toUpperCase(final AsciiString string) {
148         final byte[] byteArray = string.array();
149         final int offset = string.arrayOffset();
150         final int length = string.length();
151         if (!containsLowerCase(byteArray, offset, length)) {
152             return string;
153         }
154         final byte[] newByteArray = PlatformDependent.allocateUninitializedArray(length);
155         toUpperCase(byteArray, offset, newByteArray);
156         return new AsciiString(newByteArray, false);
157     }
158 
159     private static boolean containsLowerCase(final byte[] byteArray, int offset, final int length) {
160         if (!PlatformDependent.isUnaligned()) {
161             return linearContainsLowerCase(byteArray, offset, length);
162         }
163 
164         final int longCount = length >>> 3;
165         for (int i = 0; i < longCount; ++i) {
166             final long word = PlatformDependent.getLong(byteArray, offset);
167             if (SWARUtil.containsLowerCase(word)) {
168                 return true;
169             }
170             offset += Long.BYTES;
171         }
172         return unrolledContainsLowerCase(byteArray, offset, length & 7);
173     }
174 
175     private static boolean linearContainsLowerCase(final byte[] byteArray, final int offset, final int length) {
176         final int end = offset + length;
177         for (int idx = offset; idx < end; ++idx) {
178             if (isLowerCase(byteArray[idx])) {
179                 return true;
180             }
181         }
182         return false;
183     }
184 
185     private static boolean unrolledContainsLowerCase(final byte[] byteArray, int offset, final int byteCount) {
186         assert byteCount >= 0 && byteCount < 8;
187         if ((byteCount & Integer.BYTES) != 0) {
188             final int word = PlatformDependent.getInt(byteArray, offset);
189             if (SWARUtil.containsLowerCase(word)) {
190                 return true;
191             }
192             offset += Integer.BYTES;
193         }
194         if ((byteCount & Short.BYTES) != 0) {
195             if (isLowerCase(PlatformDependent.getByte(byteArray, offset))) {
196                 return true;
197             }
198             if (isLowerCase(PlatformDependent.getByte(byteArray, offset + 1))) {
199                 return true;
200             }
201             offset += Short.BYTES;
202         }
203         if ((byteCount & Byte.BYTES) != 0) {
204             return isLowerCase(PlatformDependent.getByte(byteArray, offset));
205         }
206         return false;
207     }
208 
209     private static void toUpperCase(final byte[] src, final int srcOffset, final byte[] dst) {
210         if (!PlatformDependent.isUnaligned()) {
211             linearToUpperCase(src, srcOffset, dst);
212             return;
213         }
214 
215         final int length = dst.length;
216         final int longCount = length >>> 3;
217         int offset = 0;
218         for (int i = 0; i < longCount; ++i) {
219             final long word = PlatformDependent.getLong(src, srcOffset + offset);
220             PlatformDependent.putLong(dst, offset, SWARUtil.toUpperCase(word));
221             offset += Long.BYTES;
222         }
223         unrolledToUpperCase(src, srcOffset + offset, dst, offset, length & 7);
224     }
225 
226     private static void linearToUpperCase(final byte[] src, final int srcOffset, final byte[] dst) {
227         for (int i = 0; i < dst.length; ++i) {
228             dst[i] = toUpperCase(src[srcOffset + i]);
229         }
230     }
231 
232     private static void unrolledToUpperCase(final byte[] src, int srcOffset,
233                                             final byte[] dst, int dstOffset, final int byteCount) {
234         assert byteCount >= 0 && byteCount < 8;
235         int offset = 0;
236         if ((byteCount & Integer.BYTES) != 0) {
237             final int word = PlatformDependent.getInt(src, srcOffset + offset);
238             PlatformDependent.putInt(dst, dstOffset + offset, SWARUtil.toUpperCase(word));
239             offset += Integer.BYTES;
240         }
241         if ((byteCount & Short.BYTES) != 0) {
242             final short word = PlatformDependent.getShort(src, srcOffset + offset);
243             final short result = (short) ((toUpperCase((byte) (word >>> 8)) << 8) | toUpperCase((byte) word));
244             PlatformDependent.putShort(dst, dstOffset + offset, result);
245             offset += Short.BYTES;
246         }
247 
248         if ((byteCount & Byte.BYTES) != 0) {
249             PlatformDependent.putByte(dst, dstOffset + offset,
250                                       toUpperCase(PlatformDependent.getByte(src, srcOffset + offset)));
251         }
252     }
253 
254     private static boolean isLowerCase(final byte value) {
255         return value >= 'a' && value <= 'z';
256     }
257 
258     /**
259      * Check if the given byte is upper case.
260      *
261      * @param value the byte to check
262      * @return {@code true} if the byte is upper case, {@code false} otherwise.
263      */
264     static boolean isUpperCase(final byte value) {
265         return value >= 'A' && value <= 'Z';
266     }
267 
268     /**
269      * Convert the given byte to lower case.
270      *
271      * @param value the byte to convert
272      * @return the lower case byte
273      */
274     static byte toLowerCase(final byte value) {
275         return isUpperCase(value)? (byte) (value + 32) : value;
276     }
277 
278     /**
279      * Convert the given byte to upper case.
280      *
281      * @param value the byte to convert
282      * @return the upper case byte
283      */
284     static byte toUpperCase(final byte value) {
285         return isLowerCase(value)? (byte) (value - 32) : value;
286     }
287 
288     private AsciiStringUtil() {
289         // Utility
290     }
291 }