Bug Summary

File:root/firefox-clang/intl/icu/source/common/cmemory.h
Warning:line 480, column 9
The first element of the 2nd argument is undefined
Note:line 480, column 9
Other elements might also be undefined

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name measunit_extra.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D U_I18N_IMPLEMENTATION -D _LIBCPP_DISABLE_DEPRECATION_WARNINGS -D U_USING_ICU_NAMESPACE=0 -D U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 -D U_HIDE_OBSOLETE_UTF_OLD_H=1 -D UCONFIG_NO_LEGACY_CONVERSION -D UCONFIG_NO_TRANSLITERATION -D UCONFIG_NO_REGULAR_EXPRESSIONS -D UCONFIG_NO_BREAK_ITERATION -D UCONFIG_NO_IDNA -D UCONFIG_NO_MF2 -D UCONFIG_NO_NORMALIZATION -D UCONFIG_NO_COLLATION -D U_CHARSET_IS_UTF8 -D UNISTR_FROM_CHAR_EXPLICIT=explicit -D UNISTR_FROM_STRING_EXPLICIT=explicit -D U_ENABLE_DYLOAD=0 -D U_DEBUG=1 -I /root/firefox-clang/config/external/icu/i18n -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -I /root/firefox-clang/intl/icu/source/common -I /root/firefox-clang/mfbt/double-conversion -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-comma -Wno-implicit-const-int-float-conversion -Wno-macro-redefined -Wno-microsoft-include -Wno-tautological-unsigned-enum-zero-compare -Wno-unreachable-code-loop-increment -Wno-unreachable-code-return -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/intl/icu/source/i18n/measunit_extra.cpp

