• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 #include <stdlib.h>
9 #include "unicode/ucal.h"
10 #include "unicode/ures.h"
11 #include "unicode/ustring.h"
12 #include "cmemory.h"
13 #include "cstring.h"
14 #include "erarules.h"
15 #include "gregoimp.h"
16 #include "uassert.h"
17 
18 U_NAMESPACE_BEGIN
19 
20 static const int32_t MAX_ENCODED_START_YEAR = 32767;
21 static const int32_t MIN_ENCODED_START_YEAR = -32768;
22 static const int32_t MIN_ENCODED_START = -2147483391;   // encodeDate(MIN_ENCODED_START_YEAR, 1, 1, ...);
23 
24 static const int32_t YEAR_MASK = 0xFFFF0000;
25 static const int32_t MONTH_MASK = 0x0000FF00;
26 static const int32_t DAY_MASK = 0x000000FF;
27 
28 static const int32_t MAX_INT32 = 0x7FFFFFFF;
29 static const int32_t MIN_INT32 = 0xFFFFFFFF;
30 
31 static const UChar VAL_FALSE[] = {0x66, 0x61, 0x6c, 0x73, 0x65};    // "false"
32 static const UChar VAL_FALSE_LEN = 5;
33 
isSet(int startDate)34 static UBool isSet(int startDate) {
35     return startDate != 0;
36 }
37 
isValidRuleStartDate(int32_t year,int32_t month,int32_t day)38 static UBool isValidRuleStartDate(int32_t year, int32_t month, int32_t day) {
39     return year >= MIN_ENCODED_START_YEAR && year <= MAX_ENCODED_START_YEAR
40             && month >= 1 && month <= 12 && day >=1 && day <= 31;
41 }
42 
43 /**
44  * Encode year/month/date to a single integer.
45  * year is high 16 bits (-32768 to 32767), month is
46  * next 8 bits and day of month is last 8 bits.
47  *
48  * @param year  year
49  * @param month month (1-base)
50  * @param day   day of month
51  * @return  an encoded date.
52  */
encodeDate(int32_t year,int32_t month,int32_t day)53 static int32_t encodeDate(int32_t year, int32_t month, int32_t day) {
54     return year << 16 | month << 8 | day;
55 }
56 
decodeDate(int32_t encodedDate,int32_t (& fields)[3])57 static void decodeDate(int32_t encodedDate, int32_t (&fields)[3]) {
58     if (encodedDate == MIN_ENCODED_START) {
59         fields[0] = MIN_INT32;
60         fields[1] = 1;
61         fields[2] = 1;
62     } else {
63         fields[0] = (encodedDate & YEAR_MASK) >> 16;
64         fields[1] = (encodedDate & MONTH_MASK) >> 8;
65         fields[2] = encodedDate & DAY_MASK;
66     }
67 }
68 
69 /**
70  * Compare an encoded date with another date specified by year/month/day.
71  * @param encoded   An encoded date
72  * @param year      Year of another date
73  * @param month     Month of another date
74  * @param day       Day of another date
75  * @return -1 when encoded date is earlier, 0 when two dates are same,
76  *          and 1 when encoded date is later.
77  */
compareEncodedDateWithYMD(int encoded,int year,int month,int day)78 static int32_t compareEncodedDateWithYMD(int encoded, int year, int month, int day) {
79     if (year < MIN_ENCODED_START_YEAR) {
80         if (encoded == MIN_ENCODED_START) {
81             if (year > MIN_INT32 || month > 1 || day > 1) {
82                 return -1;
83             }
84             return 0;
85         } else {
86             return 1;
87         }
88     } else if (year > MAX_ENCODED_START_YEAR) {
89         return -1;
90     } else {
91         int tmp = encodeDate(year, month, day);
92         if (encoded < tmp) {
93             return -1;
94         } else if (encoded == tmp) {
95             return 0;
96         } else {
97             return 1;
98         }
99     }
100 }
101 
EraRules(LocalMemory<int32_t> & eraStartDates,int32_t numEras)102 EraRules::EraRules(LocalMemory<int32_t>& eraStartDates, int32_t numEras)
103     : numEras(numEras) {
104     startDates.moveFrom(eraStartDates);
105     initCurrentEra();
106 }
107 
~EraRules()108 EraRules::~EraRules() {
109 }
110 
createInstance(const char * calType,UBool includeTentativeEra,UErrorCode & status)111 EraRules* EraRules::createInstance(const char *calType, UBool includeTentativeEra, UErrorCode& status) {
112     if(U_FAILURE(status)) {
113         return nullptr;
114     }
115     LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "supplementalData", &status));
116     ures_getByKey(rb.getAlias(), "calendarData", rb.getAlias(), &status);
117     ures_getByKey(rb.getAlias(), calType, rb.getAlias(), &status);
118     ures_getByKey(rb.getAlias(), "eras", rb.getAlias(), &status);
119 
120     if (U_FAILURE(status)) {
121         return nullptr;
122     }
123 
124     int32_t numEras = ures_getSize(rb.getAlias());
125     int32_t firstTentativeIdx = MAX_INT32;
126 
127     LocalMemory<int32_t> startDates(static_cast<int32_t *>(uprv_malloc(numEras * sizeof(int32_t))));
128     if (startDates.isNull()) {
129         status = U_MEMORY_ALLOCATION_ERROR;
130         return nullptr;
131     }
132     uprv_memset(startDates.getAlias(), 0 , numEras * sizeof(int32_t));
133 
134     while (ures_hasNext(rb.getAlias())) {
135         LocalUResourceBundlePointer eraRuleRes(ures_getNextResource(rb.getAlias(), nullptr, &status));
136         if (U_FAILURE(status)) {
137             return nullptr;
138         }
139         const char *eraIdxStr = ures_getKey(eraRuleRes.getAlias());
140         char *endp;
141         int32_t eraIdx = (int32_t)strtol(eraIdxStr, &endp, 10);
142         if ((size_t)(endp - eraIdxStr) != uprv_strlen(eraIdxStr)) {
143             status = U_INVALID_FORMAT_ERROR;
144             return nullptr;
145         }
146         if (eraIdx < 0 || eraIdx >= numEras) {
147             status = U_INVALID_FORMAT_ERROR;
148             return nullptr;
149         }
150         if (isSet(startDates[eraIdx])) {
151             // start date of the index was already set
152             status = U_INVALID_FORMAT_ERROR;
153             return nullptr;
154         }
155 
156         UBool hasName = TRUE;
157         UBool hasEnd = TRUE;
158         int32_t len;
159         while (ures_hasNext(eraRuleRes.getAlias())) {
160             LocalUResourceBundlePointer res(ures_getNextResource(eraRuleRes.getAlias(), nullptr, &status));
161             if (U_FAILURE(status)) {
162                 return nullptr;
163             }
164             const char *key = ures_getKey(res.getAlias());
165             if (uprv_strcmp(key, "start") == 0) {
166                 const int32_t *fields = ures_getIntVector(res.getAlias(), &len, &status);
167                 if (U_FAILURE(status)) {
168                     return nullptr;
169                 }
170                 if (len != 3 || !isValidRuleStartDate(fields[0], fields[1], fields[2])) {
171                     status = U_INVALID_FORMAT_ERROR;
172                     return nullptr;
173                 }
174                 startDates[eraIdx] = encodeDate(fields[0], fields[1], fields[2]);
175             } else if (uprv_strcmp(key, "named") == 0) {
176                 const UChar *val = ures_getString(res.getAlias(), &len, &status);
177                 if (u_strncmp(val, VAL_FALSE, VAL_FALSE_LEN) == 0) {
178                     hasName = FALSE;
179                 }
180             } else if (uprv_strcmp(key, "end") == 0) {
181                 hasEnd = TRUE;
182             }
183         }
184 
185         if (isSet(startDates[eraIdx])) {
186             if (hasEnd) {
187                 // This implementation assumes either start or end is available, not both.
188                 // For now, just ignore the end rule.
189             }
190         } else {
191             if (hasEnd) {
192                 if (eraIdx != 0) {
193                     // This implementation does not support end only rule for eras other than
194                     // the first one.
195                     status = U_INVALID_FORMAT_ERROR;
196                     return nullptr;
197                 }
198                 U_ASSERT(eraIdx == 0);
199                 startDates[eraIdx] = MIN_ENCODED_START;
200             } else {
201                 status = U_INVALID_FORMAT_ERROR;
202                 return nullptr;
203             }
204         }
205 
206         if (hasName) {
207             if (eraIdx >= firstTentativeIdx) {
208                 status = U_INVALID_FORMAT_ERROR;
209                 return nullptr;
210             }
211         } else {
212             if (eraIdx < firstTentativeIdx) {
213                 firstTentativeIdx = eraIdx;
214             }
215         }
216     }
217 
218     EraRules *result;
219     if (firstTentativeIdx < MAX_INT32 && !includeTentativeEra) {
220         result = new EraRules(startDates, firstTentativeIdx);
221     } else {
222         result = new EraRules(startDates, numEras);
223     }
224 
225     if (result == nullptr) {
226         status = U_MEMORY_ALLOCATION_ERROR;
227     }
228     return result;
229 }
230 
getStartDate(int32_t eraIdx,int32_t (& fields)[3],UErrorCode & status) const231 void EraRules::getStartDate(int32_t eraIdx, int32_t (&fields)[3], UErrorCode& status) const {
232     if(U_FAILURE(status)) {
233         return;
234     }
235     if (eraIdx < 0 || eraIdx >= numEras) {
236         status = U_ILLEGAL_ARGUMENT_ERROR;
237         return;
238     }
239     decodeDate(startDates[eraIdx], fields);
240 }
241 
getStartYear(int32_t eraIdx,UErrorCode & status) const242 int32_t EraRules::getStartYear(int32_t eraIdx, UErrorCode& status) const {
243     int year = MAX_INT32;   // bogus value
244     if(U_FAILURE(status)) {
245         return year;
246     }
247     if (eraIdx < 0 || eraIdx >= numEras) {
248         status = U_ILLEGAL_ARGUMENT_ERROR;
249         return year;
250     }
251     int fields[3];
252     decodeDate(startDates[eraIdx], fields);
253     year = fields[0];
254 
255     return year;
256 }
257 
getEraIndex(int32_t year,int32_t month,int32_t day,UErrorCode & status) const258 int32_t EraRules::getEraIndex(int32_t year, int32_t month, int32_t day, UErrorCode& status) const {
259     if(U_FAILURE(status)) {
260         return -1;
261     }
262 
263     if (month < 1 || month > 12 || day < 1 || day > 31) {
264         status = U_ILLEGAL_ARGUMENT_ERROR;
265         return -1;
266     }
267     int32_t high = numEras; // last index + 1
268     int32_t low;
269 
270     // Short circuit for recent years.  Most modern computations will
271     // occur in the last few eras.
272     if (compareEncodedDateWithYMD(startDates[getCurrentEraIndex()], year, month, day) <= 0) {
273         low = getCurrentEraIndex();
274     } else {
275         low = 0;
276     }
277 
278     // Do binary search
279     while (low < high - 1) {
280         int i = (low + high) / 2;
281         if (compareEncodedDateWithYMD(startDates[i], year, month, day) <= 0) {
282             low = i;
283         } else {
284             high = i;
285         }
286     }
287     return low;
288 }
289 
initCurrentEra()290 void EraRules::initCurrentEra() {
291     UDate now = ucal_getNow();
292     int year, month0, dom, dow, doy, mid;
293     Grego::timeToFields(now, year, month0, dom, dow, doy, mid);
294     int currentEncodedDate = encodeDate(year, month0 + 1 /* changes to 1-base */, dom);
295     int eraIdx = numEras - 1;
296     while (eraIdx > 0) {
297         if (currentEncodedDate >= startDates[eraIdx]) {
298             break;
299         }
300         eraIdx--;
301     }
302     // Note: current era could be before the first era.
303     // In this case, this implementation returns the first era index (0).
304     currentEra = eraIdx;}
305 
306 U_NAMESPACE_END
307 #endif /* #if !UCONFIG_NO_FORMATTING */
308 
309 
310