DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
CharUnicodeInfo.h
Go to the documentation of this file.
1#pragma once
2
5
6/*
7class CharUnicodeInfo
8{
9 //--------------------------------------------------------------------//
10 // Internal Information //
11 //--------------------------------------------------------------------//
12
13 //
14 // Native methods to access the Unicode category data tables in charinfo.nlp.
15 //
16 internal const char HIGH_SURROGATE_START = '\ud800';
17 internal const char HIGH_SURROGATE_END = '\udbff';
18 internal const char LOW_SURROGATE_START = '\udc00';
19 internal const char LOW_SURROGATE_END = '\udfff';
20
21 internal const int UNICODE_CATEGORY_OFFSET = 0;
22 internal const int BIDI_CATEGORY_OFFSET = 1;
23
24 static bool s_initialized = InitTable();
25
26 // The native pointer to the 12:4:4 index table of the Unicode cateogry data.
27 [SecurityCritical]
28 unsafe static ushort* s_pCategoryLevel1Index;
29 [SecurityCritical]
30 unsafe static byte* s_pCategoriesValue;
31
32 // The native pointer to the 12:4:4 index table of the Unicode numeric data.
33 // The value of this index table is an index into the real value table stored in s_pNumericValues.
34 [SecurityCritical]
35 unsafe static ushort* s_pNumericLevel1Index;
36
37 // The numeric value table, which is indexed by s_pNumericLevel1Index.
38 // Every item contains the value for numeric value.
39 // unsafe static double* s_pNumericValues;
40 // To get around the IA64 alignment issue. Our double data is aligned in 8-byte boundary, but loader loads the embeded table starting
41 // at 4-byte boundary. This cause a alignment issue since double is 8-byte.
42 [SecurityCritical]
43 unsafe static byte* s_pNumericValues;
44
45 // The digit value table, which is indexed by s_pNumericLevel1Index. It shares the same indice as s_pNumericValues.
46 // Every item contains the value for decimal digit/digit value.
47 [SecurityCritical]
48 unsafe static DigitValues* s_pDigitValues;
49
50 internal const String UNICODE_INFO_FILE_NAME = "charinfo.nlp";
51 // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff.
52 internal const int UNICODE_PLANE01_START = 0x10000;
53
54
55 //
56 // This is the header for the native data table that we load from UNICODE_INFO_FILE_NAME.
57 //
58 // Excplicit layout is used here since a syntax like char[16] can not be used in sequential layout.
59 [StructLayout(LayoutKind.Explicit)]
60 internal unsafe struct UnicodeDataHeader {
61 [FieldOffset(0)]
62 internal char TableName; // WCHAR[16]
63 [FieldOffset(0x20)]
64 internal ushort version; // WORD[4]
65 [FieldOffset(0x28)]
66 internal uint OffsetToCategoriesIndex; // DWORD
67 [FieldOffset(0x2c)]
68 internal uint OffsetToCategoriesValue; // DWORD
69 [FieldOffset(0x30)]
70 internal uint OffsetToNumbericIndex; // DWORD
71 [FieldOffset(0x34)]
72 internal uint OffsetToDigitValue; // DWORD
73 [FieldOffset(0x38)]
74 internal uint OffsetToNumbericValue; // DWORD
75
76 }
77
78 // NOTE: It's important to specify pack size here, since the size of the structure is 2 bytes. Otherwise,
79 // the default pack size will be 4.
80
81 [StructLayout(LayoutKind.Sequential, Pack = 2)]
82 internal struct DigitValues {
83 internal sbyte decimalDigit;
84 internal sbyte digit;
85 }
86
87
88 //We need to allocate the underlying table that provides us with the information that we
89 //use. We allocate this once in the class initializer and then we don't need to worry
90 //about it again.
91 //
92 [System.Security.SecuritySafeCritical] // auto-generated
93 [ResourceExposure(ResourceScope.None)]
94 [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
95 unsafe static bool InitTable() {
96
97 // Go to native side and get pointer to the native table
98 byte* pDataTable = GlobalizationAssembly.GetGlobalizationResourceBytePtr(typeof(CharUnicodeInfo).Assembly, UNICODE_INFO_FILE_NAME);
99
100 UnicodeDataHeader* mainHeader = (UnicodeDataHeader*)pDataTable;
101
102 // Set up the native pointer to different part of the tables.
103 s_pCategoryLevel1Index = (ushort*)(pDataTable + mainHeader->OffsetToCategoriesIndex);
104 s_pCategoriesValue = (byte*)(pDataTable + mainHeader->OffsetToCategoriesValue);
105 s_pNumericLevel1Index = (ushort*)(pDataTable + mainHeader->OffsetToNumbericIndex);
106 s_pNumericValues = (byte*)(pDataTable + mainHeader->OffsetToNumbericValue);
107 s_pDigitValues = (DigitValues*)(pDataTable + mainHeader->OffsetToDigitValue);
108
109 return true;
110 }
111
112
114 //
115 // Actions:
116 // Convert the BMP character or surrogate pointed by index to a UTF32 value.
117 // This is similar to Char.ConvertToUTF32, but the difference is that
118 // it does not throw exceptions when invalid surrogate characters are passed in.
119 //
120 // WARNING: since it doesn't throw an exception it CAN return a value
121 // in the surrogate range D800-DFFF, which are not legal unicode values.
122 //
124
125 internal static int InternalConvertToUtf32(String s, int index) {
126 Contract.Assert(s != null, "s != null");
127 Contract.Assert(index >= 0 && index < s.Length, "index < s.Length");
128 if (index < s.Length - 1) {
129 int temp1 = (int)s[index] - HIGH_SURROGATE_START;
130 if (temp1 >= 0 && temp1 <= 0x3ff) {
131 int temp2 = (int)s[index + 1] - LOW_SURROGATE_START;
132 if (temp2 >= 0 && temp2 <= 0x3ff) {
133 // Convert the surrogate to UTF32 and get the result.
134 return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START);
135 }
136 }
137 }
138 return ((int)s[index]);
139 }
140
142 //
143 // Convert a character or a surrogate pair starting at index of string s
144 // to UTF32 value.
145 //
146 // Parameters:
147 // s The string
148 // index The starting index. It can point to a BMP character or
149 // a surrogate pair.
150 // len The length of the string.
151 // charLength [out] If the index points to a BMP char, charLength
152 // will be 1. If the index points to a surrogate pair,
153 // charLength will be 2.
154 //
155 // WARNING: since it doesn't throw an exception it CAN return a value
156 // in the surrogate range D800-DFFF, which are not legal unicode values.
157 //
158 // Returns:
159 // The UTF32 value
160 //
162
163 internal static int InternalConvertToUtf32(String s, int index, out int charLength) {
164 Contract.Assert(s != null, "s != null");
165 Contract.Assert(s.Length > 0, "s.Length > 0");
166 Contract.Assert(index >= 0 && index < s.Length, "index >= 0 && index < s.Length");
167 charLength = 1;
168 if (index < s.Length - 1) {
169 int temp1 = (int)s[index] - HIGH_SURROGATE_START;
170 if (temp1 >= 0 && temp1 <= 0x3ff) {
171 int temp2 = (int)s[index + 1] - LOW_SURROGATE_START;
172 if (temp2 >= 0 && temp2 <= 0x3ff) {
173 // Convert the surrogate to UTF32 and get the result.
174 charLength++;
175 return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START);
176 }
177 }
178 }
179 return ((int)s[index]);
180 }
181
183 //
184 // IsWhiteSpace
185 //
186 // Determines if the given character is a white space character.
187 //
189
190 internal static bool IsWhiteSpace(String s, int index)
191 {
192 Contract.Assert(s != null, "s!=null");
193 Contract.Assert(index >= 0 && index < s.Length, "index >= 0 && index < s.Length");
194
195 UnicodeCategory uc = GetUnicodeCategory(s, index);
196 // In Unicode 3.0, U+2028 is the only character which is under the category "LineSeparator".
197 // And U+2029 is th eonly character which is under the category "ParagraphSeparator".
198 switch (uc) {
199 case (UnicodeCategory.SpaceSeparator):
200 case (UnicodeCategory.LineSeparator):
201 case (UnicodeCategory.ParagraphSeparator):
202 return (true);
203 }
204 return (false);
205 }
206
207
208 internal static bool IsWhiteSpace(char c)
209 {
210 UnicodeCategory uc = GetUnicodeCategory(c);
211 // In Unicode 3.0, U+2028 is the only character which is under the category "LineSeparator".
212 // And U+2029 is th eonly character which is under the category "ParagraphSeparator".
213 switch (uc) {
214 case (UnicodeCategory.SpaceSeparator):
215 case (UnicodeCategory.LineSeparator):
216 case (UnicodeCategory.ParagraphSeparator):
217 return (true);
218 }
219
220 return (false);
221 }
222
223 //
224 // This is called by the public char and string, index versions
225 //
226 // Note that for ch in the range D800-DFFF we just treat it as any other non-numeric character
227 //
228 [System.Security.SecuritySafeCritical] // auto-generated
229 internal unsafe static double InternalGetNumericValue(int ch) {
230 Contract.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range.");
231 // Get the level 2 item from the highest 12 bit (8 - 19) of ch.
232 ushort index = s_pNumericLevel1Index[ch >> 8];
233 // Get the level 2 WORD offset from the 4 - 7 bit of ch. This provides the base offset of the level 3 table.
234 // The offset is referred to an float item in m_pNumericFloatData.
235 // Note that & has the lower precedence than addition, so don't forget the parathesis.
236 index = s_pNumericLevel1Index[index + ((ch >> 4) & 0x000f)];
237 byte* pBytePtr = (byte*)&(s_pNumericLevel1Index[index]);
238 // Get the result from the 0 -3 bit of ch.
239#if WIN64
240 // To get around the IA64 alignment issue. Our double data is aligned in 8-byte boundary, but loader loads the embeded table starting
241 // at 4-byte boundary. This cause a alignment issue since double is 8-byte.
242 byte* pSourcePtr = &(s_pNumericValues[pBytePtr[(ch & 0x000f)] * sizeof(double)]);
243 if (((long)pSourcePtr % 8) != 0) {
244 // We are not aligned in 8-byte boundary. Do a copy.
245 double ret;
246 byte* retPtr = (byte*)&ret;
247 Buffer.Memcpy(retPtr, pSourcePtr, sizeof(double));
248 return (ret);
249 }
250 return (((double*)s_pNumericValues)[pBytePtr[(ch & 0x000f)]]);
251#else
252 return (((double*)s_pNumericValues)[pBytePtr[(ch & 0x000f)]]);
253#endif
254 }
255
256 //
257 // This is called by the public char and string, index versions
258 //
259 // Note that for ch in the range D800-DFFF we just treat it as any other non-numeric character
260 //
261 [System.Security.SecuritySafeCritical] // auto-generated
262 internal unsafe static DigitValues* InternalGetDigitValues(int ch) {
263 Contract.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range.");
264 // Get the level 2 item from the highest 12 bit (8 - 19) of ch.
265 ushort index = s_pNumericLevel1Index[ch >> 8];
266 // Get the level 2 WORD offset from the 4 - 7 bit of ch. This provides the base offset of the level 3 table.
267 // The offset is referred to an float item in m_pNumericFloatData.
268 // Note that & has the lower precedence than addition, so don't forget the parathesis.
269 index = s_pNumericLevel1Index[index + ((ch >> 4) & 0x000f)];
270 byte* pBytePtr = (byte*)&(s_pNumericLevel1Index[index]);
271 // Get the result from the 0 -3 bit of ch.
272 return &(s_pDigitValues[pBytePtr[(ch & 0x000f)]]);
273 }
274
275 [System.Security.SecuritySafeCritical] // auto-generated
276 internal unsafe static sbyte InternalGetDecimalDigitValue(int ch) {
277 return (InternalGetDigitValues(ch)->decimalDigit);
278 }
279
280 [System.Security.SecuritySafeCritical] // auto-generated
281 internal unsafe static sbyte InternalGetDigitValue(int ch) {
282 return (InternalGetDigitValues(ch)->digit);
283 }
284
285
287 //
288 //Returns the numeric value associated with the character c. If the character is a fraction,
289 // the return value will not be an integer. If the character does not have a numeric value, the return value is -1.
290 //
291 //Returns:
292 // the numeric value for the specified Unicode character. If the character does not have a numeric value, the return value is -1.
293 //Arguments:
294 // ch a Unicode character
295 //Exceptions:
296 // ArgumentNullException
297 // ArgumentOutOfRangeException
298 //
300
301
302 public static double GetNumericValue(char ch) {
303 return (InternalGetNumericValue(ch));
304 }
305
306
307 public static double GetNumericValue(String s, int index) {
308 if (s == null) {
309 throw new ArgumentNullException("s");
310 }
311 if (index < 0 || index >= s.Length) {
312 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
313 }
314 Contract.EndContractBlock();
315 return (InternalGetNumericValue(InternalConvertToUtf32(s, index)));
316
317 }
318
320 //
321 //Returns the decimal digit value associated with the character c.
322 //
323 // The value should be from 0 ~ 9.
324 // If the character does not have a numeric value, the return value is -1.
325 // From Unicode.org: Decimal Digits. Digits that can be used to form decimal-radix numbers.
326 //Returns:
327 // the decimal digit value for the specified Unicode character. If the character does not have a decimal digit value, the return value is -1.
328 //Arguments:
329 // ch a Unicode character
330 //Exceptions:
331 // ArgumentNullException
332 // ArgumentOutOfRangeException
333 //
335
336
337 public static int GetDecimalDigitValue(char ch) {
338 return (InternalGetDecimalDigitValue(ch));
339 }
340
341
342 public static int GetDecimalDigitValue(String s, int index) {
343 if (s == null) {
344 throw new ArgumentNullException("s");
345 }
346 if (index < 0 || index >= s.Length) {
347 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
348 }
349 Contract.EndContractBlock();
350
351 return (InternalGetDecimalDigitValue(InternalConvertToUtf32(s, index)));
352 }
353
355 //
356 //Action: Returns the digit value associated with the character c.
357 // If the character does not have a numeric value, the return value is -1.
358 // From Unicode.org: If the character represents a digit, not necessarily a decimal digit,
359 // the value is here. This covers digits which do not form decimal radix forms, such as the compatibility superscript digits.
360 //
361 // An example is: U+2460 IRCLED DIGIT ONE. This character has digit value 1, but does not have associcated decimal digit value.
362 //
363 //Returns:
364 // the digit value for the specified Unicode character. If the character does not have a digit value, the return value is -1.
365 //Arguments:
366 // ch a Unicode character
367 //Exceptions:
368 // ArgumentNullException
369 // ArgumentOutOfRangeException
370 //
372
373
374 public static int GetDigitValue(char ch) {
375 return (InternalGetDigitValue(ch));
376 }
377
378
379 public static int GetDigitValue(String s, int index) {
380 if (s == null) {
381 throw new ArgumentNullException("s");
382 }
383 if (index < 0 || index >= s.Length) {
384 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
385 }
386 Contract.EndContractBlock();
387 return (InternalGetDigitValue(InternalConvertToUtf32(s, index)));
388 }
389
390 public static UnicodeCategory GetUnicodeCategory(char ch)
391 {
392 return (InternalGetUnicodeCategory(ch));
393 }
394
395 public static UnicodeCategory GetUnicodeCategory(String s, int index)
396 {
397 if (s == null)
398 throw new ArgumentNullException("s");
399 if (((uint)index) >= ((uint)s.Length)) {
400 throw new ArgumentOutOfRangeException("index");
401 }
402 Contract.EndContractBlock();
403 return InternalGetUnicodeCategory(s, index);
404 }
405
406 internal unsafe static UnicodeCategory InternalGetUnicodeCategory(int ch) {
407 return ((UnicodeCategory)InternalGetCategoryValue(ch, UNICODE_CATEGORY_OFFSET));
408 }
409
411 //
412 //Action: Returns the Unicode Category property for the character c.
413 //Returns:
414 // an value in UnicodeCategory enum
415 //Arguments:
416 // ch a Unicode character
417 //Exceptions:
418 // None
419 //
420 //Note that this API will return values for D800-DF00 surrogate halves.
421 //
423
424 [System.Security.SecuritySafeCritical] // auto-generated
425 internal unsafe static byte InternalGetCategoryValue(int ch, int offset) {
426 Contract.Assert(ch >= 0 && ch <= 0x10ffff, "ch is not in valid Unicode range.");
427 // Get the level 2 item from the highest 12 bit (8 - 19) of ch.
428 ushort index = s_pCategoryLevel1Index[ch >> 8];
429 // Get the level 2 WORD offset from the 4 - 7 bit of ch. This provides the base offset of the level 3 table.
430 // Note that & has the lower precedence than addition, so don't forget the parathesis.
431 index = s_pCategoryLevel1Index[index + ((ch >> 4) & 0x000f)];
432 byte* pBytePtr = (byte*)&(s_pCategoryLevel1Index[index]);
433 // Get the result from the 0 -3 bit of ch.
434 byte valueIndex = pBytePtr[(ch & 0x000f)];
435 byte uc = s_pCategoriesValue[valueIndex * 2 + offset];
436 //
437 // Make sure that OtherNotAssigned is the last category in UnicodeCategory.
438 // If that changes, change the following assertion as well.
439 //
440 //Contract.Assert(uc >= 0 && uc <= UnicodeCategory.OtherNotAssigned, "Table returns incorrect Unicode category");
441 return (uc);
442 }
443
444 // internal static BidiCategory GetBidiCategory(char ch) {
445 // return ((BidiCategory)InternalGetCategoryValue(c, BIDI_CATEGORY_OFFSET));
446 // }
447
448 internal static BidiCategory GetBidiCategory(String s, int index) {
449 if (s == null)
450 throw new ArgumentNullException("s");
451 if (((uint)index) >= ((uint)s.Length)) {
452 throw new ArgumentOutOfRangeException("index");
453 }
454 Contract.EndContractBlock();
455 return ((BidiCategory)InternalGetCategoryValue(InternalConvertToUtf32(s, index), BIDI_CATEGORY_OFFSET));
456 }
457
459 //
460 //Action: Returns the Unicode Category property for the character c.
461 //Returns:
462 // an value in UnicodeCategory enum
463 //Arguments:
464 // value a Unicode String
465 // index Index for the specified string.
466 //Exceptions:
467 // None
468 //
470
471 internal static UnicodeCategory InternalGetUnicodeCategory(String value, int index) {
472 Contract.Assert(value != null, "value can not be null");
473 Contract.Assert(index < value.Length, "index < value.Length");
474
475 return (InternalGetUnicodeCategory(InternalConvertToUtf32(value, index)));
476 }
477
479 //
480 // Get the Unicode category of the character starting at index. If the character is in BMP, charLength will return 1.
481 // If the character is a valid surrogate pair, charLength will return 2.
482 //
484
485 internal static UnicodeCategory InternalGetUnicodeCategory(String str, int index, out int charLength) {
486 Contract.Assert(str != null, "str can not be null");
487 Contract.Assert(str.Length > 0, "str.Length > 0");;
488 Contract.Assert(index >= 0 && index < str.Length, "index >= 0 && index < str.Length");
489
490 return (InternalGetUnicodeCategory(InternalConvertToUtf32(str, index, out charLength)));
491 }
492
493 internal static bool IsCombiningCategory(UnicodeCategory uc) {
494 Contract.Assert(uc >= 0, "uc >= 0");
495 return (
496 uc == UnicodeCategory.NonSpacingMark ||
497 uc == UnicodeCategory.SpacingCombiningMark ||
498 uc == UnicodeCategory.EnclosingMark
499 );
500 }
501};
502*/