1/*
2*******************************************************************************
3* Copyright (C) 2013-2014, International Business Machines
4* Corporation and others.  All Rights Reserved.
5*******************************************************************************
6* collationsettings.cpp
7*
8* created on: 2013feb07
9* created by: Markus W. Scherer
10*/
11
12#include "unicode/utypes.h"
13
14#if !UCONFIG_NO_COLLATION
15
16#include "unicode/ucol.h"
17#include "cmemory.h"
18#include "collation.h"
19#include "collationsettings.h"
20#include "sharedobject.h"
21#include "uassert.h"
22#include "umutex.h"
23
24U_NAMESPACE_BEGIN
25
26CollationSettings::CollationSettings(const CollationSettings &other)
27        : SharedObject(other),
28          options(other.options), variableTop(other.variableTop),
29          reorderTable(NULL),
30          reorderCodes(NULL), reorderCodesLength(0), reorderCodesCapacity(0),
31          fastLatinOptions(other.fastLatinOptions) {
32    int32_t length = other.reorderCodesLength;
33    if(length == 0) {
34        U_ASSERT(other.reorderTable == NULL);
35    } else {
36        U_ASSERT(other.reorderTable != NULL);
37        if(other.reorderCodesCapacity == 0) {
38            aliasReordering(other.reorderCodes, length, other.reorderTable);
39        } else {
40            setReordering(other.reorderCodes, length, other.reorderTable);
41        }
42    }
43    if(fastLatinOptions >= 0) {
44        uprv_memcpy(fastLatinPrimaries, other.fastLatinPrimaries, sizeof(fastLatinPrimaries));
45    }
46}
47
48CollationSettings::~CollationSettings() {
49    if(reorderCodesCapacity != 0) {
50        uprv_free(const_cast<int32_t *>(reorderCodes));
51    }
52}
53
54UBool
55CollationSettings::operator==(const CollationSettings &other) const {
56    if(options != other.options) { return FALSE; }
57    if((options & ALTERNATE_MASK) != 0 && variableTop != other.variableTop) { return FALSE; }
58    if(reorderCodesLength != other.reorderCodesLength) { return FALSE; }
59    for(int32_t i = 0; i < reorderCodesLength; ++i) {
60        if(reorderCodes[i] != other.reorderCodes[i]) { return FALSE; }
61    }
62    return TRUE;
63}
64
65int32_t
66CollationSettings::hashCode() const {
67    int32_t h = options << 8;
68    if((options & ALTERNATE_MASK) != 0) { h ^= variableTop; }
69    h ^= reorderCodesLength;
70    for(int32_t i = 0; i < reorderCodesLength; ++i) {
71        h ^= (reorderCodes[i] << i);
72    }
73    return h;
74}
75
76void
77CollationSettings::resetReordering() {
78    // When we turn off reordering, we want to set a NULL permutation
79    // rather than a no-op permutation.
80    // Keep the memory via reorderCodes and its capacity.
81    reorderTable = NULL;
82    reorderCodesLength = 0;
83}
84
85void
86CollationSettings::aliasReordering(const int32_t *codes, int32_t length, const uint8_t *table) {
87    if(length == 0) {
88        resetReordering();
89    } else {
90        // We need to release the memory before setting the alias pointer.
91        if(reorderCodesCapacity != 0) {
92            uprv_free(const_cast<int32_t *>(reorderCodes));
93            reorderCodesCapacity = 0;
94        }
95        reorderTable = table;
96        reorderCodes = codes;
97        reorderCodesLength = length;
98    }
99}
100
101UBool
102CollationSettings::setReordering(const int32_t *codes, int32_t length, const uint8_t table[256]) {
103    if(length == 0) {
104        resetReordering();
105    } else {
106        uint8_t *ownedTable;
107        int32_t *ownedCodes;
108        if(length <= reorderCodesCapacity) {
109            ownedTable = const_cast<uint8_t *>(reorderTable);
110            ownedCodes = const_cast<int32_t *>(reorderCodes);
111        } else {
112            // Allocate one memory block for the codes and the 16-aligned table.
113            int32_t capacity = (length + 3) & ~3;  // round up to a multiple of 4 ints
114            uint8_t *bytes = (uint8_t *)uprv_malloc(256 + capacity * 4);
115            if(bytes == NULL) { return FALSE; }
116            if(reorderCodesCapacity != 0) {
117                uprv_free(const_cast<int32_t *>(reorderCodes));
118            }
119            reorderTable = ownedTable = bytes + capacity * 4;
120            reorderCodes = ownedCodes = (int32_t *)bytes;
121            reorderCodesCapacity = capacity;
122        }
123        uprv_memcpy(ownedTable, table, 256);
124        uprv_memcpy(ownedCodes, codes, length * 4);
125        reorderCodesLength = length;
126    }
127    return TRUE;
128}
129
130void
131CollationSettings::setStrength(int32_t value, int32_t defaultOptions, UErrorCode &errorCode) {
132    if(U_FAILURE(errorCode)) { return; }
133    int32_t noStrength = options & ~STRENGTH_MASK;
134    switch(value) {
135    case UCOL_PRIMARY:
136    case UCOL_SECONDARY:
137    case UCOL_TERTIARY:
138    case UCOL_QUATERNARY:
139    case UCOL_IDENTICAL:
140        options = noStrength | (value << STRENGTH_SHIFT);
141        break;
142    case UCOL_DEFAULT:
143        options = noStrength | (defaultOptions & STRENGTH_MASK);
144        break;
145    default:
146        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
147        break;
148    }
149}
150
151void
152CollationSettings::setFlag(int32_t bit, UColAttributeValue value,
153                           int32_t defaultOptions, UErrorCode &errorCode) {
154    if(U_FAILURE(errorCode)) { return; }
155    switch(value) {
156    case UCOL_ON:
157        options |= bit;
158        break;
159    case UCOL_OFF:
160        options &= ~bit;
161        break;
162    case UCOL_DEFAULT:
163        options = (options & ~bit) | (defaultOptions & bit);
164        break;
165    default:
166        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
167        break;
168    }
169}
170
171void
172CollationSettings::setCaseFirst(UColAttributeValue value,
173                                int32_t defaultOptions, UErrorCode &errorCode) {
174    if(U_FAILURE(errorCode)) { return; }
175    int32_t noCaseFirst = options & ~CASE_FIRST_AND_UPPER_MASK;
176    switch(value) {
177    case UCOL_OFF:
178        options = noCaseFirst;
179        break;
180    case UCOL_LOWER_FIRST:
181        options = noCaseFirst | CASE_FIRST;
182        break;
183    case UCOL_UPPER_FIRST:
184        options = noCaseFirst | CASE_FIRST_AND_UPPER_MASK;
185        break;
186    case UCOL_DEFAULT:
187        options = noCaseFirst | (defaultOptions & CASE_FIRST_AND_UPPER_MASK);
188        break;
189    default:
190        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
191        break;
192    }
193}
194
195void
196CollationSettings::setAlternateHandling(UColAttributeValue value,
197                                        int32_t defaultOptions, UErrorCode &errorCode) {
198    if(U_FAILURE(errorCode)) { return; }
199    int32_t noAlternate = options & ~ALTERNATE_MASK;
200    switch(value) {
201    case UCOL_NON_IGNORABLE:
202        options = noAlternate;
203        break;
204    case UCOL_SHIFTED:
205        options = noAlternate | SHIFTED;
206        break;
207    case UCOL_DEFAULT:
208        options = noAlternate | (defaultOptions & ALTERNATE_MASK);
209        break;
210    default:
211        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
212        break;
213    }
214}
215
216void
217CollationSettings::setMaxVariable(int32_t value, int32_t defaultOptions, UErrorCode &errorCode) {
218    if(U_FAILURE(errorCode)) { return; }
219    int32_t noMax = options & ~MAX_VARIABLE_MASK;
220    switch(value) {
221    case MAX_VAR_SPACE:
222    case MAX_VAR_PUNCT:
223    case MAX_VAR_SYMBOL:
224    case MAX_VAR_CURRENCY:
225        options = noMax | (value << MAX_VARIABLE_SHIFT);
226        break;
227    case UCOL_DEFAULT:
228        options = noMax | (defaultOptions & MAX_VARIABLE_MASK);
229        break;
230    default:
231        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
232        break;
233    }
234}
235
236U_NAMESPACE_END
237
238#endif  // !UCONFIG_NO_COLLATION
239