• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 #include "utf.h"
18 
19 #include <android-base/logging.h>
20 #include <android-base/stringprintf.h>
21 #include <android-base/strings.h>
22 
23 #include "base/casts.h"
24 #include "utf-inl.h"
25 
26 namespace art {
27 
28 using android::base::StringAppendF;
29 
30 // This is used only from debugger and test code.
CountModifiedUtf8Chars(const char * utf8)31 size_t CountModifiedUtf8Chars(const char* utf8) {
32   return CountModifiedUtf8Chars(utf8, strlen(utf8));
33 }
34 
35 /*
36  * This does not validate UTF8 rules (nor did older code). But it gets the right answer
37  * for valid UTF-8 and that's fine because it's used only to size a buffer for later
38  * conversion.
39  *
40  * Modified UTF-8 consists of a series of bytes up to 21 bit Unicode code points as follows:
41  * U+0001  - U+007F   0xxxxxxx
42  * U+0080  - U+07FF   110xxxxx 10xxxxxx
43  * U+0800  - U+FFFF   1110xxxx 10xxxxxx 10xxxxxx
44  * U+10000 - U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
45  *
46  * U+0000 is encoded using the 2nd form to avoid nulls inside strings (this differs from
47  * standard UTF-8).
48  * The four byte encoding converts to two utf16 characters.
49  */
CountModifiedUtf8Chars(const char * utf8,size_t byte_count)50 size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) {
51   DCHECK_LE(byte_count, strlen(utf8));
52   size_t len = 0;
53   const char* end = utf8 + byte_count;
54   for (; utf8 < end; ++utf8) {
55     int ic = *utf8;
56     len++;
57     if (LIKELY((ic & 0x80) == 0)) {
58       // One-byte encoding.
59       continue;
60     }
61     // Two- or three-byte encoding.
62     utf8++;
63     if ((ic & 0x20) == 0) {
64       // Two-byte encoding.
65       continue;
66     }
67     utf8++;
68     if ((ic & 0x10) == 0) {
69       // Three-byte encoding.
70       continue;
71     }
72 
73     // Four-byte encoding: needs to be converted into a surrogate
74     // pair.
75     utf8++;
76     len++;
77   }
78   return len;
79 }
80 
81 // This is used only from debugger and test code.
ConvertModifiedUtf8ToUtf16(uint16_t * utf16_data_out,const char * utf8_data_in)82 void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, const char* utf8_data_in) {
83   while (*utf8_data_in != '\0') {
84     const uint32_t ch = GetUtf16FromUtf8(&utf8_data_in);
85     const uint16_t leading = GetLeadingUtf16Char(ch);
86     const uint16_t trailing = GetTrailingUtf16Char(ch);
87 
88     *utf16_data_out++ = leading;
89     if (trailing != 0) {
90       *utf16_data_out++ = trailing;
91     }
92   }
93 }
94 
ConvertModifiedUtf8ToUtf16(uint16_t * utf16_data_out,size_t out_chars,const char * utf8_data_in,size_t in_bytes)95 void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_data_out, size_t out_chars,
96                                 const char* utf8_data_in, size_t in_bytes) {
97   const char *in_start = utf8_data_in;
98   const char *in_end = utf8_data_in + in_bytes;
99   uint16_t *out_p = utf16_data_out;
100 
101   if (LIKELY(out_chars == in_bytes)) {
102     // Common case where all characters are ASCII.
103     for (const char *p = in_start; p < in_end;) {
104       // Safe even if char is signed because ASCII characters always have
105       // the high bit cleared.
106       *out_p++ = dchecked_integral_cast<uint16_t>(*p++);
107     }
108     return;
109   }
110 
111   // String contains non-ASCII characters.
112   for (const char *p = in_start; p < in_end;) {
113     const uint32_t ch = GetUtf16FromUtf8(&p);
114     const uint16_t leading = GetLeadingUtf16Char(ch);
115     const uint16_t trailing = GetTrailingUtf16Char(ch);
116 
117     *out_p++ = leading;
118     if (trailing != 0) {
119       *out_p++ = trailing;
120     }
121   }
122 }
123 
ConvertUtf16ToModifiedUtf8(char * utf8_out,size_t byte_count,const uint16_t * utf16_in,size_t char_count)124 void ConvertUtf16ToModifiedUtf8(char* utf8_out, size_t byte_count,
125                                 const uint16_t* utf16_in, size_t char_count) {
126   if (LIKELY(byte_count == char_count)) {
127     // Common case where all characters are ASCII.
128     const uint16_t *utf16_end = utf16_in + char_count;
129     for (const uint16_t *p = utf16_in; p < utf16_end;) {
130       *utf8_out++ = dchecked_integral_cast<char>(*p++);
131     }
132     return;
133   }
134 
135   // String contains non-ASCII characters.
136   // FIXME: We should not emit 4-byte sequences. Bug: 192935764
137   auto append = [&](char c) { *utf8_out++ = c; };
138   ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
139                      /*kUse4ByteSequence=*/ true,
140                      /*kReplaceBadSurrogates=*/ false>(utf16_in, char_count, append);
141 }
142 
ComputeUtf16HashFromModifiedUtf8(const char * utf8,size_t utf16_length)143 int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length) {
144   uint32_t hash = 0;
145   while (utf16_length != 0u) {
146     const uint32_t pair = GetUtf16FromUtf8(&utf8);
147     const uint16_t first = GetLeadingUtf16Char(pair);
148     hash = hash * 31 + first;
149     --utf16_length;
150     const uint16_t second = GetTrailingUtf16Char(pair);
151     if (second != 0) {
152       hash = hash * 31 + second;
153       DCHECK_NE(utf16_length, 0u);
154       --utf16_length;
155     }
156   }
157   return static_cast<int32_t>(hash);
158 }
159 
ComputeModifiedUtf8Hash(const char * chars)160 uint32_t ComputeModifiedUtf8Hash(const char* chars) {
161   uint32_t hash = StartModifiedUtf8Hash();
162   while (*chars != '\0') {
163     hash = UpdateModifiedUtf8Hash(hash, *chars);
164     ++chars;
165   }
166   return hash;
167 }
168 
ComputeModifiedUtf8Hash(std::string_view chars)169 uint32_t ComputeModifiedUtf8Hash(std::string_view chars) {
170   return UpdateModifiedUtf8Hash(StartModifiedUtf8Hash(), chars);
171 }
172 
CompareModifiedUtf8ToUtf16AsCodePointValues(const char * utf8,const uint16_t * utf16,size_t utf16_length)173 int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8, const uint16_t* utf16,
174                                                 size_t utf16_length) {
175   for (;;) {
176     if (*utf8 == '\0') {
177       return (utf16_length == 0) ? 0 : -1;
178     } else if (utf16_length == 0) {
179       return 1;
180     }
181 
182     const uint32_t pair = GetUtf16FromUtf8(&utf8);
183 
184     // First compare the leading utf16 char.
185     const uint16_t lhs = GetLeadingUtf16Char(pair);
186     const uint16_t rhs = *utf16++;
187     --utf16_length;
188     if (lhs != rhs) {
189       return lhs > rhs ? 1 : -1;
190     }
191 
192     // Then compare the trailing utf16 char. First check if there
193     // are any characters left to consume.
194     const uint16_t lhs2 = GetTrailingUtf16Char(pair);
195     if (lhs2 != 0) {
196       if (utf16_length == 0) {
197         return 1;
198       }
199 
200       const uint16_t rhs2 = *utf16++;
201       --utf16_length;
202       if (lhs2 != rhs2) {
203         return lhs2 > rhs2 ? 1 : -1;
204       }
205     }
206   }
207 }
208 
CountModifiedUtf8BytesInUtf16(const uint16_t * chars,size_t char_count)209 size_t CountModifiedUtf8BytesInUtf16(const uint16_t* chars, size_t char_count) {
210   // FIXME: We should not emit 4-byte sequences. Bug: 192935764
211   size_t result = 0;
212   auto append = [&](char c ATTRIBUTE_UNUSED) { ++result; };
213   ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
214                      /*kUse4ByteSequence=*/ true,
215                      /*kReplaceBadSurrogates=*/ false>(chars, char_count, append);
216   return result;
217 }
218 
NeedsEscaping(uint16_t ch)219 static inline constexpr bool NeedsEscaping(uint16_t ch) {
220   return (ch < ' ' || ch > '~');
221 }
222 
PrintableChar(uint16_t ch)223 std::string PrintableChar(uint16_t ch) {
224   std::string result;
225   result += '\'';
226   if (NeedsEscaping(ch)) {
227     StringAppendF(&result, "\\u%04x", ch);
228   } else {
229     result += static_cast<std::string::value_type>(ch);
230   }
231   result += '\'';
232   return result;
233 }
234 
PrintableString(const char * utf8)235 std::string PrintableString(const char* utf8) {
236   std::string result;
237   result += '"';
238   const char* p = utf8;
239   size_t char_count = CountModifiedUtf8Chars(p);
240   for (size_t i = 0; i < char_count; ++i) {
241     uint32_t ch = GetUtf16FromUtf8(&p);
242     if (ch == '\\') {
243       result += "\\\\";
244     } else if (ch == '\n') {
245       result += "\\n";
246     } else if (ch == '\r') {
247       result += "\\r";
248     } else if (ch == '\t') {
249       result += "\\t";
250     } else {
251       const uint16_t leading = GetLeadingUtf16Char(ch);
252 
253       if (NeedsEscaping(leading)) {
254         StringAppendF(&result, "\\u%04x", leading);
255       } else {
256         result += static_cast<std::string::value_type>(leading);
257       }
258 
259       const uint32_t trailing = GetTrailingUtf16Char(ch);
260       if (trailing != 0) {
261         // All high surrogates will need escaping.
262         StringAppendF(&result, "\\u%04x", trailing);
263         // Account for the surrogate pair.
264         ++i;
265         DCHECK_LT(i, char_count);
266       }
267     }
268   }
269   result += '"';
270   return result;
271 }
272 
273 }  // namespace art
274