/root/firefox-clang/intl/icu/source/common/cmemory.h

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) 1997-2016, International Business Machines
7* Corporation and others. All Rights Reserved.
8*
9******************************************************************************
10*
11* File CMEMORY.H
12*
13* Contains stdlib.h/string.h memory functions
14*
15* @author Bertrand A. Damiba
16*
17* Modification History:
18*
19* Date Name Description
20* 6/20/98 Bertrand Created.
21* 05/03/99 stephen Changed from functions to macros.
22*
23******************************************************************************
24*/
25
26#ifndef CMEMORY_H
27#define CMEMORY_H
28
29#include "unicode/utypes.h"
30
31#include <stddef.h>
32#include <string.h>
33#include "unicode/localpointer.h"
34#include "uassert.h"
35
36#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
37#include <stdio.h>
38#endif
39
40// uprv_memcpy and uprv_memmove
41#if defined(__clang__1)
42#define uprv_memcpy(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
43 /* Suppress warnings about addresses that will never be NULL */ \
44 _Pragma("clang diagnostic push")clang diagnostic push \
45 _Pragma("clang diagnostic ignored \"-Waddress\"")clang diagnostic ignored "-Waddress" \
46 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
47 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
48 _Pragma("clang diagnostic pop")clang diagnostic pop \
49 U_STANDARD_CPP_NAMESPACE:: memcpy(dst, src, size); \
50} UPRV_BLOCK_MACRO_ENDwhile (false)
51#define uprv_memmove(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memmove(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
52 /* Suppress warnings about addresses that will never be NULL */ \
53 _Pragma("clang diagnostic push")clang diagnostic push \
54 _Pragma("clang diagnostic ignored \"-Waddress\"")clang diagnostic ignored "-Waddress" \
55 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
56 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
57 _Pragma("clang diagnostic pop")clang diagnostic pop \
58 U_STANDARD_CPP_NAMESPACE:: memmove(dst, src, size); \
59} UPRV_BLOCK_MACRO_ENDwhile (false)
60#elif defined(__GNUC__4)
61#define uprv_memcpy(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
62 /* Suppress warnings about addresses that will never be NULL */ \
63 _Pragma("GCC diagnostic push")GCC diagnostic push \
64 _Pragma("GCC diagnostic ignored \"-Waddress\"")GCC diagnostic ignored "-Waddress" \
65 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
66 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
67 _Pragma("GCC diagnostic pop")GCC diagnostic pop \
68 U_STANDARD_CPP_NAMESPACE:: memcpy(dst, src, size); \
69} UPRV_BLOCK_MACRO_ENDwhile (false)
70#define uprv_memmove(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memmove(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
71 /* Suppress warnings about addresses that will never be NULL */ \
72 _Pragma("GCC diagnostic push")GCC diagnostic push \
73 _Pragma("GCC diagnostic ignored \"-Waddress\"")GCC diagnostic ignored "-Waddress" \
74 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
75 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
76 _Pragma("GCC diagnostic pop")GCC diagnostic pop \
77 U_STANDARD_CPP_NAMESPACE:: memmove(dst, src, size); \
78} UPRV_BLOCK_MACRO_ENDwhile (false)
79#else
80#define uprv_memcpy(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
81 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
82 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
83 U_STANDARD_CPP_NAMESPACE:: memcpy(dst, src, size); \
84} UPRV_BLOCK_MACRO_ENDwhile (false)
85#define uprv_memmove(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memmove(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
86 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
87 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
88 U_STANDARD_CPP_NAMESPACE:: memmove(dst, src, size); \
89} UPRV_BLOCK_MACRO_ENDwhile (false)
90#endif
91
92/**
93 * \def UPRV_LENGTHOF
94 * Convenience macro to determine the length of a fixed array at compile-time.
95 * @param array A fixed length array
96 * @return The length of the array, in elements
97 * @internal
98 */
99#define UPRV_LENGTHOF(array)(int32_t)(sizeof(array)/sizeof((array)[0])) (int32_t)(sizeof(array)/sizeof((array)[0]))
100#define uprv_memset(buffer, mark, size):: memset(buffer, mark, size) U_STANDARD_CPP_NAMESPACE:: memset(buffer, mark, size)
101#define uprv_memcmp(buffer1, buffer2, size):: memcmp(buffer1, buffer2,size) U_STANDARD_CPP_NAMESPACE:: memcmp(buffer1, buffer2,size)
102#define uprv_memchr(ptr, value, num):: memchr(ptr, value, num) U_STANDARD_CPP_NAMESPACE:: memchr(ptr, value, num)
103
104U_CAPIextern "C" void * U_EXPORT2
105uprv_malloc(size_t s) U_MALLOC_ATTR__attribute__ ((__malloc__)) U_ALLOC_SIZE_ATTR(1)__attribute__ ((alloc_size(1)));
106
107U_CAPIextern "C" void * U_EXPORT2
108uprv_realloc(void *mem, size_t size) U_ALLOC_SIZE_ATTR(2)__attribute__ ((alloc_size(2)));
109
110U_CAPIextern "C" void U_EXPORT2
111uprv_free(void *mem);
112
113U_CAPIextern "C" void * U_EXPORT2
114uprv_calloc(size_t num, size_t size) U_MALLOC_ATTR__attribute__ ((__malloc__)) U_ALLOC_SIZE_ATTR2(1,2)__attribute__ ((alloc_size(1,2)));
115
116/**
117 * Get the least significant bits of a pointer (a memory address).
118 * For example, with a mask of 3, the macro gets the 2 least significant bits,
119 * which will be 0 if the pointer is 32-bit (4-byte) aligned.
120 *
121 * uintptr_t is the most appropriate integer type to cast to.
122 */
123#define U_POINTER_MASK_LSB(ptr, mask)((uintptr_t)(ptr) & (mask)) ((uintptr_t)(ptr) & (mask))
124
125/**
126 * Create & return an instance of "type" in statically allocated storage.
127 * e.g.
128 * static std::mutex *myMutex = STATIC_NEW(std::mutex);
129 * To destroy an object created in this way, invoke the destructor explicitly, e.g.
130 * myMutex->~mutex();
131 * DO NOT use delete.
132 * DO NOT use with class UMutex, which has specific support for static instances.
133 *
134 * STATIC_NEW is intended for use when
135 * - We want a static (or global) object.
136 * - We don't want it to ever be destructed, or to explicitly control destruction,
137 * to avoid use-after-destruction problems.
138 * - We want to avoid an ordinary heap allocated object,
139 * to avoid the possibility of memory allocation failures, and
140 * to avoid memory leak reports, from valgrind, for example.
141 * This is defined as a macro rather than a template function because each invocation
142 * must define distinct static storage for the object being returned.
143 */
144#define STATIC_NEW(type)[] () { alignas(type) static char storage[sizeof(type)]; return
new(storage) type();} ()
[] () { \
145 alignas(type) static char storage[sizeof(type)]; \
146 return new(storage) type();} ()
147
148/**
149 * Heap clean up function, called from u_cleanup()
150 * Clears any user heap functions from u_setMemoryFunctions()
151 * Does NOT deallocate any remaining allocated memory.
152 */
153U_CFUNCextern "C" UBool
154cmemory_cleanup(void);
155
156/**
157 * A function called by <TT>uhash_remove</TT>,
158 * <TT>uhash_close</TT>, or <TT>uhash_put</TT> to delete
159 * an existing key or value.
160 * @param obj A key or value stored in a hashtable
161 * @see uprv_deleteUObject
162 */
163typedef void U_CALLCONV UObjectDeleter(void* obj);
164
165/**
166 * Deleter for UObject instances.
167 * Works for all subclasses of UObject because it has a virtual destructor.
168 */
169U_CAPIextern "C" void U_EXPORT2
170uprv_deleteUObject(void *obj);
171
172#ifdef __cplusplus202002L
173
174#include <utility>
175#include "unicode/uobject.h"
176
177U_NAMESPACE_BEGINnamespace icu_78 {
178
179/**
180 * "Smart pointer" class, deletes memory via uprv_free().
181 * For most methods see the LocalPointerBase base class.
182 * Adds operator[] for array item access.
183 *
184 * @see LocalPointerBase
185 */
186template<typename T>
187class LocalMemory : public LocalPointerBase<T> {
188public:
189 using LocalPointerBase<T>::operator*;
190 using LocalPointerBase<T>::operator->;
191 /**
192 * Constructor takes ownership.
193 * @param p simple pointer to an array of T items that is adopted
194 */
195 explicit LocalMemory(T *p=nullptr) : LocalPointerBase<T>(p) {}
196 /**
197 * Move constructor, leaves src with isNull().
198 * @param src source smart pointer
199 */
200 LocalMemory(LocalMemory<T> &&src) noexcept : LocalPointerBase<T>(src.ptr) {
201 src.ptr=nullptr;
202 }
203 /**
204 * Destructor deletes the memory it owns.
205 */
206 ~LocalMemory() {
207 uprv_free(LocalPointerBase<T>::ptr);
208 }
209 /**
210 * Move assignment operator, leaves src with isNull().
211 * The behavior is undefined if *this and src are the same object.
212 * @param src source smart pointer
213 * @return *this
214 */
215 LocalMemory<T> &operator=(LocalMemory<T> &&src) noexcept {
216 uprv_free(LocalPointerBase<T>::ptr);
217 LocalPointerBase<T>::ptr=src.ptr;
218 src.ptr=nullptr;
219 return *this;
220 }
221 /**
222 * Swap pointers.
223 * @param other other smart pointer
224 */
225 void swap(LocalMemory<T> &other) noexcept {
226 T *temp=LocalPointerBase<T>::ptr;
227 LocalPointerBase<T>::ptr=other.ptr;
228 other.ptr=temp;
229 }
230 /**
231 * Non-member LocalMemory swap function.
232 * @param p1 will get p2's pointer
233 * @param p2 will get p1's pointer
234 */
235 friend inline void swap(LocalMemory<T> &p1, LocalMemory<T> &p2) noexcept {
236 p1.swap(p2);
237 }
238 /**
239 * Deletes the array it owns,
240 * and adopts (takes ownership of) the one passed in.
241 * @param p simple pointer to an array of T items that is adopted
242 */
243 void adoptInstead(T *p) {
244 uprv_free(LocalPointerBase<T>::ptr);
245 LocalPointerBase<T>::ptr=p;
246 }
247 /**
248 * Deletes the array it owns, allocates a new one and reset its bytes to 0.
249 * Returns the new array pointer.
250 * If the allocation fails, then the current array is unchanged and
251 * this method returns nullptr.
252 * @param newCapacity must be >0
253 * @return the allocated array pointer, or nullptr if the allocation failed
254 */
255 inline T *allocateInsteadAndReset(int32_t newCapacity=1);
256 /**
257 * Deletes the array it owns and allocates a new one, copying length T items.
258 * Returns the new array pointer.
259 * If the allocation fails, then the current array is unchanged and
260 * this method returns nullptr.
261 * @param newCapacity must be >0
262 * @param length number of T items to be copied from the old array to the new one;
263 * must be no more than the capacity of the old array,
264 * which the caller must track because the LocalMemory does not track it
265 * @return the allocated array pointer, or nullptr if the allocation failed
266 */
267 inline T *allocateInsteadAndCopy(int32_t newCapacity=1, int32_t length=0);
268 /**
269 * Array item access (writable).
270 * No index bounds check.
271 * @param i array index
272 * @return reference to the array item
273 */
274 T &operator[](ptrdiff_t i) const { return LocalPointerBase<T>::ptr[i]; }
275};
276
277template<typename T>
278inline T *LocalMemory<T>::allocateInsteadAndReset(int32_t newCapacity) {
279 if(newCapacity>0) {
280 T *p=(T *)uprv_malloc(newCapacity*sizeof(T));
281 if(p!=nullptr) {
282 uprv_memset(p, 0, newCapacity*sizeof(T)):: memset(p, 0, newCapacity*sizeof(T));
283 uprv_free(LocalPointerBase<T>::ptr);
284 LocalPointerBase<T>::ptr=p;
285 }
286 return p;
287 } else {
288 return nullptr;
289 }
290}
291
292
293template<typename T>
294inline T *LocalMemory<T>::allocateInsteadAndCopy(int32_t newCapacity, int32_t length) {
295 if(newCapacity>0) {
296 T *p=(T *)uprv_malloc(newCapacity*sizeof(T));
297 if(p!=nullptr) {
298 if(length>0) {
299 if(length>newCapacity) {
300 length=newCapacity;
301 }
302 uprv_memcpy(p, LocalPointerBase<T>::ptr, (size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (LocalPointerBase
<T>::ptr != __null) ? void (0) : __assert_fail ("LocalPointerBase<T>::ptr != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(p, LocalPointerBase<T>
::ptr, (size_t)length*sizeof(T)); } while (false)
;
303 }
304 uprv_free(LocalPointerBase<T>::ptr);
305 LocalPointerBase<T>::ptr=p;
306 }
307 return p;
308 } else {
309 return nullptr;
310 }
311}
312
313/**
314 * Simple array/buffer management class using uprv_malloc() and uprv_free().
315 * Provides an internal array with fixed capacity. Can alias another array
316 * or allocate one.
317 *
318 * The array address is properly aligned for type T. It might not be properly
319 * aligned for types larger than T (or larger than the largest subtype of T).
320 *
321 * Unlike LocalMemory and LocalArray, this class never adopts
322 * (takes ownership of) another array.
323 *
324 * WARNING: MaybeStackArray only works with primitive (plain-old data) types.
325 * It does NOT know how to call a destructor! If you work with classes with
326 * destructors, consider:
327 *
328 * - LocalArray in localpointer.h if you know the length ahead of time
329 * - MaybeStackVector if you know the length at runtime
330 */
331template<typename T, int32_t stackCapacity>
332class MaybeStackArray {
333public:
334 // No heap allocation. Use only on the stack.
335 static void* U_EXPORT2 operator new(size_t) noexcept = delete;
336 static void* U_EXPORT2 operator new[](size_t) noexcept = delete;
337 static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete;
338
339 /**
340 * Default constructor initializes with internal T[stackCapacity] buffer.
341 */
342 MaybeStackArray() : ptr(stackArray), capacity(stackCapacity), needToRelease(false) {}
343 /**
344 * Automatically allocates the heap array if the argument is larger than the stack capacity.
345 * Intended for use when an approximate capacity is known at compile time but the true
346 * capacity is not known until runtime.
347 */
348 MaybeStackArray(int32_t newCapacity, UErrorCode status) : MaybeStackArray() {
349 if (U_FAILURE(status)) {
350 return;
351 }
352 if (capacity < newCapacity) {
353 if (resize(newCapacity) == nullptr) {
354 status = U_MEMORY_ALLOCATION_ERROR;
355 }
356 }
357 }
358 /**
359 * Destructor deletes the array (if owned).
360 */
361 ~MaybeStackArray() { releaseArray(); }
362 /**
363 * Move constructor: transfers ownership or copies the stack array.
364 */
365 MaybeStackArray(MaybeStackArray<T, stackCapacity> &&src) noexcept;
366 /**
367 * Move assignment: transfers ownership or copies the stack array.
368 */
369 MaybeStackArray<T, stackCapacity> &operator=(MaybeStackArray<T, stackCapacity> &&src) noexcept;
370 /**
371 * Returns the array capacity (number of T items).
372 * @return array capacity
373 */
374 int32_t getCapacity() const { return capacity; }
375 /**
376 * Access without ownership change.
377 * @return the array pointer
378 */
379 T *getAlias() const { return ptr; }
380 /**
381 * Returns the array limit. Simple convenience method.
382 * @return getAlias()+getCapacity()
383 */
384 T *getArrayLimit() const { return getAlias()+capacity; }
385 // No "operator T *() const" because that can make
386 // expressions like mbs[index] ambiguous for some compilers.
387 /**
388 * Array item access (const).
389 * No index bounds check.
390 * @param i array index
391 * @return reference to the array item
392 */
393 const T &operator[](ptrdiff_t i) const { return ptr[i]; }
394 /**
395 * Array item access (writable).
396 * No index bounds check.
397 * @param i array index
398 * @return reference to the array item
399 */
400 T &operator[](ptrdiff_t i) { return ptr[i]; }
401 /**
402 * Deletes the array (if owned) and aliases another one, no transfer of ownership.
403 * If the arguments are illegal, then the current array is unchanged.
404 * @param otherArray must not be nullptr
405 * @param otherCapacity must be >0
406 */
407 void aliasInstead(T *otherArray, int32_t otherCapacity) {
408 if(otherArray!=nullptr && otherCapacity>0) {
409 releaseArray();
410 ptr=otherArray;
411 capacity=otherCapacity;
412 needToRelease=false;
413 }
414 }
415 /**
416 * Deletes the array (if owned) and allocates a new one, copying length T items.
417 * Returns the new array pointer.
418 * If the allocation fails, then the current array is unchanged and
419 * this method returns nullptr.
420 * @param newCapacity can be less than or greater than the current capacity;
421 * must be >0
422 * @param length number of T items to be copied from the old array to the new one
423 * @return the allocated array pointer, or nullptr if the allocation failed
424 */
425 inline T *resize(int32_t newCapacity, int32_t length=0);
426 /**
427 * Gives up ownership of the array if owned, or else clones it,
428 * copying length T items; resets itself to the internal stack array.
429 * Returns nullptr if the allocation failed.
430 * @param length number of T items to copy when cloning,
431 * and capacity of the clone when cloning
432 * @param resultCapacity will be set to the returned array's capacity (output-only)
433 * @return the array pointer;
434 * caller becomes responsible for deleting the array
435 */
436 inline T *orphanOrClone(int32_t length, int32_t &resultCapacity);
437
438protected:
439 // Resizes the array to the size of src, then copies the contents of src.
440 void copyFrom(const MaybeStackArray &src, UErrorCode &status) {
441 if (U_FAILURE(status)) {
442 return;
443 }
444 if (this->resize(src.capacity, 0) == nullptr) {
445 status = U_MEMORY_ALLOCATION_ERROR;
446 return;
447 }
448 uprv_memcpy(this->ptr, src.ptr, (size_t)capacity * sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (this->ptr != __null) ? void (
0) : __assert_fail ("this->ptr != __null", __builtin_FILE (
), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); (static_cast
<bool> (src.ptr != __null) ? void (0) : __assert_fail (
"src.ptr != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); clang diagnostic pop :: memcpy(this->
ptr, src.ptr, (size_t)capacity * sizeof(T)); } while (false)
;
449 }
450
451private:
452 T *ptr;
453 int32_t capacity;
454 UBool needToRelease;
455 T stackArray[stackCapacity];
456 void releaseArray() {
457 if(needToRelease) {
458 uprv_free(ptr);
459 }
460 }
461 void resetToStackArray() {
462 ptr=stackArray;
463 capacity=stackCapacity;
464 needToRelease=false;
465 }
466 /* No comparison operators with other MaybeStackArray's. */
467 bool operator==(const MaybeStackArray & /*other*/) = delete;
468 bool operator!=(const MaybeStackArray & /*other*/) = delete;
469 /* No ownership transfer: No copy constructor, no assignment operator. */
470 MaybeStackArray(const MaybeStackArray & /*other*/) = delete;
471 void operator=(const MaybeStackArray & /*other*/) = delete;
472};
473
474template<typename T, int32_t stackCapacity>
475icu::MaybeStackArray<T, stackCapacity>::MaybeStackArray(
476 MaybeStackArray <T, stackCapacity>&& src) noexcept
477 : ptr(src.ptr), capacity(src.capacity), needToRelease(src.needToRelease) {
478 if (src.ptr
7.1
Field 'ptr' is equal to field 'stackArray'
7.1
Field 'ptr' is equal to field 'stackArray'
== src.stackArray) {
8
Taking true branch
479 ptr = stackArray;
480 uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (stackArray != __null) ? void (0)
: __assert_fail ("stackArray != __null", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__)); (static_cast <bool
> (src.stackArray != __null) ? void (0) : __assert_fail ("src.stackArray != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(stackArray, src.stackArray
, sizeof(T) * src.capacity); } while (false)
;
Other elements might also be undefined
9
'?' condition is true
10
'?' condition is true
11
The first element of the 2nd argument is undefined
481 } else {
482 src.resetToStackArray(); // take ownership away from src
483 }
484}
485
486template<typename T, int32_t stackCapacity>
487inline MaybeStackArray <T, stackCapacity>&
488MaybeStackArray<T, stackCapacity>::operator=(MaybeStackArray <T, stackCapacity>&& src) noexcept {
489 releaseArray(); // in case this instance had its own memory allocated
490 capacity = src.capacity;
491 needToRelease = src.needToRelease;
492 if (src.ptr == src.stackArray) {
493 ptr = stackArray;
494 uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (stackArray != __null) ? void (0)
: __assert_fail ("stackArray != __null", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__)); (static_cast <bool
> (src.stackArray != __null) ? void (0) : __assert_fail ("src.stackArray != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(stackArray, src.stackArray
, sizeof(T) * src.capacity); } while (false)
;
495 } else {
496 ptr = src.ptr;
497 src.resetToStackArray(); // take ownership away from src
498 }
499 return *this;
500}
501
502template<typename T, int32_t stackCapacity>
503inline T *MaybeStackArray<T, stackCapacity>::resize(int32_t newCapacity, int32_t length) {
504 if(newCapacity>0) {
505#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
506 ::fprintf(::stderrstderr, "MaybeStackArray (resize) alloc %d * %lu\n", newCapacity, sizeof(T));
507#endif
508 T *p=(T *)uprv_malloc(newCapacity*sizeof(T));
509 if(p!=nullptr) {
510 if(length>0) {
511 if(length>capacity) {
512 length=capacity;
513 }
514 if(length>newCapacity) {
515 length=newCapacity;
516 }
517 uprv_memcpy(p, ptr, (size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (ptr != __null
) ? void (0) : __assert_fail ("ptr != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(p, ptr, (size_t)length*sizeof(T));
} while (false)
;
518 }
519 releaseArray();
520 ptr=p;
521 capacity=newCapacity;
522 needToRelease=true;
523 }
524 return p;
525 } else {
526 return nullptr;
527 }
528}
529
530template<typename T, int32_t stackCapacity>
531inline T *MaybeStackArray<T, stackCapacity>::orphanOrClone(int32_t length, int32_t &resultCapacity) {
532 T *p;
533 if(needToRelease) {
534 p=ptr;
535 } else if(length<=0) {
536 return nullptr;
537 } else {
538 if(length>capacity) {
539 length=capacity;
540 }
541 p=(T *)uprv_malloc(length*sizeof(T));
542#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
543 ::fprintf(::stderrstderr,"MaybeStacArray (orphan) alloc %d * %lu\n", length,sizeof(T));
544#endif
545 if(p==nullptr) {
546 return nullptr;
547 }
548 uprv_memcpy(p, ptr, (size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (ptr != __null
) ? void (0) : __assert_fail ("ptr != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(p, ptr, (size_t)length*sizeof(T));
} while (false)
;
549 }
550 resultCapacity=length;
551 resetToStackArray();
552 return p;
553}
554
555/**
556 * Variant of MaybeStackArray that allocates a header struct and an array
557 * in one contiguous memory block, using uprv_malloc() and uprv_free().
558 * Provides internal memory with fixed array capacity. Can alias another memory
559 * block or allocate one.
560 * The stackCapacity is the number of T items in the internal memory,
561 * not counting the H header.
562 * Unlike LocalMemory and LocalArray, this class never adopts
563 * (takes ownership of) another memory block.
564 */
565template<typename H, typename T, int32_t stackCapacity>
566class MaybeStackHeaderAndArray {
567public:
568 // No heap allocation. Use only on the stack.
569 static void* U_EXPORT2 operator new(size_t) noexcept = delete;
570 static void* U_EXPORT2 operator new[](size_t) noexcept = delete;
571 static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete;
572
573 /**
574 * Default constructor initializes with internal H+T[stackCapacity] buffer.
575 */
576 MaybeStackHeaderAndArray() : ptr(&stackHeader), capacity(stackCapacity), needToRelease(false) {}
577 /**
578 * Destructor deletes the memory (if owned).
579 */
580 ~MaybeStackHeaderAndArray() { releaseMemory(); }
581 /**
582 * Returns the array capacity (number of T items).
583 * @return array capacity
584 */
585 int32_t getCapacity() const { return capacity; }
586 /**
587 * Access without ownership change.
588 * @return the header pointer
589 */
590 H *getAlias() const { return ptr; }
591 /**
592 * Returns the array start.
593 * @return array start, same address as getAlias()+1
594 */
595 T *getArrayStart() const { return reinterpret_cast<T *>(getAlias()+1); }
596 /**
597 * Returns the array limit.
598 * @return array limit
599 */
600 T *getArrayLimit() const { return getArrayStart()+capacity; }
601 /**
602 * Access without ownership change. Same as getAlias().
603 * A class instance can be used directly in expressions that take a T *.
604 * @return the header pointer
605 */
606 operator H *() const { return ptr; }
607 /**
608 * Array item access (writable).
609 * No index bounds check.
610 * @param i array index
611 * @return reference to the array item
612 */
613 T &operator[](ptrdiff_t i) { return getArrayStart()[i]; }
614 /**
615 * Deletes the memory block (if owned) and aliases another one, no transfer of ownership.
616 * If the arguments are illegal, then the current memory is unchanged.
617 * @param otherArray must not be nullptr
618 * @param otherCapacity must be >0
619 */
620 void aliasInstead(H *otherMemory, int32_t otherCapacity) {
621 if(otherMemory!=nullptr && otherCapacity>0) {
622 releaseMemory();
623 ptr=otherMemory;
624 capacity=otherCapacity;
625 needToRelease=false;
626 }
627 }
628 /**
629 * Deletes the memory block (if owned) and allocates a new one,
630 * copying the header and length T array items.
631 * Returns the new header pointer.
632 * If the allocation fails, then the current memory is unchanged and
633 * this method returns nullptr.
634 * @param newCapacity can be less than or greater than the current capacity;
635 * must be >0
636 * @param length number of T items to be copied from the old array to the new one
637 * @return the allocated pointer, or nullptr if the allocation failed
638 */
639 inline H *resize(int32_t newCapacity, int32_t length=0);
640 /**
641 * Gives up ownership of the memory if owned, or else clones it,
642 * copying the header and length T array items; resets itself to the internal memory.
643 * Returns nullptr if the allocation failed.
644 * @param length number of T items to copy when cloning,
645 * and array capacity of the clone when cloning
646 * @param resultCapacity will be set to the returned array's capacity (output-only)
647 * @return the header pointer;
648 * caller becomes responsible for deleting the array
649 */
650 inline H *orphanOrClone(int32_t length, int32_t &resultCapacity);
651private:
652 H *ptr;
653 int32_t capacity;
654 UBool needToRelease;
655 // stackHeader must precede stackArray immediately.
656 H stackHeader;
657 T stackArray[stackCapacity];
658 void releaseMemory() {
659 if(needToRelease) {
660 uprv_free(ptr);
661 }
662 }
663 /* No comparison operators with other MaybeStackHeaderAndArray's. */
664 bool operator==(const MaybeStackHeaderAndArray & /*other*/) {return false;}
665 bool operator!=(const MaybeStackHeaderAndArray & /*other*/) {return true;}
666 /* No ownership transfer: No copy constructor, no assignment operator. */
667 MaybeStackHeaderAndArray(const MaybeStackHeaderAndArray & /*other*/) {}
668 void operator=(const MaybeStackHeaderAndArray & /*other*/) {}
669};
670
671template<typename H, typename T, int32_t stackCapacity>
672inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::resize(int32_t newCapacity,
673 int32_t length) {
674 if(newCapacity>=0) {
675#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
676 ::fprintf(::stderrstderr,"MaybeStackHeaderAndArray alloc %d + %d * %ul\n", sizeof(H),newCapacity,sizeof(T));
677#endif
678 H *p=(H *)uprv_malloc(sizeof(H)+newCapacity*sizeof(T));
679 if(p!=nullptr) {
680 if(length<0) {
681 length=0;
682 } else if(length>0) {
683 if(length>capacity) {
684 length=capacity;
685 }
686 if(length>newCapacity) {
687 length=newCapacity;
688 }
689 }
690 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (ptr != __null
) ? void (0) : __assert_fail ("ptr != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof
(T)); } while (false)
;
691 releaseMemory();
692 ptr=p;
693 capacity=newCapacity;
694 needToRelease=true;
695 }
696 return p;
697 } else {
698 return nullptr;
699 }
700}
701
702template<typename H, typename T, int32_t stackCapacity>
703inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::orphanOrClone(int32_t length,
704 int32_t &resultCapacity) {
705 H *p;
706 if(needToRelease) {
707 p=ptr;
708 } else {
709 if(length<0) {
710 length=0;
711 } else if(length>capacity) {
712 length=capacity;
713 }
714#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
715 ::fprintf(::stderrstderr,"MaybeStackHeaderAndArray (orphan) alloc %ul + %d * %lu\n", sizeof(H),length,sizeof(T));
716#endif
717 p=(H *)uprv_malloc(sizeof(H)+length*sizeof(T));
718 if(p==nullptr) {
719 return nullptr;
720 }
721 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (ptr != __null
) ? void (0) : __assert_fail ("ptr != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof
(T)); } while (false)
;
722 }
723 resultCapacity=length;
724 ptr=&stackHeader;
725 capacity=stackCapacity;
726 needToRelease=false;
727 return p;
728}
729
730/**
731 * A simple memory management class that creates new heap allocated objects (of
732 * any class that has a public constructor), keeps track of them and eventually
733 * deletes them all in its own destructor.
734 *
735 * A typical use-case would be code like this:
736 *
737 * MemoryPool<MyType> pool;
738 *
739 * MyType* o1 = pool.create();
740 * if (o1 != nullptr) {
741 * foo(o1);
742 * }
743 *
744 * MyType* o2 = pool.create(1, 2, 3);
745 * if (o2 != nullptr) {
746 * bar(o2);
747 * }
748 *
749 * // MemoryPool will take care of deleting the MyType objects.
750 *
751 * It doesn't do anything more than that, and is intentionally kept minimalist.
752 */
753template<typename T, int32_t stackCapacity = 8>
754class MemoryPool : public UMemory {
755public:
756 MemoryPool() : fCount(0), fPool() {}
757
758 ~MemoryPool() {
759 for (int32_t i = 0; i < fCount; ++i) {
760 delete fPool[i];
761 }
762 }
763
764 MemoryPool(const MemoryPool&) = delete;
765 MemoryPool& operator=(const MemoryPool&) = delete;
766
767 MemoryPool(MemoryPool&& other) noexcept : fCount(other.fCount),
768 fPool(std::move(other.fPool)) {
7
Calling move constructor for 'MaybeStackArray<icu_78::MeasureUnitImplWithIndex *, 8>'
769 other.fCount = 0;
770 }
771
772 MemoryPool& operator=(MemoryPool&& other) noexcept {
773 // Since `this` may contain instances that need to be deleted, we can't
774 // just throw them away and replace them with `other`. The normal way of
775 // dealing with this in C++ is to swap `this` and `other`, rather than
776 // simply overwrite: the destruction of `other` can then take care of
777 // running MemoryPool::~MemoryPool() over the still-to-be-deallocated
778 // instances.
779 std::swap(fCount, other.fCount);
780 std::swap(fPool, other.fPool);
781 return *this;
782 }
783
784 /**
785 * Creates a new object of typename T, by forwarding any and all arguments
786 * to the typename T constructor.
787 *
788 * @param args Arguments to be forwarded to the typename T constructor.
789 * @return A pointer to the newly created object, or nullptr on error.
790 */
791 template<typename... Args>
792 T* create(Args&&... args) {
793 int32_t capacity = fPool.getCapacity();
794 if (fCount == capacity &&
795 fPool.resize(capacity == stackCapacity ? 4 * capacity : 2 * capacity,
796 capacity) == nullptr) {
797 return nullptr;
798 }
799 return fPool[fCount++] = new T(std::forward<Args>(args)...);
800 }
801
802 template <typename... Args>
803 T* createAndCheckErrorCode(UErrorCode &status, Args &&... args) {
804 if (U_FAILURE(status)) {
805 return nullptr;
806 }
807 T *pointer = this->create(args...);
808 if (U_SUCCESS(status) && pointer == nullptr) {
809 status = U_MEMORY_ALLOCATION_ERROR;
810 }
811 return pointer;
812 }
813
814 /**
815 * @return Number of elements that have been allocated.
816 */
817 int32_t count() const {
818 return fCount;
819 }
820
821protected:
822 int32_t fCount;
823 MaybeStackArray<T*, stackCapacity> fPool;
824};
825
826/**
827 * An internal Vector-like implementation based on MemoryPool.
828 *
829 * Heap-allocates each element and stores pointers.
830 *
831 * To append an item to the vector, use emplaceBack.
832 *
833 * MaybeStackVector<MyType> vector;
834 * MyType* element = vector.emplaceBack();
835 * if (!element) {
836 * status = U_MEMORY_ALLOCATION_ERROR;
837 * }
838 * // do stuff with element
839 *
840 * To loop over the vector, use a for loop with indices:
841 *
842 * for (int32_t i = 0; i < vector.length(); i++) {
843 * MyType* element = vector[i];
844 * }
845 */
846template<typename T, int32_t stackCapacity = 8>
847class MaybeStackVector : protected MemoryPool<T, stackCapacity> {
848public:
849 template<typename... Args>
850 T* emplaceBack(Args&&... args) {
851 return this->create(args...);
852 }
853
854 template <typename... Args>
855 T *emplaceBackAndCheckErrorCode(UErrorCode &status, Args &&... args) {
856 return this->createAndCheckErrorCode(status, args...);
857 }
858
859 int32_t length() const {
860 return this->fCount;
861 }
862
863 T** getAlias() {
864 return this->fPool.getAlias();
865 }
866
867 const T *const *getAlias() const {
868 return this->fPool.getAlias();
869 }
870
871 /**
872 * Array item access (read-only).
873 * No index bounds check.
874 * @param i array index
875 * @return reference to the array item
876 */
877 const T* operator[](ptrdiff_t i) const {
878 return this->fPool[i];
879 }
880
881 /**
882 * Array item access (writable).
883 * No index bounds check.
884 * @param i array index
885 * @return reference to the array item
886 */
887 T* operator[](ptrdiff_t i) {
888 return this->fPool[i];
889 }
890};
891
892
893U_NAMESPACE_END}
894
895#endif /* __cplusplus */
896#endif /* CMEMORY_H */

/root/firefox-clang/intl/icu/source/i18n/measunit_extra.cpp

1// © 2020 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3
4// Extra functions for MeasureUnit not needed for all clients.
5// Separate .o file so that it can be removed for modularity.
6
7#include "unicode/utypes.h"
8
9#if !UCONFIG_NO_FORMATTING0
10
11// Allow implicit conversion from char16_t* to UnicodeString for this file:
12// Helpful in toString methods and elsewhere.
13#define UNISTR_FROM_STRING_EXPLICIT
14
15#include "charstr.h"
16#include "cmemory.h"
17#include "cstring.h"
18#ifdef JS_HAS_INTL_API1
19#include "double-conversion/string-to-double.h"
20#else
21#include "double-conversion-string-to-double.h"
22#endif
23#include "measunit_impl.h"
24#include "resource.h"
25#include "uarrsort.h"
26#include "uassert.h"
27#include "ucln_in.h"
28#include "umutex.h"
29#include "unicode/bytestrie.h"
30#include "unicode/bytestriebuilder.h"
31#include "unicode/localpointer.h"
32#include "unicode/stringpiece.h"
33#include "unicode/stringtriebuilder.h"
34#include "unicode/ures.h"
35#include "unicode/ustringtrie.h"
36#include "uresimp.h"
37#include "util.h"
38#include <limits.h>
39#include <cstdlib>
40U_NAMESPACE_BEGINnamespace icu_78 {
41
42
43namespace {
44
45#ifdef JS_HAS_INTL_API1
46using double_conversion::StringToDoubleConverter;
47#else
48using icu::double_conversion::StringToDoubleConverter;
49#endif
50
51// TODO: Propose a new error code for this?
52constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
53
54// Trie value offset for SI or binary prefixes. This is big enough to ensure we only
55// insert positive integers into the trie.
56constexpr int32_t kPrefixOffset = 64;
57static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_BIN > 0,
58 "kPrefixOffset is too small for minimum UMeasurePrefix value");
59static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_SI > 0,
60 "kPrefixOffset is too small for minimum UMeasurePrefix value");
61
62// Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
63constexpr int32_t kCompoundPartOffset = 128;
64static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_BIN,
65 "Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens");
66static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_SI,
67 "Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens");
68
69enum CompoundPart {
70 // Represents "-per-"
71 COMPOUND_PART_PER = kCompoundPartOffset,
72 // Represents "-"
73 COMPOUND_PART_TIMES,
74 // Represents "-and-"
75 COMPOUND_PART_AND,
76};
77
78// Trie value offset for "per-".
79constexpr int32_t kInitialCompoundPartOffset = 192;
80
81enum InitialCompoundPart {
82 // Represents "per-", the only compound part that can appear at the start of
83 // an identifier.
84 INITIAL_COMPOUND_PART_PER = kInitialCompoundPartOffset,
85};
86
87// Trie value offset for powers like "square-", "cubic-", "pow2-" etc.
88constexpr int32_t kPowerPartOffset = 256;
89
90enum PowerPart {
91 POWER_PART_P2 = kPowerPartOffset + 2,
92 POWER_PART_P3,
93 POWER_PART_P4,
94 POWER_PART_P5,
95 POWER_PART_P6,
96 POWER_PART_P7,
97 POWER_PART_P8,
98 POWER_PART_P9,
99 POWER_PART_P10,
100 POWER_PART_P11,
101 POWER_PART_P12,
102 POWER_PART_P13,
103 POWER_PART_P14,
104 POWER_PART_P15,
105};
106
107// Trie value offset for simple units, e.g. "gram", "nautical-mile",
108// "fluid-ounce-imperial".
109constexpr int32_t kSimpleUnitOffset = 512;
110
111// Trie value offset for aliases, e.g. "portion" replaced by "part"
112constexpr int32_t kAliasOffset = 51200; // This will give a very big space for the units ids.
113
114const struct UnitPrefixStrings {
115 const char* const string;
116 UMeasurePrefix value;
117} gUnitPrefixStrings[] = {
118 // SI prefixes
119 { "quetta", UMEASURE_PREFIX_QUETTA },
120 { "ronna", UMEASURE_PREFIX_RONNA },
121 { "yotta", UMEASURE_PREFIX_YOTTA },
122 { "zetta", UMEASURE_PREFIX_ZETTA },
123 { "exa", UMEASURE_PREFIX_EXA },
124 { "peta", UMEASURE_PREFIX_PETA },
125 { "tera", UMEASURE_PREFIX_TERA },
126 { "giga", UMEASURE_PREFIX_GIGA },
127 { "mega", UMEASURE_PREFIX_MEGA },
128 { "kilo", UMEASURE_PREFIX_KILO },
129 { "hecto", UMEASURE_PREFIX_HECTO },
130 { "deka", UMEASURE_PREFIX_DEKA },
131 { "deci", UMEASURE_PREFIX_DECI },
132 { "centi", UMEASURE_PREFIX_CENTI },
133 { "milli", UMEASURE_PREFIX_MILLI },
134 { "micro", UMEASURE_PREFIX_MICRO },
135 { "nano", UMEASURE_PREFIX_NANO },
136 { "pico", UMEASURE_PREFIX_PICO },
137 { "femto", UMEASURE_PREFIX_FEMTO },
138 { "atto", UMEASURE_PREFIX_ATTO },
139 { "zepto", UMEASURE_PREFIX_ZEPTO },
140 { "yocto", UMEASURE_PREFIX_YOCTO },
141 { "ronto", UMEASURE_PREFIX_RONTO },
142 { "quecto", UMEASURE_PREFIX_QUECTO },
143 // Binary prefixes
144 { "yobi", UMEASURE_PREFIX_YOBI },
145 { "zebi", UMEASURE_PREFIX_ZEBI },
146 { "exbi", UMEASURE_PREFIX_EXBI },
147 { "pebi", UMEASURE_PREFIX_PEBI },
148 { "tebi", UMEASURE_PREFIX_TEBI },
149 { "gibi", UMEASURE_PREFIX_GIBI },
150 { "mebi", UMEASURE_PREFIX_MEBI },
151 { "kibi", UMEASURE_PREFIX_KIBI },
152};
153
154/**
155 * A ResourceSink that collects simple unit identifiers from the keys of the
156 * convertUnits table into an array, and adds these values to a TrieBuilder,
157 * with associated values being their index into this array plus a specified
158 * offset.
159 *
160 * Example code:
161 *
162 * UErrorCode status = U_ZERO_ERROR;
163 * BytesTrieBuilder b(status);
164 * int32_t ARR_SIZE = 200;
165 * const char *unitIdentifiers[ARR_SIZE];
166 * int32_t *unitCategories[ARR_SIZE];
167 * SimpleUnitIdentifiersSink identifierSink(gSerializedUnitCategoriesTrie, unitIdentifiers,
168 * unitCategories, ARR_SIZE, b, kTrieValueOffset);
169 * LocalUResourceBundlePointer unitsBundle(ures_openDirect(nullptr, "units", &status));
170 * ures_getAllItemsWithFallback(unitsBundle.getAlias(), "convertUnits", identifierSink, status);
171 */
172class SimpleUnitIdentifiersSink : public icu::ResourceSink {
173 public:
174 /**
175 * Constructor.
176 * @param quantitiesTrieData The data for constructing a quantitiesTrie,
177 * which maps from a simple unit identifier to an index into the
178 * gCategories array.
179 * @param out Array of char* to which pointers to the simple unit
180 * identifiers will be saved. (Does not take ownership.)
181 * @param outCategories Array of int32_t to which category indexes will be
182 * saved: this corresponds to simple unit IDs saved to `out`, mapping
183 * from the ID to the value produced by the quantitiesTrie (which is an
184 * index into the gCategories array).
185 * @param outSize The size of `out` and `outCategories`.
186 * @param trieBuilder The trie builder to which the simple unit identifier
187 * should be added. The trie builder must outlive this resource sink.
188 * @param trieValueOffset This is added to the index of the identifier in
189 * the `out` array, before adding to `trieBuilder` as the value
190 * associated with the identifier.
191 */
192 explicit SimpleUnitIdentifiersSink(StringPiece quantitiesTrieData, const char **out,
193 int32_t *outCategories, int32_t outSize,
194 BytesTrieBuilder &trieBuilder, int32_t trieValueOffset)
195 : outArray(out), outCategories(outCategories), outSize(outSize), trieBuilder(trieBuilder),
196 trieValueOffset(trieValueOffset), quantitiesTrieData(quantitiesTrieData), outIndex(0) {}
197
198 /**
199 * Adds the table keys found in value to the output vector.
200 * @param key The key of the resource passed to `value`: the second
201 * parameter of the ures_getAllItemsWithFallback() call.
202 * @param value Should be a ResourceTable value, if
203 * ures_getAllItemsWithFallback() was called correctly for this sink.
204 * @param noFallback Ignored.
205 * @param status The standard ICU error code output parameter.
206 */
207 void put(const char * /*key*/, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override {
208 ResourceTable table = value.getTable(status);
209 if (U_FAILURE(status)) return;
210
211 if (outIndex + table.getSize() > outSize) {
212 status = U_INDEX_OUTOFBOUNDS_ERROR;
213 return;
214 }
215
216 BytesTrie quantitiesTrie(quantitiesTrieData.data());
217
218 // Collect keys from the table resource.
219 const char *simpleUnitID;
220 for (int32_t i = 0; table.getKeyAndValue(i, simpleUnitID, value); ++i) {
221 U_ASSERT(i < table.getSize())(static_cast <bool> (i < table.getSize()) ? void (0)
: __assert_fail ("i < table.getSize()", __builtin_FILE ()
, __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
222 U_ASSERT(outIndex < outSize)(static_cast <bool> (outIndex < outSize) ? void (0) :
__assert_fail ("outIndex < outSize", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__))
;
223 if (uprv_strcmp(simpleUnitID, "kilogram"):: strcmp(simpleUnitID, "kilogram") == 0) {
224 // For parsing, we use "gram", the prefixless metric mass unit. We
225 // thus ignore the SI Base Unit of Mass: it exists due to being the
226 // mass conversion target unit, but not needed for MeasureUnit
227 // parsing.
228 continue;
229 }
230 outArray[outIndex] = simpleUnitID;
231 trieBuilder.add(simpleUnitID, trieValueOffset + outIndex, status);
232
233 // Find the base target unit for this simple unit
234 ResourceTable table = value.getTable(status);
235 if (U_FAILURE(status)) { return; }
236 if (!table.findValue("target", value)) {
237 status = U_INVALID_FORMAT_ERROR;
238 break;
239 }
240 int32_t len;
241 const char16_t* uTarget = value.getString(len, status);
242 CharString target;
243 target.appendInvariantChars(uTarget, len, status);
244 if (U_FAILURE(status)) { return; }
245 quantitiesTrie.reset();
246 UStringTrieResult result = quantitiesTrie.next(target.data(), target.length());
247 if (!USTRINGTRIE_HAS_VALUE(result)((result)>=USTRINGTRIE_FINAL_VALUE)) {
248 status = U_INVALID_FORMAT_ERROR;
249 break;
250 }
251 outCategories[outIndex] = quantitiesTrie.getValue();
252
253 outIndex++;
254 }
255 }
256
257 private:
258 const char **outArray;
259 int32_t *outCategories;
260 int32_t outSize;
261 BytesTrieBuilder &trieBuilder;
262 int32_t trieValueOffset;
263
264 StringPiece quantitiesTrieData;
265
266 int32_t outIndex;
267};
268
269class UnitAliasesSink : public icu::ResourceSink {
270 public:
271 /**
272 * Constructor.
273 * @param unitAliases The output vector of unit alias identifiers (CharString).
274 * @param unitReplacements The output vector of replacements for the unit aliases (CharString).
275 */
276 explicit UnitAliasesSink(MaybeStackVector<CharString> &unitAliases,
277 MaybeStackVector<CharString> &unitReplacements)
278 : unitAliases(unitAliases), unitReplacements(unitReplacements) {}
279
280 /**
281 * Adds the unit alias key and its replacement to the unitAliases and unitReplacements vectors.
282 * @param key The unit alias identifier (e.g., "meter-and-liter").
283 * @param value Should be a ResourceTable value containing the replacement,
284 * when ures_getAllChildrenWithFallback() is called correctly for this sink.
285 * @param noFallback Ignored.
286 * @param status The standard ICU error code output parameter.
287 */
288 void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
289 UErrorCode &status) override {
290 if (U_FAILURE(status)) return;
291
292 // Add the unit alias key to the unitAliases vector
293 int32_t keyLen = static_cast<int32_t>(uprv_strlen(key):: strlen(key));
294 unitAliases.emplaceBackAndCheckErrorCode(status)->append(key, keyLen, status);
295 if (U_FAILURE(status)) {
296 return;
297 }
298
299 // Find the replacement for this unit alias from the alias table resource.
300 ResourceTable aliasTable = value.getTable(status);
301 if (U_FAILURE(status)) {
302 return;
303 }
304
305 if (!aliasTable.findValue("replacement", value)) {
306 status = U_MISSING_RESOURCE_ERROR;
307 return;
308 }
309
310 int32_t len;
311 const char16_t *uReplacement = value.getString(len, status);
312 unitReplacements.emplaceBackAndCheckErrorCode(status)->appendInvariantChars(uReplacement,
313 len, status);
314 }
315
316 private:
317 MaybeStackVector<CharString> &unitAliases;
318 MaybeStackVector<CharString> &unitReplacements;
319};
320
321/**
322 * A ResourceSink that collects information from `unitQuantities` in the `units`
323 * resource to provide key->value lookups from base unit to category, as well as
324 * preserving ordering information for these categories. See `units.txt`.
325 *
326 * For example: "kilogram" -> "mass", "meter-per-second" -> "speed".
327 *
328 * In C++ unitQuantity values are collected in order into a char16_t* array, while
329 * unitQuantity keys are added added to a TrieBuilder, with associated values
330 * being the index into the aforementioned char16_t* array.
331 */
332class CategoriesSink : public icu::ResourceSink {
333 public:
334 /**
335 * Constructor.
336 * @param out Array of char16_t* to which unitQuantity values will be saved.
337 * The pointers returned not owned: they point directly at the resource
338 * strings in static memory.
339 * @param outSize The size of the `out` array.
340 * @param trieBuilder The trie builder to which the keys (base units) of
341 * each unitQuantity will be added, each with value being the offset
342 * into `out`.
343 */
344 explicit CategoriesSink(const char16_t **out, int32_t &outSize, BytesTrieBuilder &trieBuilder)
345 : outQuantitiesArray(out), outSize(outSize), trieBuilder(trieBuilder), outIndex(0) {}
346
347 void put(const char * /*key*/, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override {
348 ResourceArray array = value.getArray(status);
349 if (U_FAILURE(status)) {
350 return;
351 }
352
353 if (outIndex + array.getSize() > outSize) {
354 status = U_INDEX_OUTOFBOUNDS_ERROR;
355 return;
356 }
357
358 for (int32_t i = 0; array.getValue(i, value); ++i) {
359 U_ASSERT(outIndex < outSize)(static_cast <bool> (outIndex < outSize) ? void (0) :
__assert_fail ("outIndex < outSize", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__))
;
360 ResourceTable table = value.getTable(status);
361 if (U_FAILURE(status)) {
362 return;
363 }
364 if (table.getSize() != 1) {
365 status = U_INVALID_FORMAT_ERROR;
366 return;
367 }
368 const char *key;
369 table.getKeyAndValue(0, key, value);
370 int32_t uTmpLen;
371 outQuantitiesArray[outIndex] = value.getString(uTmpLen, status);
372 trieBuilder.add(key, outIndex, status);
373 outIndex++;
374 }
375 }
376
377 private:
378 const char16_t **outQuantitiesArray;
379 int32_t &outSize;
380 BytesTrieBuilder &trieBuilder;
381
382 int32_t outIndex;
383};
384
385icu::UInitOnce gUnitExtrasInitOnce {};
386
387// Array of unit aliases.
388const char** gUnitReplacements;
389const char* gUnitReplacementStrings;
390int32_t gNumUnitReplacements;
391
392// Array of simple unit IDs.
393//
394// The array memory itself is owned by this pointer, but the individual char* in
395// that array point at static memory. (Note that these char* are also returned
396// by SingleUnitImpl::getSimpleUnitID().)
397const char **gSimpleUnits = nullptr;
398
399// Maps from the value associated with each simple unit ID to an index into the
400// gCategories array.
401int32_t *gSimpleUnitCategories = nullptr;
402
403char *gSerializedUnitExtrasStemTrie = nullptr;
404
405// Array of char16_t* pointing at the unit categories (aka "quantities", aka
406// "types"), as found in the `unitQuantities` resource. The array memory itself
407// is owned by this pointer, but the individual char16_t* in that array point at
408// static memory.
409const char16_t **gCategories = nullptr;
410// Number of items in `gCategories`.
411int32_t gCategoriesCount = 0;
412// Serialized BytesTrie for mapping from base units to indices into gCategories.
413char *gSerializedUnitCategoriesTrie = nullptr;
414
415UBool U_CALLCONV cleanupUnitExtras() {
416 uprv_free(gSerializedUnitCategoriesTrie);
417 gSerializedUnitCategoriesTrie = nullptr;
418 uprv_free(gCategories);
419 gCategories = nullptr;
420 uprv_free(gSerializedUnitExtrasStemTrie);
421 gSerializedUnitExtrasStemTrie = nullptr;
422 uprv_free(gSimpleUnitCategories);
423 gSimpleUnitCategories = nullptr;
424 uprv_free(gSimpleUnits);
425 gSimpleUnits = nullptr;
426 uprv_free((void*)gUnitReplacementStrings);
427 gUnitReplacementStrings = nullptr;
428 uprv_free(gUnitReplacements);
429 gUnitReplacements = nullptr;
430 gNumUnitReplacements = 0;
431 gUnitExtrasInitOnce.reset();
432 return true;
433}
434
435void U_CALLCONV initUnitExtras(UErrorCode& status) {
436 ucln_i18n_registerCleanup(UCLN_I18N_UNIT_EXTRAS, cleanupUnitExtras);
437 LocalUResourceBundlePointer unitsBundle(ures_openDirect(nullptr, "units", &status));
438
439 // Collect unitQuantities information into gSerializedUnitCategoriesTrie and gCategories.
440 const char *CATEGORY_TABLE_NAME = "unitQuantities";
441 LocalUResourceBundlePointer unitQuantities(
442 ures_getByKey(unitsBundle.getAlias(), CATEGORY_TABLE_NAME, nullptr, &status));
443 if (U_FAILURE(status)) { return; }
444 gCategoriesCount = unitQuantities.getAlias()->fSize;
445 size_t quantitiesMallocSize = sizeof(char16_t *) * gCategoriesCount;
446 gCategories = static_cast<const char16_t **>(uprv_malloc(quantitiesMallocSize));
447 if (gCategories == nullptr) {
448 status = U_MEMORY_ALLOCATION_ERROR;
449 return;
450 }
451 uprv_memset(gCategories, 0, quantitiesMallocSize):: memset(gCategories, 0, quantitiesMallocSize);
452 BytesTrieBuilder quantitiesBuilder(status);
453 CategoriesSink categoriesSink(gCategories, gCategoriesCount, quantitiesBuilder);
454 ures_getAllItemsWithFallback(unitsBundle.getAlias(), CATEGORY_TABLE_NAME, categoriesSink, status);
455 StringPiece resultQuantities = quantitiesBuilder.buildStringPiece(USTRINGTRIE_BUILD_FAST, status);
456 if (U_FAILURE(status)) { return; }
457 // Copy the result into the global constant pointer
458 size_t numBytesQuantities = resultQuantities.length();
459 gSerializedUnitCategoriesTrie = static_cast<char *>(uprv_malloc(numBytesQuantities));
460 if (gSerializedUnitCategoriesTrie == nullptr) {
461 status = U_MEMORY_ALLOCATION_ERROR;
462 return;
463 }
464 uprv_memcpy(gSerializedUnitCategoriesTrie, resultQuantities.data(), numBytesQuantities)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (gSerializedUnitCategoriesTrie !=
__null) ? void (0) : __assert_fail ("gSerializedUnitCategoriesTrie != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); (static_cast <bool> (resultQuantities.data() != __null
) ? void (0) : __assert_fail ("resultQuantities.data() != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(gSerializedUnitCategoriesTrie
, resultQuantities.data(), numBytesQuantities); } while (false
)
;
465
466 // Build the BytesTrie that Parser needs for parsing unit identifiers.
467
468 BytesTrieBuilder b(status);
469 if (U_FAILURE(status)) { return; }
470
471 // Add SI and binary prefixes
472 for (const auto& unitPrefixInfo : gUnitPrefixStrings) {
473 b.add(unitPrefixInfo.string, unitPrefixInfo.value + kPrefixOffset, status);
474 }
475 if (U_FAILURE(status)) { return; }
476
477 // Add syntax parts (compound, power prefixes)
478 b.add("-per-", COMPOUND_PART_PER, status);
479 b.add("-", COMPOUND_PART_TIMES, status);
480 b.add("-and-", COMPOUND_PART_AND, status);
481 b.add("per-", INITIAL_COMPOUND_PART_PER, status);
482 b.add("square-", POWER_PART_P2, status);
483 b.add("cubic-", POWER_PART_P3, status);
484 b.add("pow2-", POWER_PART_P2, status);
485 b.add("pow3-", POWER_PART_P3, status);
486 b.add("pow4-", POWER_PART_P4, status);
487 b.add("pow5-", POWER_PART_P5, status);
488 b.add("pow6-", POWER_PART_P6, status);
489 b.add("pow7-", POWER_PART_P7, status);
490 b.add("pow8-", POWER_PART_P8, status);
491 b.add("pow9-", POWER_PART_P9, status);
492 b.add("pow10-", POWER_PART_P10, status);
493 b.add("pow11-", POWER_PART_P11, status);
494 b.add("pow12-", POWER_PART_P12, status);
495 b.add("pow13-", POWER_PART_P13, status);
496 b.add("pow14-", POWER_PART_P14, status);
497 b.add("pow15-", POWER_PART_P15, status);
498 if (U_FAILURE(status)) { return; }
499
500 // Add sanctioned simple units by offset: simple units all have entries in
501 // units/convertUnits resources.
502 LocalUResourceBundlePointer convertUnits(
503 ures_getByKey(unitsBundle.getAlias(), "convertUnits", nullptr, &status));
504 if (U_FAILURE(status)) { return; }
505
506 // Allocate enough space: with identifierSink below skipping kilogram, we're
507 // probably allocating one more than needed.
508 int32_t simpleUnitsCount = convertUnits.getAlias()->fSize;
509 int32_t arrayMallocSize = sizeof(char *) * simpleUnitsCount;
510 gSimpleUnits = static_cast<const char **>(uprv_malloc(arrayMallocSize));
511 if (gSimpleUnits == nullptr) {
512 status = U_MEMORY_ALLOCATION_ERROR;
513 return;
514 }
515 uprv_memset(gSimpleUnits, 0, arrayMallocSize):: memset(gSimpleUnits, 0, arrayMallocSize);
516 arrayMallocSize = sizeof(int32_t) * simpleUnitsCount;
517 gSimpleUnitCategories = static_cast<int32_t *>(uprv_malloc(arrayMallocSize));
518 if (gSimpleUnitCategories == nullptr) {
519 status = U_MEMORY_ALLOCATION_ERROR;
520 return;
521 }
522 uprv_memset(gSimpleUnitCategories, 0, arrayMallocSize):: memset(gSimpleUnitCategories, 0, arrayMallocSize);
523
524 // Populate gSimpleUnits and build the associated trie.
525 SimpleUnitIdentifiersSink identifierSink(resultQuantities, gSimpleUnits, gSimpleUnitCategories,
526 simpleUnitsCount, b, kSimpleUnitOffset);
527 ures_getAllItemsWithFallback(unitsBundle.getAlias(), "convertUnits", identifierSink, status);
528
529 // Populate gUnitReplacements and its associated data structures.
530 LocalUResourceBundlePointer aliasBundle(ures_open(U_ICUDATA_ALIAS"ICUDATA", "metadata", &status));
531 if (U_FAILURE(status)) {
532 return;
533 }
534 MaybeStackVector<CharString> unitAliases;
535 MaybeStackVector<CharString> unitReplacements;
536
537 UnitAliasesSink aliasSink(unitAliases, unitReplacements);
538 ures_getAllChildrenWithFallback(aliasBundle.getAlias(), "alias/unit", aliasSink, status);
539 if (U_FAILURE(status)) {
540 return;
541 }
542
543 for (int32_t i = 0; i < unitAliases.length(); i++) {
544 b.add(unitAliases[i]->data(), i + kAliasOffset, status);
545 if (U_FAILURE(status)) {
546 return;
547 }
548 }
549
550 int32_t unitReplacementStringLength = 0;
551 for (int32_t i = 0; i < unitReplacements.length(); i++) {
552 unitReplacementStringLength += unitReplacements[i]->length() + 1;
553 }
554 gUnitReplacementStrings = (const char*)uprv_malloc(unitReplacementStringLength * sizeof(char));
555 gUnitReplacements = (const char**)uprv_malloc(unitReplacements.length() * sizeof(const char**));
556 if (gUnitReplacementStrings == nullptr || gUnitReplacements == nullptr) {
557 status = U_MEMORY_ALLOCATION_ERROR;
558 return;
559 }
560 gNumUnitReplacements = unitReplacements.length();
561 char* p = const_cast<char*>(gUnitReplacementStrings);
562 for (int32_t i = 0; i < unitReplacements.length(); i++) {
563 gUnitReplacements[i] = p;
564 uprv_strcpy(p, unitReplacements[i]->data()):: strcpy(p, unitReplacements[i]->data());
565 p += unitReplacements[i]->length() + 1;
566 }
567
568 // Build the CharsTrie
569 // TODO: Use SLOW or FAST here?
570 StringPiece result = b.buildStringPiece(USTRINGTRIE_BUILD_FAST, status);
571 if (U_FAILURE(status)) { return; }
572
573 // Copy the result into the global constant pointer
574 size_t numBytes = result.length();
575 gSerializedUnitExtrasStemTrie = static_cast<char *>(uprv_malloc(numBytes));
576 if (gSerializedUnitExtrasStemTrie == nullptr) {
577 status = U_MEMORY_ALLOCATION_ERROR;
578 return;
579 }
580 uprv_memcpy(gSerializedUnitExtrasStemTrie, result.data(), numBytes)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (gSerializedUnitExtrasStemTrie !=
__null) ? void (0) : __assert_fail ("gSerializedUnitExtrasStemTrie != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); (static_cast <bool> (result.data() != __null) ? void
(0) : __assert_fail ("result.data() != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(gSerializedUnitExtrasStemTrie, result
.data(), numBytes); } while (false)
;
581}
582
583class Token {
584public:
585 Token(int64_t match) : fMatch(match) {
586 if (fMatch < kCompoundPartOffset) {
587 this->fType = TYPE_PREFIX;
588 } else if (fMatch < kInitialCompoundPartOffset) {
589 this->fType = TYPE_COMPOUND_PART;
590 } else if (fMatch < kPowerPartOffset) {
591 this->fType = TYPE_INITIAL_COMPOUND_PART;
592 } else if (fMatch < kSimpleUnitOffset) {
593 this->fType = TYPE_POWER_PART;
594 } else if (fMatch < kAliasOffset) {
595 this->fType = TYPE_SIMPLE_UNIT;
596 } else {
597 this->fType = TYPE_ALIAS;
598 }
599 }
600
601 static Token constantToken(StringPiece str, UErrorCode &status) {
602 Token result;
603 auto value = Token::parseStringToLong(str, status);
604 if (U_FAILURE(status)) {
605 return result;
606 }
607 result.fMatch = value;
608 result.fType = TYPE_CONSTANT_DENOMINATOR;
609 return result;
610 }
611
612 enum Type {
613 TYPE_UNDEFINED,
614 TYPE_PREFIX,
615 // Token type for "-per-", "-", and "-and-".
616 TYPE_COMPOUND_PART,
617 // Token type for "per-".
618 TYPE_INITIAL_COMPOUND_PART,
619 TYPE_POWER_PART,
620 TYPE_SIMPLE_UNIT,
621 TYPE_CONSTANT_DENOMINATOR,
622 TYPE_ALIAS,
623 };
624
625 // Calling getType() is invalid, resulting in an assertion failure, if Token
626 // value isn't positive.
627 Type getType() const {
628 U_ASSERT(fMatch >= 0)(static_cast <bool> (fMatch >= 0) ? void (0) : __assert_fail
("fMatch >= 0", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
629 return this->fType;
630 }
631
632 // Retrieve the value of the constant denominator if the token is of type TYPE_CONSTANT_DENOMINATOR.
633 uint64_t getConstantDenominator() const {
634 U_ASSERT(getType() == TYPE_CONSTANT_DENOMINATOR)(static_cast <bool> (getType() == TYPE_CONSTANT_DENOMINATOR
) ? void (0) : __assert_fail ("getType() == TYPE_CONSTANT_DENOMINATOR"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
635 return static_cast<uint64_t>(fMatch);
636 }
637
638 UMeasurePrefix getUnitPrefix() const {
639 U_ASSERT(getType() == TYPE_PREFIX)(static_cast <bool> (getType() == TYPE_PREFIX) ? void (
0) : __assert_fail ("getType() == TYPE_PREFIX", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
640 return static_cast<UMeasurePrefix>(fMatch - kPrefixOffset);
641 }
642
643 // Valid only for tokens with type TYPE_COMPOUND_PART.
644 int32_t getMatch() const {
645 U_ASSERT(getType() == TYPE_COMPOUND_PART)(static_cast <bool> (getType() == TYPE_COMPOUND_PART) ?
void (0) : __assert_fail ("getType() == TYPE_COMPOUND_PART",
__builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
646 return fMatch;
647 }
648
649 int32_t getInitialCompoundPart() const {
650 // Even if there is only one InitialCompoundPart value, we have this
651 // function for the simplicity of code consistency.
652 U_ASSERT(getType() == TYPE_INITIAL_COMPOUND_PART)(static_cast <bool> (getType() == TYPE_INITIAL_COMPOUND_PART
) ? void (0) : __assert_fail ("getType() == TYPE_INITIAL_COMPOUND_PART"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
653 // Defensive: if this assert fails, code using this function also needs
654 // to change.
655 U_ASSERT(fMatch == INITIAL_COMPOUND_PART_PER)(static_cast <bool> (fMatch == INITIAL_COMPOUND_PART_PER
) ? void (0) : __assert_fail ("fMatch == INITIAL_COMPOUND_PART_PER"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
656 return fMatch;
657 }
658
659 int8_t getPower() const {
660 U_ASSERT(getType() == TYPE_POWER_PART)(static_cast <bool> (getType() == TYPE_POWER_PART) ? void
(0) : __assert_fail ("getType() == TYPE_POWER_PART", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
661 return static_cast<int8_t>(fMatch - kPowerPartOffset);
662 }
663
664 int32_t getSimpleUnitIndex() const {
665 U_ASSERT(getType() == TYPE_SIMPLE_UNIT)(static_cast <bool> (getType() == TYPE_SIMPLE_UNIT) ? void
(0) : __assert_fail ("getType() == TYPE_SIMPLE_UNIT", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
666 return fMatch - kSimpleUnitOffset;
667 }
668
669 int32_t getAliasIndex() const {
670 U_ASSERT(getType() == TYPE_ALIAS)(static_cast <bool> (getType() == TYPE_ALIAS) ? void (0
) : __assert_fail ("getType() == TYPE_ALIAS", __builtin_FILE (
), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
671 return static_cast<int32_t>(fMatch - kAliasOffset);
672 }
673
674 // TODO: Consider moving this to a separate utility class.
675 // Utility function to parse a string into an unsigned long value.
676 // The value must be a positive integer within the range [1, INT64_MAX].
677 // The input can be in integer or scientific notation.
678 static uint64_t parseStringToLong(const StringPiece strNum, UErrorCode &status) {
679 // We are processing well-formed input, so we don't need any special options to
680 // StringToDoubleConverter.
681 StringToDoubleConverter converter(0, 0, 0, "", "");
682 int32_t count;
683 double double_result = converter.StringToDouble(strNum.data(), strNum.length(), &count);
684 if (count != strNum.length()) {
685 status = kUnitIdentifierSyntaxError;
686 return 0;
687 }
688
689 if (U_FAILURE(status) || double_result < 1.0 || double_result > static_cast<double>(INT64_MAX(9223372036854775807L))) {
690 status = kUnitIdentifierSyntaxError;
691 return 0;
692 }
693
694 // Check if the value is integer.
695 uint64_t int_result = static_cast<uint64_t>(double_result);
696 const double kTolerance = 1e-9;
697 if (abs(double_result - int_result) > kTolerance) {
698 status = kUnitIdentifierSyntaxError;
699 return 0;
700 }
701
702 return int_result;
703 }
704
705private:
706 Token() = default;
707 int64_t fMatch;
708 Type fType = TYPE_UNDEFINED;
709};
710
711class Parser {
712public:
713 /**
714 * Factory function for parsing the given identifier.
715 *
716 * @param source The identifier to parse. This function does not make a copy
717 * of source: the underlying string that source points at, must outlive the
718 * parser.
719 * @param status ICU error code.
720 */
721 static Parser from(StringPiece source, UErrorCode& status) {
722 if (U_FAILURE(status)) {
723 return {};
724 }
725 umtx_initOnce(gUnitExtrasInitOnce, &initUnitExtras, status);
726 if (U_FAILURE(status)) {
727 return {};
728 }
729 return {source};
730 }
731
732 /**
733 * A single unit or a constant denominator.
734 */
735 struct SingleUnitOrConstant {
736 enum ValueType {
737 kSingleUnit,
738 kConstantDenominator,
739 };
740
741 ValueType type = kSingleUnit;
742 SingleUnitImpl singleUnit;
743 uint64_t constantDenominator;
744
745 static SingleUnitOrConstant singleUnitValue(SingleUnitImpl singleUnit) {
746 SingleUnitOrConstant result;
747 result.type = kSingleUnit;
748 result.singleUnit = singleUnit;
749 result.constantDenominator = 0;
750 return result;
751 }
752
753 static SingleUnitOrConstant constantDenominatorValue(uint64_t constant) {
754 SingleUnitOrConstant result;
755 result.type = kConstantDenominator;
756 result.singleUnit = {};
757 result.constantDenominator = constant;
758 return result;
759 }
760
761 uint64_t getConstantDenominator() const {
762 U_ASSERT(type == kConstantDenominator)(static_cast <bool> (type == kConstantDenominator) ? void
(0) : __assert_fail ("type == kConstantDenominator", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
763 return constantDenominator;
764 }
765
766 SingleUnitImpl getSingleUnit() const {
767 U_ASSERT(type == kSingleUnit)(static_cast <bool> (type == kSingleUnit) ? void (0) : __assert_fail
("type == kSingleUnit", __builtin_FILE (), __builtin_LINE ()
, __extension__ __PRETTY_FUNCTION__))
;
768 return singleUnit;
769 }
770
771 bool isSingleUnit() const { return type == kSingleUnit; }
772
773 bool isConstantDenominator() const { return type == kConstantDenominator; }
774 };
775
776 MeasureUnitImpl parse(UErrorCode& status) {
777 MeasureUnitImpl result;
778
779 if (U_FAILURE(status)) {
780 return result;
781 }
782 if (fSource.empty()) {
783 // The dimenionless unit: nothing to parse. leave result as is.
784 return result;
785 }
786
787 while (hasNext()) {
788 bool sawAnd = false;
789
790 auto singleUnitOrConstant = nextSingleUnitOrConstant(sawAnd, status);
791 if (U_FAILURE(status)) {
792 return result;
793 }
794
795 if (singleUnitOrConstant.isConstantDenominator()) {
796 if (result.constantDenominator > 0) {
797 status = kUnitIdentifierSyntaxError;
798 return result;
799 }
800 result.constantDenominator = singleUnitOrConstant.getConstantDenominator();
801 result.complexity = UMEASURE_UNIT_COMPOUND;
802 continue;
803 }
804
805 U_ASSERT(singleUnitOrConstant.isSingleUnit())(static_cast <bool> (singleUnitOrConstant.isSingleUnit(
)) ? void (0) : __assert_fail ("singleUnitOrConstant.isSingleUnit()"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
806 bool added = result.appendSingleUnit(singleUnitOrConstant.getSingleUnit(), status);
807 if (U_FAILURE(status)) {
808 return result;
809 }
810
811 if (sawAnd && !added) {
812 // Two similar units are not allowed in a mixed unit.
813 status = kUnitIdentifierSyntaxError;
814 return result;
815 }
816
817 if (result.singleUnits.length() >= 2) {
818 // nextSingleUnit fails appropriately for "per" and "and" in the
819 // same identifier. It doesn't fail for other compound units
820 // (COMPOUND_PART_TIMES). Consequently we take care of that
821 // here.
822 UMeasureUnitComplexity complexity =
823 sawAnd ? UMEASURE_UNIT_MIXED : UMEASURE_UNIT_COMPOUND;
824 if (result.singleUnits.length() == 2) {
825 // After appending two singleUnits, the complexity will be `UMEASURE_UNIT_COMPOUND`
826 U_ASSERT(result.complexity == UMEASURE_UNIT_COMPOUND)(static_cast <bool> (result.complexity == UMEASURE_UNIT_COMPOUND
) ? void (0) : __assert_fail ("result.complexity == UMEASURE_UNIT_COMPOUND"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
827 result.complexity = complexity;
828 } else if (result.complexity != complexity) {
829 // Can't have mixed compound units
830 status = kUnitIdentifierSyntaxError;
831 return result;
832 }
833 }
834 }
835
836 if (result.singleUnits.length() == 0) {
837 // The identifier was empty or only had a constant denominator.
838 status = kUnitIdentifierSyntaxError;
839 return result; // add it for code consistency.
840 }
841
842 return result;
843 }
844
845private:
846 // Tracks parser progress: the offset into fSource.
847 int32_t fIndex = 0;
848
849 // Since we're not owning this memory, whatever is passed to the constructor
850 // should live longer than this Parser - and the parser shouldn't return any
851 // references to that string.
852 StringPiece fSource;
853 BytesTrie fTrie;
854
855 // Storage for modified source string when aliases are expanded
856 CharString fModifiedSource;
857
858 // Set to true when we've seen a "-per-" or a "per-", after which all units
859 // are in the denominator. Until we find an "-and-", at which point the
860 // identifier is invalid pending TODO(CLDR-13701).
861 bool fAfterPer = false;
862
863 // Set to true when we've just seen a "per-". This is used to determine if
864 // the next token can be a constant denominator token.
865 bool fJustSawPer = false;
866
867 Parser() : fSource(""), fTrie(u"") {}
868
869 Parser(StringPiece source)
870 : fSource(source), fTrie(gSerializedUnitExtrasStemTrie) {}
871
872 inline bool hasNext() const {
873 return fIndex < fSource.length();
874 }
875
876 // Returns the next Token parsed from fSource, advancing fIndex to the end
877 // of that token in fSource. In case of U_FAILURE(status), the token
878 // returned will cause an abort if getType() is called on it.
879 Token nextToken(UErrorCode& status) {
880 fTrie.reset();
881 int32_t match = -1;
882 // Saves the position in the fSource string for the end of the most
883 // recent matching token.
884 int32_t previ = -1;
885
886 // Saves the position in the fSource string for later use in case of unit constant found.
887 int32_t currentFIndex = fIndex;
888
889 // Find the longest token that matches a value in the trie:
890 while (fIndex < fSource.length()) {
891 auto result = fTrie.next(fSource.data()[fIndex++]);
892 if (result == USTRINGTRIE_NO_MATCH) {
893 break;
894 } else if (result == USTRINGTRIE_NO_VALUE) {
895 continue;
896 }
897 U_ASSERT(USTRINGTRIE_HAS_VALUE(result))(static_cast <bool> (((result)>=USTRINGTRIE_FINAL_VALUE
)) ? void (0) : __assert_fail ("((result)>=USTRINGTRIE_FINAL_VALUE)"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
898 match = fTrie.getValue();
899 previ = fIndex;
900 if (result == USTRINGTRIE_FINAL_VALUE) {
901 break;
902 }
903 U_ASSERT(result == USTRINGTRIE_INTERMEDIATE_VALUE)(static_cast <bool> (result == USTRINGTRIE_INTERMEDIATE_VALUE
) ? void (0) : __assert_fail ("result == USTRINGTRIE_INTERMEDIATE_VALUE"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
904 // continue;
905 }
906
907 if (match >= 0) {
908 fIndex = previ;
909 return {match};
910 }
911
912 // If no match was found, we check if the token is a constant denominator.
913 // 1. We find the index of the start of the next token or the end of the string.
914 int32_t endOfConstantIndex = fSource.find("-", currentFIndex);
915 endOfConstantIndex = (endOfConstantIndex == -1) ? fSource.length() : endOfConstantIndex;
916 if (endOfConstantIndex <= currentFIndex) {
917 status = kUnitIdentifierSyntaxError;
918 return {match};
919 }
920
921 // 2. We extract the substring from the start of the constant to the end of the constant.
922 StringPiece constantDenominatorStr =
923 fSource.substr(currentFIndex, endOfConstantIndex - currentFIndex);
924 fIndex = endOfConstantIndex;
925 return Token::constantToken(constantDenominatorStr, status);
926 }
927
928 /**
929 * Returns the next "single unit" via result.
930 *
931 * If a "-per-" was parsed, the result will have appropriate negative
932 * dimensionality.
933 *
934 * Returns an error if we parse both compound units and "-and-", since mixed
935 * compound units are not yet supported - TODO(CLDR-13701).
936 *
937 * @param result Will be overwritten by the result, if status shows success.
938 * @param sawAnd If an "-and-" was parsed prior to finding the "single
939 * unit", sawAnd is set to true. If not, it is left as is.
940 * @param status ICU error code.
941 */
942 SingleUnitOrConstant nextSingleUnitOrConstant(bool &sawAnd, UErrorCode &status) {
943 SingleUnitImpl singleUnitResult;
944 if (U_FAILURE(status)) {
945 return {};
946 }
947
948 // state:
949 // 0 = no tokens seen yet (will accept power, SI or binary prefix, or simple unit)
950 // 1 = power token seen (will not accept another power token)
951 // 2 = SI or binary prefix token seen (will not accept a power, or SI or binary prefix token)
952 int32_t state = 0;
953
954 bool atStart = fIndex == 0;
955 Token token = nextToken(status);
956 if (U_FAILURE(status)) {
957 return {};
958 }
959
960 // Handles the case where the alias replacement begins with "per-".
961 // For example:
962 // if the alias is "permeter" and the replacement is "per-meter".
963 // NOTE: This case does not currently exist in CLDR, but this code anticipates possible future
964 // additions.
965 if (token.getType() == Token::TYPE_ALIAS) {
966 processAlias(token, status);
967 token = nextToken(status);
968 if (U_FAILURE(status)) {
969 return {};
970 }
971 }
972
973 fJustSawPer = false;
974
975 if (atStart) {
976 // Identifiers optionally start with "per-".
977 if (token.getType() == Token::TYPE_INITIAL_COMPOUND_PART) {
978 U_ASSERT(token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER)(static_cast <bool> (token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER
) ? void (0) : __assert_fail ("token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
979 fAfterPer = true;
980 fJustSawPer = true;
981 singleUnitResult.dimensionality = -1;
982
983 token = nextToken(status);
984 if (U_FAILURE(status)) {
985 return {};
986 }
987 }
988 } else {
989 // All other SingleUnit's are separated from previous SingleUnit's
990 // via a compound part:
991 if (token.getType() != Token::TYPE_COMPOUND_PART) {
992 status = kUnitIdentifierSyntaxError;
993 return {};
994 }
995
996 switch (token.getMatch()) {
997 case COMPOUND_PART_PER:
998 if (sawAnd) {
999 // Mixed compound units not yet supported,
1000 // TODO(CLDR-13701).
1001 status = kUnitIdentifierSyntaxError;
1002 return {};
1003 }
1004 fAfterPer = true;
1005 fJustSawPer = true;
1006 singleUnitResult.dimensionality = -1;
1007 break;
1008
1009 case COMPOUND_PART_TIMES:
1010 if (fAfterPer) {
1011 singleUnitResult.dimensionality = -1;
1012 }
1013 break;
1014
1015 case COMPOUND_PART_AND:
1016 if (fAfterPer) {
1017 // Can't start with "-and-", and mixed compound units
1018 // not yet supported, TODO(CLDR-13701).
1019 status = kUnitIdentifierSyntaxError;
1020 return {};
1021 }
1022 sawAnd = true;
1023 break;
1024 }
1025
1026 token = nextToken(status);
1027 if (U_FAILURE(status)) {
1028 return {};
1029 }
1030 }
1031
1032 if (token.getType() == Token::TYPE_CONSTANT_DENOMINATOR) {
1033 if (!fJustSawPer) {
1034 status = kUnitIdentifierSyntaxError;
1035 return {};
1036 }
1037
1038 return SingleUnitOrConstant::constantDenominatorValue(token.getConstantDenominator());
1039 }
1040
1041 // Read tokens until we have a complete SingleUnit or we reach the end.
1042 while (true) {
1043 switch (token.getType()) {
1044 case Token::TYPE_POWER_PART:
1045 if (state > 0) {
1046 status = kUnitIdentifierSyntaxError;
1047 return {};
1048 }
1049 singleUnitResult.dimensionality *= token.getPower();
1050 state = 1;
1051 break;
1052
1053 case Token::TYPE_PREFIX:
1054 if (state > 1) {
1055 status = kUnitIdentifierSyntaxError;
1056 return {};
1057 }
1058 singleUnitResult.unitPrefix = token.getUnitPrefix();
1059 state = 2;
1060 break;
1061
1062 case Token::TYPE_SIMPLE_UNIT:
1063 singleUnitResult.index = token.getSimpleUnitIndex();
1064 break;
1065
1066 case Token::TYPE_ALIAS:
1067 processAlias(token, status);
1068 break;
1069
1070 default:
1071 status = kUnitIdentifierSyntaxError;
1072 return {};
1073 }
1074
1075 if (token.getType() == Token::TYPE_SIMPLE_UNIT) {
1076 break;
1077 }
1078
1079 if (!hasNext()) {
1080 // We ran out of tokens before finding a complete single unit.
1081 status = kUnitIdentifierSyntaxError;
1082 return {};
1083 }
1084 token = nextToken(status);
1085 if (U_FAILURE(status)) {
1086 return {};
1087 }
1088 }
1089
1090 return SingleUnitOrConstant::singleUnitValue(singleUnitResult);
1091 }
1092
1093 private:
1094 /**
1095 * Helper function to process alias replacement.
1096 *
1097 * @param token The token of TYPE_ALIAS to process
1098 * @param status ICU error code
1099 */
1100 void processAlias(const Token &token, UErrorCode &status) {
1101 if (U_FAILURE(status)) {
1102 return;
1103 }
1104
1105 auto aliasIndex = token.getAliasIndex();
1106 if (aliasIndex < 0 || aliasIndex >= gNumUnitReplacements) {
1107 status = kUnitIdentifierSyntaxError;
1108 return;
1109 }
1110 const char* replacement = gUnitReplacements[aliasIndex];
1111
1112 // Create new source string: replacement + remaining unparsed portion
1113 fModifiedSource.clear();
1114 fModifiedSource.append(StringPiece(replacement), status);
1115
1116 // Add the remaining unparsed portion of fSource which starts from fIndex
1117 if (fIndex < fSource.length()) {
1118 StringPiece remaining = fSource.substr(fIndex);
1119 fModifiedSource.append(remaining.data(), remaining.length(), status);
1120 }
1121
1122 if (U_FAILURE(status)) {
1123 return;
1124 }
1125
1126 // Update parser state with new source and reset index
1127 fSource = StringPiece(fModifiedSource.data(), fModifiedSource.length());
1128 fIndex = 0;
1129
1130 return;
1131 }
1132};
1133
1134// Sorting function wrapping SingleUnitImpl::compareTo for use with uprv_sortArray.
1135int32_t U_CALLCONV
1136compareSingleUnits(const void* /*context*/, const void* left, const void* right) {
1137 const auto* realLeft = static_cast<const SingleUnitImpl* const*>(left);
1138 const auto* realRight = static_cast<const SingleUnitImpl* const*>(right);
1139 return (*realLeft)->compareTo(**realRight);
1140}
1141
1142// Returns an index into the gCategories array, for the "unitQuantity" (aka
1143// "type" or "category") associated with the given base unit identifier. Returns
1144// -1 on failure, together with U_UNSUPPORTED_ERROR.
1145int32_t getUnitCategoryIndex(BytesTrie &trie, StringPiece baseUnitIdentifier, UErrorCode &status) {
1146 UStringTrieResult result = trie.reset().next(baseUnitIdentifier.data(), baseUnitIdentifier.length());
1147 if (!USTRINGTRIE_HAS_VALUE(result)((result)>=USTRINGTRIE_FINAL_VALUE)) {
1148 status = U_UNSUPPORTED_ERROR;
1149 return -1;
1150 }
1151
1152 return trie.getValue();
1153}
1154
1155} // namespace
1156
1157U_CAPIextern "C" int32_t U_EXPORT2
1158umeas_getPrefixPower(UMeasurePrefix unitPrefix) {
1159 if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN &&
1160 unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) {
1161 return unitPrefix - UMEASURE_PREFIX_INTERNAL_ONE_BIN;
1162 }
1163 U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI &&(static_cast <bool> (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI
&& unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI)
? void (0) : __assert_fail ("unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI && unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
1164 unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI)(static_cast <bool> (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI
&& unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI)
? void (0) : __assert_fail ("unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI && unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1165 return unitPrefix - UMEASURE_PREFIX_ONE;
1166}
1167
1168U_CAPIextern "C" int32_t U_EXPORT2
1169umeas_getPrefixBase(UMeasurePrefix unitPrefix) {
1170 if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN &&
1171 unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) {
1172 return 1024;
1173 }
1174 U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI &&(static_cast <bool> (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI
&& unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI)
? void (0) : __assert_fail ("unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI && unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
1175 unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI)(static_cast <bool> (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI
&& unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI)
? void (0) : __assert_fail ("unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI && unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1176 return 10;
1177}
1178
1179CharString U_I18N_API getUnitQuantity(const MeasureUnitImpl &baseMeasureUnitImpl, UErrorCode &status) {
1180 CharString result;
1181 MeasureUnitImpl baseUnitImpl = baseMeasureUnitImpl.copy(status);
1182 UErrorCode localStatus = U_ZERO_ERROR;
1183 umtx_initOnce(gUnitExtrasInitOnce, &initUnitExtras, status);
1184 if (U_FAILURE(status)) {
1185 return result;
1186 }
1187 BytesTrie trie(gSerializedUnitCategoriesTrie);
1188
1189 baseUnitImpl.serialize(status);
1190 StringPiece identifier = baseUnitImpl.identifier.data();
1191 int32_t idx = getUnitCategoryIndex(trie, identifier, localStatus);
1192 if (U_FAILURE(status)) {
1193 return result;
1194 }
1195
1196 // In case the base unit identifier did not match any entry.
1197 if (U_FAILURE(localStatus)) {
1198 localStatus = U_ZERO_ERROR;
1199 baseUnitImpl.takeReciprocal(status);
1200 baseUnitImpl.serialize(status);
1201 identifier.set(baseUnitImpl.identifier.data());
1202 idx = getUnitCategoryIndex(trie, identifier, localStatus);
1203
1204 if (U_FAILURE(status)) {
1205 return result;
1206 }
1207 }
1208
1209 // In case the reciprocal of the base unit identifier did not match any entry.
1210 MeasureUnitImpl simplifiedUnit = baseMeasureUnitImpl.copyAndSimplify(status);
1211 if (U_FAILURE(status)) {
1212 return result;
1213 }
1214 if (U_FAILURE(localStatus)) {
1215 localStatus = U_ZERO_ERROR;
1216 simplifiedUnit.serialize(status);
1217 identifier.set(simplifiedUnit.identifier.data());
1218 idx = getUnitCategoryIndex(trie, identifier, localStatus);
1219
1220 if (U_FAILURE(status)) {
1221 return result;
1222 }
1223 }
1224
1225 // In case the simplified base unit identifier did not match any entry.
1226 if (U_FAILURE(localStatus)) {
1227 localStatus = U_ZERO_ERROR;
1228 simplifiedUnit.takeReciprocal(status);
1229 simplifiedUnit.serialize(status);
1230 identifier.set(simplifiedUnit.identifier.data());
1231 idx = getUnitCategoryIndex(trie, identifier, localStatus);
1232
1233 if (U_FAILURE(status)) {
1234 return result;
1235 }
1236 }
1237
1238 // If there is no match at all, throw an exception.
1239 if (U_FAILURE(localStatus)) {
1240 status = U_INVALID_FORMAT_ERROR;
1241 return result;
1242 }
1243
1244 if (idx < 0 || idx >= gCategoriesCount) {
1245 status = U_INVALID_FORMAT_ERROR;
1246 return result;
1247 }
1248
1249 result.appendInvariantChars(gCategories[idx], u_strlen(gCategories[idx]), status);
1250 return result;
1251}
1252
1253// In ICU4J, this is MeasureUnit.getSingleUnitImpl().
1254SingleUnitImpl SingleUnitImpl::forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status) {
1255 MeasureUnitImpl temp;
1256 const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(measureUnit, temp, status);
1257 if (U_FAILURE(status)) {
1258 return {};
1259 }
1260 if (impl.singleUnits.length() == 0) {
1261 return {};
1262 }
1263 if (impl.singleUnits.length() == 1) {
1264 return *impl.singleUnits[0];
1265 }
1266 status = U_ILLEGAL_ARGUMENT_ERROR;
1267 return {};
1268}
1269
1270MeasureUnit SingleUnitImpl::build(UErrorCode& status) const {
1271 MeasureUnitImpl temp;
1272 temp.appendSingleUnit(*this, status);
1273 // TODO(icu-units#28): the MeasureUnitImpl::build() method uses
1274 // findBySubtype, which is relatively slow.
1275 // - At the time of loading the simple unit IDs, we could also save a
1276 // mapping to the builtin MeasureUnit type and subtype they correspond to.
1277 // - This method could then check dimensionality and index, and if both are
1278 // 1, directly return MeasureUnit instances very quickly.
1279 return std::move(temp).build(status);
1280}
1281
1282const char *SingleUnitImpl::getSimpleUnitID() const {
1283 return gSimpleUnits[index];
1284}
1285
1286void SingleUnitImpl::appendNeutralIdentifier(CharString &result, UErrorCode &status) const UPRV_NO_SANITIZE_UNDEFINED__attribute__((no_sanitize("undefined"))) {
1287 int32_t absPower = std::abs(this->dimensionality);
1288
1289 U_ASSERT(absPower > 0)(static_cast <bool> (absPower > 0) ? void (0) : __assert_fail
("absPower > 0", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; // "this function does not support the dimensionless single units";
1290
1291 if (absPower == 1) {
1292 // no-op
1293 } else if (absPower == 2) {
1294 result.append(StringPiece("square-"), status);
1295 } else if (absPower == 3) {
1296 result.append(StringPiece("cubic-"), status);
1297 } else if (absPower <= 15) {
1298 result.append(StringPiece("pow"), status);
1299 result.appendNumber(absPower, status);
1300 result.append(StringPiece("-"), status);
1301 } else {
1302 status = U_ILLEGAL_ARGUMENT_ERROR; // Unit Identifier Syntax Error
1303 return;
1304 }
1305
1306 if (U_FAILURE(status)) {
1307 return;
1308 }
1309
1310 if (this->unitPrefix != UMEASURE_PREFIX_ONE) {
1311 bool found = false;
1312 for (const auto &unitPrefixInfo : gUnitPrefixStrings) {
1313 // TODO: consider using binary search? If we do this, add a unit
1314 // test to ensure gUnitPrefixStrings is sorted?
1315 if (unitPrefixInfo.value == this->unitPrefix) {
1316 result.append(unitPrefixInfo.string, status);
1317 found = true;
1318 break;
1319 }
1320 }
1321 if (!found) {
1322 status = U_UNSUPPORTED_ERROR;
1323 return;
1324 }
1325 }
1326
1327 result.append(StringPiece(this->getSimpleUnitID()), status);
1328}
1329
1330int32_t SingleUnitImpl::getUnitCategoryIndex() const {
1331 return gSimpleUnitCategories[index];
1332}
1333
1334MeasureUnitImpl::MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status) {
1335 this->appendSingleUnit(singleUnit, status);
1336}
1337
1338MeasureUnitImpl MeasureUnitImpl::forIdentifier(StringPiece identifier, UErrorCode& status) {
1339 return Parser::from(identifier, status).parse(status);
1340}
1341
1342const MeasureUnitImpl& MeasureUnitImpl::forMeasureUnit(
1343 const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status) {
1344 if (measureUnit.fImpl) {
1345 return *measureUnit.fImpl;
1346 } else {
1347 memory = Parser::from(measureUnit.getIdentifier(), status).parse(status);
1348 return memory;
1349 }
1350}
1351
1352MeasureUnitImpl MeasureUnitImpl::forMeasureUnitMaybeCopy(
1353 const MeasureUnit& measureUnit, UErrorCode& status) {
1354 if (measureUnit.fImpl) {
1355 return measureUnit.fImpl->copy(status);
1356 } else {
1357 return Parser::from(measureUnit.getIdentifier(), status).parse(status);
1358 }
1359}
1360
1361void MeasureUnitImpl::takeReciprocal(UErrorCode& /*status*/) {
1362 identifier.clear();
1363 for (int32_t i = 0; i < singleUnits.length(); i++) {
1364 singleUnits[i]->dimensionality *= -1;
1365 }
1366}
1367
1368MeasureUnitImpl MeasureUnitImpl::copyAndSimplify(UErrorCode &status) const {
1369 MeasureUnitImpl result;
1370 for (int32_t i = 0; i < singleUnits.length(); i++) {
1371 const SingleUnitImpl &singleUnit = *this->singleUnits[i];
1372
1373 // The following `for` loop will cause time complexity to be O(n^2).
1374 // However, n is very small (number of units, generally, at maximum equal to 10)
1375 bool unitExist = false;
1376 for (int32_t j = 0; j < result.singleUnits.length(); j++) {
1377 if (uprv_strcmp(result.singleUnits[j]->getSimpleUnitID(), singleUnit.getSimpleUnitID()):: strcmp(result.singleUnits[j]->getSimpleUnitID(), singleUnit
.getSimpleUnitID())
==
1378 0 &&
1379 result.singleUnits[j]->unitPrefix == singleUnit.unitPrefix) {
1380 unitExist = true;
1381 result.singleUnits[j]->dimensionality =
1382 result.singleUnits[j]->dimensionality + singleUnit.dimensionality;
1383 break;
1384 }
1385 }
1386
1387 if (!unitExist) {
1388 result.appendSingleUnit(singleUnit, status);
1389 }
1390 }
1391
1392 return result;
1393}
1394
1395bool MeasureUnitImpl::appendSingleUnit(const SingleUnitImpl &singleUnit, UErrorCode &status) {
1396 identifier.clear();
1397
1398 if (singleUnit.isDimensionless()) {
1399 // Do not append dimensionless units.
1400 return false;
1401 }
1402
1403 // Find a similar unit that already exists, to attempt to coalesce
1404 SingleUnitImpl *oldUnit = nullptr;
1405 for (int32_t i = 0; i < this->singleUnits.length(); i++) {
1406 auto *candidate = this->singleUnits[i];
1407 if (candidate->isCompatibleWith(singleUnit)) {
1408 oldUnit = candidate;
1409 }
1410 }
1411
1412 if (oldUnit) {
1413 // Both dimensionalities will be positive, or both will be negative, by
1414 // virtue of isCompatibleWith().
1415 oldUnit->dimensionality += singleUnit.dimensionality;
1416
1417 return false;
1418 }
1419
1420 // Add a copy of singleUnit
1421 // NOTE: MaybeStackVector::emplaceBackAndCheckErrorCode creates new copy of singleUnit.
1422 this->singleUnits.emplaceBackAndCheckErrorCode(status, singleUnit);
1423 if (U_FAILURE(status)) {
1424 return false;
1425 }
1426
1427 // If the MeasureUnitImpl is `UMEASURE_UNIT_SINGLE` and after the appending a unit, the `singleUnits`
1428 // contains more than one. thus means the complexity should be `UMEASURE_UNIT_COMPOUND`
1429 if (this->singleUnits.length() > 1 &&
1430 this->complexity == UMeasureUnitComplexity::UMEASURE_UNIT_SINGLE) {
1431 this->complexity = UMeasureUnitComplexity::UMEASURE_UNIT_COMPOUND;
1432 }
1433
1434 return true;
1435}
1436
1437MaybeStackVector<MeasureUnitImplWithIndex>
1438MeasureUnitImpl::extractIndividualUnitsWithIndices(UErrorCode &status) const {
1439 MaybeStackVector<MeasureUnitImplWithIndex> result;
1440
1441 if (this->complexity != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
1
Assuming field 'complexity' is equal to UMEASURE_UNIT_MIXED
2
Taking false branch
1442 result.emplaceBackAndCheckErrorCode(status, 0, *this, status);
1443 return result;
1444 }
1445
1446 for (int32_t i = 0; i < singleUnits.length(); ++i) {
3
Assuming the condition is false
4
Loop condition is false. Execution continues on line 1453
1447 result.emplaceBackAndCheckErrorCode(status, i, *singleUnits[i], status);
1448 if (U_FAILURE(status)) {
1449 return result;
1450 }
1451 }
1452
1453 return result;
5
Calling implicit move constructor for 'MaybeStackVector<icu_78::MeasureUnitImplWithIndex, 8>'
6
Calling move constructor for 'MemoryPool<icu_78::MeasureUnitImplWithIndex, 8>'
1454}
1455
1456int32_t countCharacter(const CharString &str, char c) {
1457 int32_t count = 0;
1458 for (int32_t i = 0, n = str.length(); i < n; i++) {
1459 if (str[i] == c) {
1460 count++;
1461 }
1462 }
1463 return count;
1464}
1465
1466/**
1467 * Internal function that returns a string of the constants in the correct
1468 * format.
1469 *
1470 * Example:
1471 * 1000 --> "-per-1000"
1472 * 1000000 --> "-per-1e6"
1473 *
1474 * NOTE: this function is only used when the constant denominator is greater
1475 * than 0.
1476 */
1477CharString getConstantsString(uint64_t constantDenominator, UErrorCode &status) {
1478 U_ASSERT(constantDenominator > 0 && constantDenominator <= LLONG_MAX)(static_cast <bool> (constantDenominator > 0 &&
constantDenominator <= 9223372036854775807LL) ? void (0) :
__assert_fail ("constantDenominator > 0 && constantDenominator <= 9223372036854775807LL"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1479
1480 CharString result;
1481 result.appendNumber(constantDenominator, status);
1482 if (U_FAILURE(status)) {
1483 return result;
1484 }
1485
1486 if (constantDenominator <= 1000) {
1487 return result;
1488 }
1489
1490 // Check if the constant is a power of 10.
1491 int32_t zeros = countCharacter(result, '0');
1492 if (zeros == result.length() - 1 && result[0] == '1') {
1493 result.clear();
1494 result.append(StringPiece("1e"), status);
1495 result.appendNumber(zeros, status);
1496 }
1497
1498 return result;
1499}
1500
1501/**
1502 * Normalize a MeasureUnitImpl and generate the identifier string in place.
1503 */
1504void MeasureUnitImpl::serialize(UErrorCode &status) {
1505 if (U_FAILURE(status)) {
1506 return;
1507 }
1508
1509 if (this->singleUnits.length() == 0 && this->constantDenominator == 0) {
1510 // Dimensionless, constructed by the default constructor.
1511 return;
1512 }
1513
1514 if (this->complexity == UMEASURE_UNIT_COMPOUND) {
1515 // Note: don't sort a MIXED unit
1516 uprv_sortArray(this->singleUnits.getAlias(), this->singleUnits.length(),
1517 sizeof(this->singleUnits[0]), compareSingleUnits, nullptr, false, &status);
1518 if (U_FAILURE(status)) {
1519 return;
1520 }
1521 }
1522
1523 CharString result;
1524 bool beforePer = true;
1525 bool firstTimeNegativeDimension = false;
1526 bool constantDenominatorAppended = false;
1527 for (int32_t i = 0; i < this->singleUnits.length(); i++) {
1528 if (beforePer && (*this->singleUnits[i]).dimensionality < 0) {
1529 beforePer = false;
1530 firstTimeNegativeDimension = true;
1531 } else if ((*this->singleUnits[i]).dimensionality < 0) {
1532 firstTimeNegativeDimension = false;
1533 }
1534
1535 if (U_FAILURE(status)) {
1536 return;
1537 }
1538
1539 if (this->complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
1540 if (result.length() != 0) {
1541 result.append(StringPiece("-and-"), status);
1542 }
1543 } else {
1544 if (firstTimeNegativeDimension) {
1545 if (result.length() == 0) {
1546 result.append(StringPiece("per-"), status);
1547 } else {
1548 result.append(StringPiece("-per-"), status);
1549 }
1550
1551 if (this->constantDenominator > 0) {
1552 result.append(getConstantsString(this->constantDenominator, status), status);
1553 result.append(StringPiece("-"), status);
1554 constantDenominatorAppended = true;
1555 }
1556
1557 } else if (result.length() != 0) {
1558 result.append(StringPiece("-"), status);
1559 }
1560 }
1561
1562 this->singleUnits[i]->appendNeutralIdentifier(result, status);
1563 }
1564
1565 if (!constantDenominatorAppended && this->constantDenominator > 0) {
1566 result.append(StringPiece("-per-"), status);
1567 result.append(getConstantsString(this->constantDenominator, status), status);
1568 }
1569
1570 if (U_FAILURE(status)) {
1571 return;
1572 }
1573 this->identifier = result.toStringPiece();
1574 if (this->identifier.isEmpty() != result.isEmpty()) {
1575 status = U_MEMORY_ALLOCATION_ERROR;
1576 }
1577}
1578
1579MeasureUnit MeasureUnitImpl::build(UErrorCode &status) && {
1580 this->serialize(status);
1581 return MeasureUnit(std::move(*this));
1582}
1583
1584MeasureUnit MeasureUnit::forIdentifier(StringPiece identifier, UErrorCode &status) {
1585 return Parser::from(identifier, status).parse(status).build(status);
1586}
1587
1588UMeasureUnitComplexity MeasureUnit::getComplexity(UErrorCode &status) const {
1589 MeasureUnitImpl temp;
1590 return MeasureUnitImpl::forMeasureUnit(*this, temp, status).complexity;
1591}
1592
1593UMeasurePrefix MeasureUnit::getPrefix(UErrorCode &status) const {
1594 return SingleUnitImpl::forMeasureUnit(*this, status).unitPrefix;
1595}
1596
1597MeasureUnit MeasureUnit::withPrefix(UMeasurePrefix prefix,
1598 UErrorCode &status) const UPRV_NO_SANITIZE_UNDEFINED__attribute__((no_sanitize("undefined"))) {
1599 SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
1600 singleUnit.unitPrefix = prefix;
1601 return singleUnit.build(status);
1602}
1603
1604uint64_t MeasureUnit::getConstantDenominator(UErrorCode &status) const {
1605 // TODO(ICU-23219)
1606 auto measureUnitImpl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status);
1607 if (U_FAILURE(status)) {
1608 return 0;
1609 }
1610
1611 auto complexity = measureUnitImpl.complexity;
1612
1613 if (complexity != UMEASURE_UNIT_SINGLE && complexity != UMEASURE_UNIT_COMPOUND) {
1614 status = U_ILLEGAL_ARGUMENT_ERROR;
1615 return 0;
1616 }
1617
1618
1619 return measureUnitImpl.constantDenominator;
1620}
1621
1622MeasureUnit MeasureUnit::withConstantDenominator(uint64_t denominator, UErrorCode &status) const {
1623 // To match the behavior of the Java API, we do not allow a constant denominator
1624 // bigger than LONG_MAX.
1625 if (denominator > LONG_MAX9223372036854775807L) {
1626 status = U_ILLEGAL_ARGUMENT_ERROR;
1627 return {};
1628 }
1629
1630 auto complexity = this->getComplexity(status);
1631 if (U_FAILURE(status)) {
1632 return {};
1633 }
1634 if (complexity != UMEASURE_UNIT_SINGLE && complexity != UMEASURE_UNIT_COMPOUND) {
1635 status = U_ILLEGAL_ARGUMENT_ERROR;
1636 return {};
1637 }
1638
1639 MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status);
1640 if (U_FAILURE(status)) {
1641 return {};
1642 }
1643
1644 impl.constantDenominator = denominator;
1645 impl.complexity = (impl.singleUnits.length() < 2 && denominator == 0) ? UMEASURE_UNIT_SINGLE
1646 : UMEASURE_UNIT_COMPOUND;
1647 return std::move(impl).build(status);
1648}
1649
1650int32_t MeasureUnit::getDimensionality(UErrorCode& status) const {
1651 SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
1652 if (U_FAILURE(status)) { return 0; }
1653 if (singleUnit.isDimensionless()) {
1654 return 0;
1655 }
1656 return singleUnit.dimensionality;
1657}
1658
1659MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode& status) const {
1660 SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
1661 singleUnit.dimensionality = dimensionality;
1662 return singleUnit.build(status);
1663}
1664
1665MeasureUnit MeasureUnit::reciprocal(UErrorCode& status) const {
1666 MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status);
1667 // The reciprocal of a unit that has a constant denominator is not allowed.
1668 if (impl.constantDenominator != 0) {
1669 status = U_ILLEGAL_ARGUMENT_ERROR;
1670 return {};
1671 }
1672 impl.takeReciprocal(status);
1673 return std::move(impl).build(status);
1674}
1675
1676MeasureUnit MeasureUnit::product(const MeasureUnit& other, UErrorCode& status) const {
1677 MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, status);
1678 MeasureUnitImpl temp;
1679 const MeasureUnitImpl& otherImpl = MeasureUnitImpl::forMeasureUnit(other, temp, status);
1680 if (impl.complexity == UMEASURE_UNIT_MIXED || otherImpl.complexity == UMEASURE_UNIT_MIXED) {
1681 status = U_ILLEGAL_ARGUMENT_ERROR;
1682 return {};
1683 }
1684 for (int32_t i = 0; i < otherImpl.singleUnits.length(); i++) {
1685 impl.appendSingleUnit(*otherImpl.singleUnits[i], status);
1686 }
1687
1688 uint64_t currentConstatDenominator = impl.constantDenominator;
1689 uint64_t otherConstantDenominator = otherImpl.constantDenominator;
1690
1691 // TODO: we can also multiply the constant denominators instead of returning an error.
1692 if (currentConstatDenominator != 0 && otherConstantDenominator != 0) {
1693 // There is only `one` constant denominator in a compound unit.
1694 // Therefore, we Cannot multiply units that both of them have a constant denominator
1695 status = U_ILLEGAL_ARGUMENT_ERROR;
1696 return {};
1697 }
1698
1699 // Because either one of the constant denominators is zero, we can use the maximum of them.
1700 impl.constantDenominator = uprv_max(currentConstatDenominator, otherConstantDenominator);
1701
1702 if (impl.singleUnits.length() > 1 || impl.constantDenominator > 0) {
1703 impl.complexity = UMEASURE_UNIT_COMPOUND;
1704 }
1705
1706 return std::move(impl).build(status);
1707}
1708
1709LocalArray<MeasureUnit> MeasureUnit::splitToSingleUnitsImpl(int32_t& outCount, UErrorCode& status) const {
1710 MeasureUnitImpl temp;
1711 const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(*this, temp, status);
1712 outCount = impl.singleUnits.length();
1713 MeasureUnit* arr = new MeasureUnit[outCount];
1714 if (arr == nullptr) {
1715 status = U_MEMORY_ALLOCATION_ERROR;
1716 return LocalArray<MeasureUnit>();
1717 }
1718 for (int32_t i = 0; i < outCount; i++) {
1719 arr[i] = impl.singleUnits[i]->build(status);
1720 }
1721 return LocalArray<MeasureUnit>(arr, status);
1722}
1723
1724
1725U_NAMESPACE_END}
1726
1727#endif /* !UNCONFIG_NO_FORMATTING */