1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 ******************************************************************************
5 *
6 *   Copyright (C) 1998-2014, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 ******************************************************************************
10 *
11 * File ufmt_cmn.c
12 *
13 * Modification History:
14 *
15 *   Date        Name        Description
16 *   12/02/98    stephen     Creation.
17 *   03/12/99    stephen     Modified for new C API.
18 *   03/15/99    stephen     Added defaultCPToUnicode, unicodeToDefaultCP
19 *   07/19/99    stephen     Fixed bug in defaultCPToUnicode
20 ******************************************************************************
21 */
22 
23 #include "cstring.h"
24 #include "cmemory.h"
25 #include "ufmt_cmn.h"
26 #include "unicode/uchar.h"
27 #include "unicode/ucnv.h"
28 #include "ustr_cnv.h"
29 
30 #if !UCONFIG_NO_CONVERSION
31 
32 
33 #define DIGIT_0     0x0030
34 #define DIGIT_9     0x0039
35 #define LOWERCASE_A 0x0061
36 #define UPPERCASE_A 0x0041
37 #define LOWERCASE_Z 0x007A
38 #define UPPERCASE_Z 0x005A
39 
40 int
ufmt_digitvalue(UChar c)41 ufmt_digitvalue(UChar c)
42 {
43     if( ((c>=DIGIT_0)&&(c<=DIGIT_9)) ||
44         ((c>=LOWERCASE_A)&&(c<=LOWERCASE_Z)) ||
45         ((c>=UPPERCASE_A)&&(c<=UPPERCASE_Z))  )
46     {
47       return c - DIGIT_0 - (c >= 0x0041 ? (c >= 0x0061 ? 39 : 7) : 0);
48     }
49     else
50     {
51       return -1;
52     }
53 }
54 
55 UBool
ufmt_isdigit(UChar c,int32_t radix)56 ufmt_isdigit(UChar     c,
57              int32_t     radix)
58 {
59     int digitVal = ufmt_digitvalue(c);
60 
61     return (UBool)(digitVal < radix && digitVal >= 0);
62 }
63 
64 #define TO_UC_DIGIT(a) a <= 9 ? (DIGIT_0 + a) : (0x0037 + a)
65 #define TO_LC_DIGIT(a) a <= 9 ? (DIGIT_0 + a) : (0x0057 + a)
66 
67 void
ufmt_64tou(UChar * buffer,int32_t * len,uint64_t value,uint8_t radix,UBool uselower,int32_t minDigits)68 ufmt_64tou(UChar     *buffer,
69           int32_t   *len,
70           uint64_t  value,
71           uint8_t  radix,
72           UBool     uselower,
73           int32_t   minDigits)
74 {
75     int32_t  length = 0;
76     uint32_t digit;
77     UChar    *left, *right, temp;
78 
79     do {
80         digit = (uint32_t)(value % radix);
81         value = value / radix;
82         buffer[length++] = (UChar)(uselower ? TO_LC_DIGIT(digit)
83             : TO_UC_DIGIT(digit));
84     } while(value);
85 
86     /* pad with zeroes to make it minDigits long */
87     if(minDigits != -1 && length < minDigits) {
88         while(length < minDigits && length < *len)
89             buffer[length++] = DIGIT_0;  /*zero padding */
90     }
91 
92     /* reverse the buffer */
93     left     = buffer;
94     right = buffer + length;
95     while(left < --right) {
96         temp     = *left;
97         *left++     = *right;
98         *right     = temp;
99     }
100 
101     *len = length;
102 }
103 
104 void
ufmt_ptou(UChar * buffer,int32_t * len,void * value,UBool uselower)105 ufmt_ptou(UChar    *buffer,
106           int32_t   *len,
107           void      *value,
108           UBool     uselower)
109 {
110     int32_t i;
111     int32_t length = 0;
112     uint8_t *ptrIdx = (uint8_t *)&value;
113 
114 #if U_IS_BIG_ENDIAN
115     for (i = 0; i < (int32_t)sizeof(void *); i++)
116 #else
117     for (i = (int32_t)sizeof(void *)-1; i >= 0 ; i--)
118 #endif
119     {
120         uint8_t byteVal = ptrIdx[i];
121         uint16_t firstNibble = (uint16_t)(byteVal>>4);
122         uint16_t secondNibble = (uint16_t)(byteVal&0xF);
123         if (uselower) {
124             buffer[length++]=TO_LC_DIGIT(firstNibble);
125             buffer[length++]=TO_LC_DIGIT(secondNibble);
126         }
127         else {
128             buffer[length++]=TO_UC_DIGIT(firstNibble);
129             buffer[length++]=TO_UC_DIGIT(secondNibble);
130         }
131     }
132 
133     *len = length;
134 }
135 
136 int64_t
ufmt_uto64(const UChar * buffer,int32_t * len,int8_t radix)137 ufmt_uto64(const UChar     *buffer,
138           int32_t     *len,
139           int8_t     radix)
140 {
141     const UChar     *limit;
142     int32_t         count;
143     uint64_t        result;
144 
145 
146     /* intialize parameters */
147     limit     = buffer + *len;
148     count     = 0;
149     result    = 0;
150 
151     /* iterate through buffer */
152     while(ufmt_isdigit(*buffer, radix) && buffer < limit) {
153 
154         /* read the next digit */
155         result *= radix;
156         result += ufmt_digitvalue(*buffer++);
157 
158         /* increment our count */
159         ++count;
160     }
161 
162     *len = count;
163     return static_cast<int64_t>(result);
164 }
165 
166 #define NIBBLE_PER_BYTE 2
167 void *
ufmt_utop(const UChar * buffer,int32_t * len)168 ufmt_utop(const UChar     *buffer,
169           int32_t     *len)
170 {
171     int32_t count, resultIdx, incVal, offset;
172     /* This union allows the pointer to be written as an array. */
173     union {
174         void *ptr;
175         uint8_t bytes[sizeof(void*)];
176     } result;
177 
178     /* intialize variables */
179     count      = 0;
180     offset     = 0;
181     result.ptr = NULL;
182 
183     /* Skip the leading zeros */
184     while(buffer[count] == DIGIT_0 || u_isspace(buffer[count])) {
185         count++;
186         offset++;
187     }
188 
189     /* iterate through buffer, stop when you hit the end */
190     while(count < *len && ufmt_isdigit(buffer[count], 16)) {
191         /* increment the count consumed */
192         ++count;
193     }
194 
195     /* detect overflow */
196     if (count - offset > (int32_t)(sizeof(void*)*NIBBLE_PER_BYTE)) {
197         offset = count - (int32_t)(sizeof(void*)*NIBBLE_PER_BYTE);
198     }
199 
200     /* Initialize the direction of the input */
201 #if U_IS_BIG_ENDIAN
202     incVal = -1;
203     resultIdx = (int32_t)(sizeof(void*) - 1);
204 #else
205     incVal = 1;
206     resultIdx = 0;
207 #endif
208     /* Write how much was consumed. */
209     *len = count;
210     while(--count >= offset) {
211         /* Get the first nibble of the byte */
212         uint8_t byte = (uint8_t)ufmt_digitvalue(buffer[count]);
213 
214         if (count > offset) {
215             /* Get the second nibble of the byte when available */
216             byte = (uint8_t)(byte + (ufmt_digitvalue(buffer[--count]) << 4));
217         }
218         /* Write the byte into the array */
219         result.bytes[resultIdx] = byte;
220         resultIdx += incVal;
221     }
222 
223     return result.ptr;
224 }
225 
226 UChar*
ufmt_defaultCPToUnicode(const char * s,int32_t sSize,UChar * target,int32_t tSize)227 ufmt_defaultCPToUnicode(const char *s, int32_t sSize,
228                         UChar *target, int32_t tSize)
229 {
230     UChar *alias;
231     UErrorCode status = U_ZERO_ERROR;
232     UConverter *defConverter = u_getDefaultConverter(&status);
233 
234     if(U_FAILURE(status) || defConverter == 0)
235         return 0;
236 
237     if(sSize <= 0) {
238         sSize = static_cast<int32_t>(uprv_strlen(s)) + 1;
239     }
240 
241     /* perform the conversion in one swoop */
242     if(target != 0) {
243 
244         alias = target;
245         ucnv_toUnicode(defConverter, &alias, alias + tSize, &s, s + sSize - 1,
246             NULL, TRUE, &status);
247 
248 
249         /* add the null terminator */
250         *alias = 0x0000;
251     }
252 
253     u_releaseDefaultConverter(defConverter);
254 
255     return target;
256 }
257 
258 
259 #endif
260