1 | /* |
2 | * Copyright © 2018 Google, Inc. |
3 | * |
4 | * This is part of HarfBuzz, a text shaping library. |
5 | * |
6 | * Permission is hereby granted, without written agreement and without |
7 | * license or royalty fees, to use, copy, modify, and distribute this |
8 | * software and its documentation for any purpose, provided that the |
9 | * above copyright notice and the following two paragraphs appear in |
10 | * all copies of this software. |
11 | * |
12 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | * DAMAGE. |
17 | * |
18 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | * |
24 | * Google Author(s): Behdad Esfahbod |
25 | */ |
26 | |
27 | #ifndef HB_ARRAY_HH |
28 | #define HB_ARRAY_HH |
29 | |
30 | #include "hb.hh" |
31 | #include "hb-dsalgs.hh" |
32 | #include "hb-iter.hh" |
33 | #include "hb-null.hh" |
34 | |
35 | |
36 | template <typename Type> |
37 | struct hb_sorted_array_t; |
38 | |
39 | template <typename Type> |
40 | struct hb_array_t : |
41 | hb_iter_t<hb_array_t<Type>, Type>, |
42 | hb_iter_mixin_t<hb_array_t<Type>, Type> |
43 | { |
44 | /* |
45 | * Constructors. |
46 | */ |
47 | hb_array_t () : arrayZ (nullptr), length (0) {} |
48 | hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {} |
49 | template <unsigned int length_> hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_) {} |
50 | |
51 | |
52 | /* |
53 | * Iterator implementation. |
54 | */ |
55 | typedef Type __item_type__; |
56 | Type& __item_at__ (unsigned i) const |
57 | { |
58 | if (unlikely (i >= length)) return CrapOrNull (Type); |
59 | return arrayZ[i]; |
60 | } |
61 | void __forward__ (unsigned n) |
62 | { |
63 | if (unlikely (n > length)) |
64 | n = length; |
65 | length -= n; |
66 | arrayZ += n; |
67 | } |
68 | void __rewind__ (unsigned n) |
69 | { |
70 | if (unlikely (n > length)) |
71 | n = length; |
72 | length -= n; |
73 | } |
74 | unsigned __len__ () const { return length; } |
75 | bool __random_access__ () const { return true; } |
76 | |
77 | /* Extra operators. |
78 | */ |
79 | Type * operator & () const { return arrayZ; } |
80 | operator hb_array_t<const Type> () { return hb_array_t<const Type> (arrayZ, length); } |
81 | template <typename T> operator T * () const { return arrayZ; } |
82 | |
83 | /* |
84 | * Compare, Sort, and Search. |
85 | */ |
86 | |
87 | /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */ |
88 | int cmp (const hb_array_t<Type> &a) const |
89 | { |
90 | if (length != a.length) |
91 | return (int) a.length - (int) length; |
92 | return hb_memcmp (a.arrayZ, arrayZ, get_size ()); |
93 | } |
94 | static int cmp (const void *pa, const void *pb) |
95 | { |
96 | hb_array_t<Type> *a = (hb_array_t<Type> *) pa; |
97 | hb_array_t<Type> *b = (hb_array_t<Type> *) pb; |
98 | return b->cmp (*a); |
99 | } |
100 | |
101 | template <typename T> |
102 | Type *lsearch (const T &x, Type *not_found = nullptr) |
103 | { |
104 | unsigned int count = length; |
105 | for (unsigned int i = 0; i < count; i++) |
106 | if (!this->arrayZ[i].cmp (x)) |
107 | return &this->arrayZ[i]; |
108 | return not_found; |
109 | } |
110 | template <typename T> |
111 | const Type *lsearch (const T &x, const Type *not_found = nullptr) const |
112 | { |
113 | unsigned int count = length; |
114 | for (unsigned int i = 0; i < count; i++) |
115 | if (!this->arrayZ[i].cmp (x)) |
116 | return &this->arrayZ[i]; |
117 | return not_found; |
118 | } |
119 | |
120 | hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*)) |
121 | { |
122 | if (likely (length)) |
123 | ::qsort (arrayZ, length, this->item_size, cmp_); |
124 | return hb_sorted_array_t<Type> (*this); |
125 | } |
126 | hb_sorted_array_t<Type> qsort () |
127 | { |
128 | if (likely (length)) |
129 | ::qsort (arrayZ, length, this->item_size, Type::cmp); |
130 | return hb_sorted_array_t<Type> (*this); |
131 | } |
132 | void qsort (unsigned int start, unsigned int end) |
133 | { |
134 | end = MIN (end, length); |
135 | assert (start <= end); |
136 | if (likely (start < end)) |
137 | ::qsort (arrayZ + start, end - start, this->item_size, Type::cmp); |
138 | } |
139 | |
140 | /* |
141 | * Other methods. |
142 | */ |
143 | |
144 | unsigned int get_size () const { return length * this->item_size; } |
145 | |
146 | hb_array_t<Type> sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const |
147 | { |
148 | if (!start_offset && !seg_count) |
149 | return *this; |
150 | |
151 | unsigned int count = length; |
152 | if (unlikely (start_offset > count)) |
153 | count = 0; |
154 | else |
155 | count -= start_offset; |
156 | if (seg_count) |
157 | count = *seg_count = MIN (count, *seg_count); |
158 | return hb_array_t<Type> (arrayZ + start_offset, count); |
159 | } |
160 | hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const |
161 | { return sub_array (start_offset, &seg_count); } |
162 | |
163 | /* Only call if you allocated the underlying array using malloc() or similar. */ |
164 | void free () |
165 | { ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; } |
166 | |
167 | template <typename hb_sanitize_context_t> |
168 | bool sanitize (hb_sanitize_context_t *c) const |
169 | { return c->check_array (arrayZ, length); } |
170 | |
171 | /* |
172 | * Members |
173 | */ |
174 | |
175 | public: |
176 | Type *arrayZ; |
177 | unsigned int length; |
178 | }; |
179 | template <typename T> inline hb_array_t<T> |
180 | hb_array (T *array, unsigned int length) |
181 | { return hb_array_t<T> (array, length); } |
182 | template <typename T, unsigned int length_> inline hb_array_t<T> |
183 | hb_array (T (&array_)[length_]) |
184 | { return hb_array_t<T> (array_); } |
185 | |
186 | |
187 | enum hb_bfind_not_found_t |
188 | { |
189 | HB_BFIND_NOT_FOUND_DONT_STORE, |
190 | HB_BFIND_NOT_FOUND_STORE, |
191 | HB_BFIND_NOT_FOUND_STORE_CLOSEST, |
192 | }; |
193 | |
194 | template <typename Type> |
195 | struct hb_sorted_array_t : |
196 | hb_sorted_iter_t<hb_sorted_array_t<Type>, Type>, |
197 | hb_array_t<Type>, |
198 | hb_iter_mixin_t<hb_sorted_array_t<Type>, Type> |
199 | { |
200 | hb_sorted_array_t () : hb_array_t<Type> () {} |
201 | hb_sorted_array_t (const hb_array_t<Type> &o) : hb_array_t<Type> (o) {} |
202 | hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {} |
203 | template <unsigned int length_> hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {} |
204 | |
205 | hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const |
206 | { return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); } |
207 | hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const |
208 | { return sub_array (start_offset, &seg_count); } |
209 | |
210 | template <typename T> |
211 | Type *bsearch (const T &x, Type *not_found = nullptr) |
212 | { |
213 | unsigned int i; |
214 | return bfind (x, &i) ? &this->arrayZ[i] : not_found; |
215 | } |
216 | template <typename T> |
217 | const Type *bsearch (const T &x, const Type *not_found = nullptr) const |
218 | { |
219 | unsigned int i; |
220 | return bfind (x, &i) ? &this->arrayZ[i] : not_found; |
221 | } |
222 | template <typename T> |
223 | bool bfind (const T &x, unsigned int *i = nullptr, |
224 | hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, |
225 | unsigned int to_store = (unsigned int) -1) const |
226 | { |
227 | int min = 0, max = (int) this->length - 1; |
228 | const Type *array = this->arrayZ; |
229 | while (min <= max) |
230 | { |
231 | int mid = ((unsigned int) min + (unsigned int) max) / 2; |
232 | int c = array[mid].cmp (x); |
233 | if (c < 0) |
234 | max = mid - 1; |
235 | else if (c > 0) |
236 | min = mid + 1; |
237 | else |
238 | { |
239 | if (i) |
240 | *i = mid; |
241 | return true; |
242 | } |
243 | } |
244 | if (i) |
245 | { |
246 | switch (not_found) |
247 | { |
248 | case HB_BFIND_NOT_FOUND_DONT_STORE: |
249 | break; |
250 | |
251 | case HB_BFIND_NOT_FOUND_STORE: |
252 | *i = to_store; |
253 | break; |
254 | |
255 | case HB_BFIND_NOT_FOUND_STORE_CLOSEST: |
256 | if (max < 0 || (max < (int) this->length && array[max].cmp (x) > 0)) |
257 | max++; |
258 | *i = max; |
259 | break; |
260 | } |
261 | } |
262 | return false; |
263 | } |
264 | }; |
265 | template <typename T> inline hb_sorted_array_t<T> |
266 | hb_sorted_array (T *array, unsigned int length) |
267 | { return hb_sorted_array_t<T> (array, length); } |
268 | template <typename T, unsigned int length_> inline hb_sorted_array_t<T> |
269 | hb_sorted_array (T (&array_)[length_]) |
270 | { return hb_sorted_array_t<T> (array_); } |
271 | |
272 | |
273 | typedef hb_array_t<const char> hb_bytes_t; |
274 | typedef hb_array_t<const unsigned char> hb_ubytes_t; |
275 | |
276 | |
277 | #endif /* HB_ARRAY_HH */ |
278 | |