1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma clang diagnostic push
18 #pragma clang diagnostic ignored "-Wunused-parameter"
19 #pragma clang diagnostic ignored "-Wunused-variable"
20 #pragma clang diagnostic ignored "-Wunused-value"
21 
22 #define C2_LOG_VERBOSE
23 
24 #include <C2Debug.h>
25 #include <C2Param.h>
26 #include <C2ParamDef.h>
27 #include <C2ParamInternal.h>
28 #include <util/C2InterfaceUtils.h>
29 
30 #include <cmath>
31 #include <limits>
32 #include <map>
33 #include <type_traits>
34 
35 #include <android-base/stringprintf.h>
36 
37 std::ostream& operator<<(std::ostream& os, const _C2FieldId &i);
38 
39 std::ostream& operator<<(std::ostream& os, const C2ParamField &i);
40 
41 /* ---------------------------- C2SupportedRange ---------------------------- */
42 
43 /**
44  * Helper class for supported values range calculations.
45  */
46 template<typename T, bool FP=std::is_floating_point<T>::value>
47 struct _C2TypedSupportedRangeHelper {
48     /**
49      * type of range size: a - b if a >= b and a and b are of type T
50      */
51     typedef typename std::make_unsigned<T>::type DiffType;
52 
53     /**
54      * calculate (high - low) mod step
55      */
mod_C2TypedSupportedRangeHelper56     static DiffType mod(T low, T high, T step) {
57         return DiffType(high - low) % DiffType(step);
58     }
59 };
60 
61 template<typename T>
62 struct _C2TypedSupportedRangeHelper<T, true> {
63     typedef T DiffType;
64 
mod_C2TypedSupportedRangeHelper65     static DiffType mod(T low, T high, T step) {
66         return fmod(high - low, step);
67     }
68 };
69 
70 template<typename T>
C2SupportedRange(const C2FieldSupportedValues & values)71 C2SupportedRange<T>::C2SupportedRange(const C2FieldSupportedValues &values) {
72     if (values.type == C2FieldSupportedValues::RANGE) {
73         _mMin = values.range.min.ref<ValueType>();
74         _mMax = values.range.max.ref<ValueType>();
75         _mStep = values.range.step.ref<ValueType>();
76         _mNum = values.range.num.ref<ValueType>();
77         _mDenom = values.range.denom.ref<ValueType>();
78     } else {
79         _mMin = MAX_VALUE;
80         _mMax = MIN_VALUE;
81         _mStep = MIN_STEP;
82         _mNum = 0;
83         _mDenom = 0;
84     }
85 }
86 
87 template<typename T>
contains(T value) const88 bool C2SupportedRange<T>::contains(T value) const {
89     // value must fall between min and max
90     if (value < _mMin || value > _mMax) {
91         return false;
92     }
93     // simple ranges contain all values between min and max
94     if (isSimpleRange()) {
95         return true;
96     }
97     // min is always part of the range
98     if (value == _mMin) {
99         return true;
100     }
101     // stepped ranges require (val - min) % step to be zero
102     if (isArithmeticSeries()) {
103         return _C2TypedSupportedRangeHelper<T>::mod(_mMin, value, _mStep) == 0;
104     }
105     // pure geometric series require (val / min) to be integer multiple of (num/denom)
106     if (isGeometricSeries()) {
107         if (value <= 0) {
108             return false;
109         }
110         double log2base = log2(_mNum / _mDenom);
111         double power = llround(log2(value / double(_mMin)) / log2base);
112         // TODO: validate that result falls within precision (other than round)
113         return value == T(_mMin * pow(_mNum / _mDenom, power) + MIN_STEP / 2);
114     }
115     // multiply-accumulate series require validating by walking through the series
116     if (isMacSeries()) {
117         double lastValue = _mMin;
118         double base = _mNum / _mDenom;
119         while (true) {
120             // this cast is safe as _mMin <= lastValue <= _mMax
121             if (T(lastValue + MIN_STEP / 2) == value) {
122                 return true;
123             }
124             double nextValue = fma(lastValue, base, _mStep);
125             if (nextValue <= lastValue || nextValue > _mMax) {
126                 return false; // series is no longer monotonic or within range
127             }
128             lastValue = nextValue;
129         };
130     }
131     // if we are here, this must be an invalid range
132     return false;
133 }
134 
135 template<typename T>
limitedTo(const C2SupportedRange<T> & limit) const136 C2SupportedRange<T> C2SupportedRange<T>::limitedTo(const C2SupportedRange<T> &limit) const {
137     // TODO - this only works for simple ranges
138     return C2SupportedRange(std::max(_mMin, limit._mMin), std::min(_mMax, limit._mMax),
139                                  std::max(_mStep, limit._mStep));
140 }
141 
142 template class C2SupportedRange<uint8_t>;
143 template class C2SupportedRange<char>;
144 template class C2SupportedRange<int32_t>;
145 template class C2SupportedRange<uint32_t>;
146 //template class C2SupportedRange<c2_cntr32_t>;
147 template class C2SupportedRange<int64_t>;
148 template class C2SupportedRange<uint64_t>;
149 //template class C2SupportedRange<c2_cntr64_t>;
150 template class C2SupportedRange<float>;
151 
152 /* -------------------------- C2SupportedFlags -------------------------- */
153 
154 /**
155  * Ordered supported flag set for a field of a given type.
156  */
157 // float flags are not supported, but define a few methods to support generic supported values code
158 template<>
contains(float value) const159 bool C2SupportedFlags<float>::contains(float value) const {
160     return false;
161 }
162 
163 template<>
flags() const164 const std::vector<float> C2SupportedFlags<float>::flags() const {
165     return std::vector<float>();
166 }
167 
168 template<>
limitedTo(const C2SupportedFlags<float> & limit) const169 C2SupportedFlags<float> C2SupportedFlags<float>::limitedTo(const C2SupportedFlags<float> &limit) const {
170     std::vector<C2Value::Primitive> values;
171     return C2SupportedFlags(std::move(values));
172 }
173 
174 template<>
min() const175 float C2SupportedFlags<float>::min() const {
176     return 0;
177 }
178 
179 template<typename T>
contains(T value) const180 bool C2SupportedFlags<T>::contains(T value) const {
181     // value must contain the minimal mask
182     T minMask = min();
183     if (~value & minMask) {
184         return false;
185     }
186     value &= ~minMask;
187     // otherwise, remove flags from value and see if we arrive at 0
188     for (const C2Value::Primitive &v : _mValues) {
189         if (value == 0) {
190             break;
191         }
192         if ((~value & v.ref<ValueType>()) == 0) {
193             value &= ~v.ref<ValueType>();
194         }
195     }
196     return value == 0;
197 }
198 
199 template<typename T>
flags() const200 const std::vector<T> C2SupportedFlags<T>::flags() const {
201     std::vector<T> vals(c2_max(_mValues.size(), 1u) - 1);
202     if (!_mValues.empty()) {
203         std::transform(_mValues.cbegin() + 1, _mValues.cend(), vals.begin(),
204                        [](const C2Value::Primitive &p)->T {
205             return p.ref<ValueType>();
206         });
207     }
208     return vals;
209 }
210 
211 template<typename T>
limitedTo(const C2SupportedFlags<T> & limit) const212 C2SupportedFlags<T> C2SupportedFlags<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
213     std::vector<C2Value::Primitive> values = _mValues; // make a copy
214     T minMask = min() | limit.min();
215     // minimum mask must be covered by both this and other
216     if (limit.contains(minMask) && contains(minMask)) {
217         values[0] = minMask;
218         // keep only flags that are covered by limit
219         std::remove_if(values.begin(), values.end(), [&limit, minMask](const C2Value::Primitive &v) -> bool {
220             T value = v.ref<ValueType>() | minMask;
221             return value == minMask || !limit.contains(value); });
222         // we also need to do it vice versa
223         for (const C2Value::Primitive &v : _mValues) {
224             T value = v.ref<ValueType>() | minMask;
225             if (value != minMask && contains(value)) {
226                 values.emplace_back((ValueType)value);
227             }
228         }
229     }
230     return C2SupportedFlags(std::move(values));
231 }
232 
233 template<typename T>
min() const234 T C2SupportedFlags<T>::min() const {
235     if (!_mValues.empty()) {
236         return _mValues.front().template ref<ValueType>();
237     } else {
238         return T(0);
239     }
240 }
241 
242 template class C2SupportedFlags<uint8_t>;
243 template class C2SupportedFlags<char>;
244 template class C2SupportedFlags<int32_t>;
245 template class C2SupportedFlags<uint32_t>;
246 //template class C2SupportedFlags<c2_cntr32_t>;
247 template class C2SupportedFlags<int64_t>;
248 template class C2SupportedFlags<uint64_t>;
249 //template class C2SupportedFlags<c2_cntr64_t>;
250 
251 /* -------------------------- C2SupportedValueSet -------------------------- */
252 
253 /**
254  * Ordered supported value set for a field of a given type.
255  */
256 template<typename T>
contains(T value) const257 bool C2SupportedValueSet<T>::contains(T value) const {
258     return std::find_if(_mValues.cbegin(), _mValues.cend(),
259             [value](const C2Value::Primitive &p) -> bool {
260                 return value == p.ref<ValueType>();
261             }) != _mValues.cend();
262 }
263 
264 template<typename T>
limitedTo(const C2SupportedValueSet<T> & limit) const265 C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedValueSet<T> &limit) const {
266     std::vector<C2Value::Primitive> values = _mValues; // make a copy
267     std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
268         return !limit.contains(v.ref<ValueType>()); });
269     return C2SupportedValueSet(std::move(values));
270 }
271 
272 template<typename T>
limitedTo(const C2SupportedRange<T> & limit) const273 C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedRange<T> &limit) const {
274     std::vector<C2Value::Primitive> values = _mValues; // make a copy
275     std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
276         return !limit.contains(v.ref<ValueType>()); });
277     return C2SupportedValueSet(std::move(values));
278 }
279 
280 template<typename T>
limitedTo(const C2SupportedFlags<T> & limit) const281 C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
282     std::vector<C2Value::Primitive> values = _mValues; // make a copy
283     std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
284         return !limit.contains(v.ref<ValueType>()); });
285     return C2SupportedValueSet(std::move(values));
286 }
287 
288 template<typename T>
values() const289 const std::vector<T> C2SupportedValueSet<T>::values() const {
290     std::vector<T> vals(_mValues.size());
291     std::transform(_mValues.cbegin(), _mValues.cend(), vals.begin(), [](const C2Value::Primitive &p) -> T {
292         return p.ref<ValueType>();
293     });
294     return vals;
295 }
296 
297 template class C2SupportedValueSet<uint8_t>;
298 template class C2SupportedValueSet<char>;
299 template class C2SupportedValueSet<int32_t>;
300 template class C2SupportedValueSet<uint32_t>;
301 //template class C2SupportedValueSet<c2_cntr32_t>;
302 template class C2SupportedValueSet<int64_t>;
303 template class C2SupportedValueSet<uint64_t>;
304 //template class C2SupportedValueSet<c2_cntr64_t>;
305 template class C2SupportedValueSet<float>;
306 
307 /* ---------------------- C2FieldSupportedValuesHelper ---------------------- */
308 
309 template<typename T>
310 struct C2FieldSupportedValuesHelper<T>::Impl {
ImplC2FieldSupportedValuesHelper::Impl311     Impl(const C2FieldSupportedValues &values)
312         : _mType(values.type),
313           _mRange(values),
314           _mValues(values),
315           _mFlags(values) { }
316 
317     bool supports(T value) const;
318 
319 private:
320     typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
321     C2FieldSupportedValues::type_t _mType;
322     C2SupportedRange<ValueType> _mRange;
323     C2SupportedValueSet<ValueType> _mValues;
324     C2SupportedValueSet<ValueType> _mFlags;
325 
326 //    friend std::ostream& operator<< <T>(std::ostream& os, const C2FieldSupportedValuesHelper<T>::Impl &i);
327 //    friend std::ostream& operator<<(std::ostream& os, const Impl &i);
328     std::ostream& streamOut(std::ostream& os) const;
329 };
330 
331 template<typename T>
supports(T value) const332 bool C2FieldSupportedValuesHelper<T>::Impl::supports(T value) const {
333     switch (_mType) {
334         case C2FieldSupportedValues::RANGE: return _mRange.contains(value);
335         case C2FieldSupportedValues::VALUES: return _mValues.contains(value);
336         case C2FieldSupportedValues::FLAGS: return _mFlags.contains(value);
337         default: return false;
338     }
339 }
340 
341 template<typename T>
C2FieldSupportedValuesHelper(const C2FieldSupportedValues & values)342 C2FieldSupportedValuesHelper<T>::C2FieldSupportedValuesHelper(const C2FieldSupportedValues &values)
343     : _mImpl(std::make_unique<C2FieldSupportedValuesHelper<T>::Impl>(values)) { }
344 
345 template<typename T>
346 C2FieldSupportedValuesHelper<T>::~C2FieldSupportedValuesHelper() = default;
347 
348 template<typename T>
supports(T value) const349 bool C2FieldSupportedValuesHelper<T>::supports(T value) const {
350     return _mImpl->supports(value);
351 }
352 
353 template class C2FieldSupportedValuesHelper<uint8_t>;
354 template class C2FieldSupportedValuesHelper<char>;
355 template class C2FieldSupportedValuesHelper<int32_t>;
356 template class C2FieldSupportedValuesHelper<uint32_t>;
357 //template class C2FieldSupportedValuesHelper<c2_cntr32_t>;
358 template class C2FieldSupportedValuesHelper<int64_t>;
359 template class C2FieldSupportedValuesHelper<uint64_t>;
360 //template class C2FieldSupportedValuesHelper<c2_cntr64_t>;
361 template class C2FieldSupportedValuesHelper<float>;
362 
363 /* ----------------------- C2ParamFieldValuesBuilder ----------------------- */
364 
365 template<typename T>
366 struct C2ParamFieldValuesBuilder<T>::Impl {
ImplC2ParamFieldValuesBuilder::Impl367     Impl(const C2ParamField &field)
368         : _mParamField(field),
369           _mType(type_t::RANGE),
370           _mDefined(false),
371           _mRange(C2SupportedRange<T>::Any()),
372           _mValues(C2SupportedValueSet<T>::None()),
373           _mFlags(C2SupportedFlags<T>::None()) { }
374 
375     /**
376      * Get C2ParamFieldValues from this builder.
377      */
operator C2ParamFieldValuesC2ParamFieldValuesBuilder::Impl378     operator C2ParamFieldValues() const {
379         if (!_mDefined) {
380             return C2ParamFieldValues(_mParamField);
381         }
382         switch (_mType) {
383         case type_t::EMPTY:
384         case type_t::VALUES:
385             return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mValues);
386         case type_t::RANGE:
387             return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mRange);
388         case type_t::FLAGS:
389             return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mFlags);
390         default:
391             // TRESPASS
392             // should never get here
393             return C2ParamFieldValues(_mParamField);
394         }
395     }
396 
397     /** Define the supported values as the currently supported values of this builder. */
anyC2ParamFieldValuesBuilder::Impl398     void any() {
399         _mDefined = true;
400     }
401 
402     /** Restrict (and thus define) the supported values to none. */
noneC2ParamFieldValuesBuilder::Impl403     void none() {
404         _mDefined = true;
405         _mType = type_t::VALUES;
406         _mValues.clear();
407     }
408 
409     /** Restrict (and thus define) the supported values to |value| alone. */
equalToC2ParamFieldValuesBuilder::Impl410     void equalTo(T value) {
411          return limitTo(C2SupportedValueSet<T>::OneOf({value}));
412     }
413 
414     /** Restrict (and thus define) the supported values to a value set. */
limitToC2ParamFieldValuesBuilder::Impl415     void limitTo(const C2SupportedValueSet<T> &limit) {
416         if (!_mDefined) {
417             C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
418 
419             // shortcut for first limit applied
420             _mDefined = true;
421             _mValues = limit;
422             _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
423         } else {
424             switch (_mType) {
425             case type_t::EMPTY:
426             case type_t::VALUES:
427                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
428                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
429 
430                 _mValues = _mValues.limitedTo(limit);
431                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
432                 break;
433             case type_t::RANGE:
434                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
435                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
436 
437                 _mValues = limit.limitedTo(_mRange);
438                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
439                 break;
440             case type_t::FLAGS:
441                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
442                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
443 
444                 _mValues = limit.limitedTo(_mFlags);
445                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
446                 break;
447             default:
448                 C2_LOG(FATAL); // should not be here
449             }
450             // TODO: support flags
451         }
452         C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
453     }
454 
455     /** Restrict (and thus define) the supported values to a flag set. */
limitToC2ParamFieldValuesBuilder::Impl456     void limitTo(const C2SupportedFlags<T> &limit) {
457         if (!_mDefined) {
458             C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
459 
460             // shortcut for first limit applied
461             _mDefined = true;
462             _mFlags = limit;
463             _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS;
464         } else {
465             switch (_mType) {
466             case type_t::EMPTY:
467             case type_t::VALUES:
468                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
469                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
470 
471                 _mValues = _mValues.limitedTo(limit);
472                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
473                 C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
474                 break;
475             case type_t::FLAGS:
476                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mFlags) << ").limitTo("
477                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
478 
479                 _mFlags = _mFlags.limitedTo(limit);
480                 _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS;
481                 C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mFlags);
482                 break;
483             case type_t::RANGE:
484                 C2_LOG(FATAL) << "limiting ranges to flags is not supported";
485                 _mType = type_t::EMPTY;
486                 break;
487             default:
488                 C2_LOG(FATAL); // should not be here
489             }
490         }
491     }
492 
limitToC2ParamFieldValuesBuilder::Impl493     void limitTo(const C2SupportedRange<T> &limit) {
494         if (!_mDefined) {
495             C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
496 
497             // shortcut for first limit applied
498             _mDefined = true;
499             _mRange = limit;
500             _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE;
501             C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange);
502         } else {
503             switch (_mType) {
504             case type_t::EMPTY:
505             case type_t::VALUES:
506                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
507                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
508                 _mValues = _mValues.limitedTo(limit);
509                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
510                 C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
511                 break;
512             case type_t::FLAGS:
513                 C2_LOG(FATAL) << "limiting flags to ranges is not supported";
514                 _mType = type_t::EMPTY;
515                 break;
516             case type_t::RANGE:
517                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
518                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
519                 _mRange = _mRange.limitedTo(limit);
520                 C2_DCHECK(_mValues.isEmpty());
521                 _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE;
522                 C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange);
523                 break;
524             default:
525                 C2_LOG(FATAL); // should not be here
526             }
527         }
528     }
529 
530 private:
instantiateC2ParamFieldValuesBuilder::Impl531     void instantiate() __unused {
532         (void)_mValues.values(); // instantiate non-const values()
533     }
534 
instantiateC2ParamFieldValuesBuilder::Impl535     void instantiate() const __unused {
536         (void)_mValues.values(); // instantiate const values()
537     }
538 
539     typedef C2FieldSupportedValues::type_t type_t;
540 
541     C2ParamField _mParamField;
542     type_t _mType;
543     bool _mDefined;
544     C2SupportedRange<T> _mRange;
545     C2SupportedValueSet<T> _mValues;
546     C2SupportedFlags<T> _mFlags;
547 
548 };
549 
550 template<typename T>
operator C2ParamFieldValues() const551 C2ParamFieldValuesBuilder<T>::operator C2ParamFieldValues() const {
552     return (C2ParamFieldValues)(*_mImpl.get());
553 }
554 
555 template<typename T>
C2ParamFieldValuesBuilder(const C2ParamField & field)556 C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamField &field)
557     : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(field)) { }
558 
559 template<typename T>
any()560 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::any() {
561     _mImpl->any();
562     return *this;
563 }
564 
565 template<typename T>
none()566 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::none() {
567     _mImpl->none();
568     return *this;
569 }
570 
571 template<typename T>
equalTo(T value)572 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::equalTo(T value) {
573     _mImpl->equalTo(value);
574     return *this;
575 }
576 
577 template<typename T>
limitTo(const C2SupportedValueSet<T> & limit)578 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedValueSet<T> &limit) {
579     _mImpl->limitTo(limit);
580     return *this;
581 }
582 
583 template<typename T>
limitTo(const C2SupportedFlags<T> & limit)584 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedFlags<T> &limit) {
585     _mImpl->limitTo(limit);
586     return *this;
587 }
588 
589 template<typename T>
limitTo(const C2SupportedRange<T> & limit)590 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedRange<T> &limit) {
591     _mImpl->limitTo(limit);
592     return *this;
593 }
594 
595 template<typename T>
C2ParamFieldValuesBuilder(const C2ParamFieldValuesBuilder<T> & other)596 C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamFieldValuesBuilder<T> &other)
597     : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get())) { }
598 
599 template<typename T>
operator =(const C2ParamFieldValuesBuilder<T> & other)600 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::operator=(
601         const C2ParamFieldValuesBuilder<T> &other) {
602     _mImpl = std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get());
603     return *this;
604 }
605 
606 template<typename T>
607 C2ParamFieldValuesBuilder<T>::~C2ParamFieldValuesBuilder() = default;
608 
609 template class C2ParamFieldValuesBuilder<uint8_t>;
610 template class C2ParamFieldValuesBuilder<char>;
611 template class C2ParamFieldValuesBuilder<int32_t>;
612 template class C2ParamFieldValuesBuilder<uint32_t>;
613 //template class C2ParamFieldValuesBuilder<c2_cntr32_t>;
614 template class C2ParamFieldValuesBuilder<int64_t>;
615 template class C2ParamFieldValuesBuilder<uint64_t>;
616 //template class C2ParamFieldValuesBuilder<c2_cntr64_t>;
617 template class C2ParamFieldValuesBuilder<float>;
618 
619 /* ------------------------- C2SettingResultBuilder ------------------------- */
620 
C2SettingConflictsBuilder()621 C2SettingConflictsBuilder::C2SettingConflictsBuilder() : _mConflicts() { }
622 
C2SettingConflictsBuilder(C2ParamFieldValues && conflict)623 C2SettingConflictsBuilder::C2SettingConflictsBuilder(C2ParamFieldValues &&conflict) {
624     _mConflicts.emplace_back(std::move(conflict));
625 }
626 
with(C2ParamFieldValues && conflict)627 C2SettingConflictsBuilder& C2SettingConflictsBuilder::with(C2ParamFieldValues &&conflict) {
628     _mConflicts.emplace_back(std::move(conflict));
629     return *this;
630 }
631 
retrieveConflicts()632 std::vector<C2ParamFieldValues> C2SettingConflictsBuilder::retrieveConflicts() {
633     return std::move(_mConflicts);
634 }
635 
636 /* ------------------------- C2SettingResult/sBuilder ------------------------- */
637 
ReadOnly(const C2ParamField & param)638 C2SettingResult C2SettingResultBuilder::ReadOnly(const C2ParamField &param) {
639     return C2SettingResult { C2SettingResult::READ_ONLY, { param }, { } };
640 }
641 
BadValue(const C2ParamField & paramField,bool isInfo)642 C2SettingResult C2SettingResultBuilder::BadValue(const C2ParamField &paramField, bool isInfo) {
643     return { isInfo ? C2SettingResult::INFO_BAD_VALUE : C2SettingResult::BAD_VALUE,
644              { paramField }, { } };
645 }
646 
Conflict(C2ParamFieldValues && paramFieldValues,C2SettingConflictsBuilder & conflicts,bool isInfo)647 C2SettingResult C2SettingResultBuilder::Conflict(
648         C2ParamFieldValues &&paramFieldValues, C2SettingConflictsBuilder &conflicts, bool isInfo) {
649     C2_CHECK(!conflicts.empty());
650     if (isInfo) {
651         return C2SettingResult {
652             C2SettingResult::INFO_CONFLICT,
653             std::move(paramFieldValues), conflicts.retrieveConflicts()
654         };
655     } else {
656         return C2SettingResult {
657             C2SettingResult::CONFLICT,
658             std::move(paramFieldValues), conflicts.retrieveConflicts()
659         };
660     }
661 }
662 
C2SettingResultsBuilder(C2SettingResult && result)663 C2SettingResultsBuilder::C2SettingResultsBuilder(C2SettingResult &&result)
664         : _mStatus(C2_BAD_VALUE) {
665     _mResults.emplace_back(new C2SettingResult(std::move(result)));
666 }
667 
plus(C2SettingResultsBuilder && results)668 C2SettingResultsBuilder C2SettingResultsBuilder::plus(C2SettingResultsBuilder&& results) {
669     for (std::unique_ptr<C2SettingResult> &r : results._mResults) {
670         _mResults.emplace_back(std::move(r));
671     }
672     results._mResults.clear();
673     // TODO: mStatus
674     return std::move(*this);
675 }
676 
retrieveFailures(std::vector<std::unique_ptr<C2SettingResult>> * const failures)677 c2_status_t C2SettingResultsBuilder::retrieveFailures(
678         std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
679     for (std::unique_ptr<C2SettingResult> &r : _mResults) {
680         failures->emplace_back(std::move(r));
681     }
682     _mResults.clear();
683     return _mStatus;
684 }
685 
C2SettingResultsBuilder(c2_status_t status)686 C2SettingResultsBuilder::C2SettingResultsBuilder(c2_status_t status) : _mStatus(status) {
687     // status must be one of OK, BAD_STATE, TIMED_OUT or CORRUPTED
688     // mainly: BLOCKING, BAD_INDEX, BAD_VALUE and NO_MEMORY requires a setting attempt
689 }
690 
691 #pragma clang diagnostic pop
692 
693 /* ------------------------- C2FieldUtils ------------------------- */
694 
695 struct C2_HIDE C2FieldUtils::_Inspector {
696     /// returns the implementation object
GetImplC2FieldUtils::_Inspector697     inline static std::shared_ptr<Info::Impl> GetImpl(const Info &info) {
698         return info._mImpl;
699     }
700 };
701 
702 /* ------------------------- C2FieldUtils::Info ------------------------- */
703 
704 struct C2_HIDE C2FieldUtils::Info::Impl {
705     C2FieldDescriptor field;
706     std::shared_ptr<Impl> parent;
707     uint32_t index;
708     uint32_t depth;
709     uint32_t baseFieldOffset;
710     uint32_t arrayOffset;
711     uint32_t usedExtent;
712 
713     /// creates a copy of this object including copies of its parent chain
714     Impl clone() const;
715 
716     /// creates a copy of a shared pointer to an object
717     static std::shared_ptr<Impl> Clone(const std::shared_ptr<Impl> &);
718 
ImplC2FieldUtils::Info::Impl719     Impl(const C2FieldDescriptor &field_, std::shared_ptr<Impl> parent_,
720             uint32_t index_, uint32_t depth_, uint32_t baseFieldOffset_,
721             uint32_t arrayOffset_, uint32_t usedExtent_)
722         : field(field_), parent(parent_), index(index_), depth(depth_),
723           baseFieldOffset(baseFieldOffset_), arrayOffset(arrayOffset_), usedExtent(usedExtent_) { }
724 };
725 
Clone(const std::shared_ptr<Impl> & info)726 std::shared_ptr<C2FieldUtils::Info::Impl> C2FieldUtils::Info::Impl::Clone(const std::shared_ptr<Impl> &info) {
727     if (info) {
728         return std::make_shared<Impl>(info->clone());
729     }
730     return nullptr;
731 }
732 
clone() const733 C2FieldUtils::Info::Impl C2FieldUtils::Info::Impl::clone() const {
734     Impl res = Impl(*this);
735     res.parent = Clone(res.parent);
736     return res;
737 }
738 
Info(std::shared_ptr<Impl> impl)739 C2FieldUtils::Info::Info(std::shared_ptr<Impl> impl)
740     : _mImpl(impl) { }
741 
arrayOffset() const742 size_t C2FieldUtils::Info::arrayOffset() const {
743     return _mImpl->arrayOffset;
744 }
745 
arraySize() const746 size_t C2FieldUtils::Info::arraySize() const {
747     return extent() * size();
748 }
749 
baseFieldOffset() const750 size_t C2FieldUtils::Info::baseFieldOffset() const {
751     return _mImpl->baseFieldOffset;
752 };
753 
depth() const754 size_t C2FieldUtils::Info::depth() const {
755     return _mImpl->depth;
756 }
757 
extent() const758 size_t C2FieldUtils::Info::extent() const {
759     return _mImpl->usedExtent;
760 }
761 
index() const762 size_t C2FieldUtils::Info::index() const {
763     return _mImpl->index;
764 }
765 
isArithmetic() const766 bool C2FieldUtils::Info::isArithmetic() const {
767     switch (_mImpl->field.type()) {
768     case C2FieldDescriptor::BLOB:
769     case C2FieldDescriptor::CNTR32:
770     case C2FieldDescriptor::CNTR64:
771     case C2FieldDescriptor::FLOAT:
772     case C2FieldDescriptor::INT32:
773     case C2FieldDescriptor::INT64:
774     case C2FieldDescriptor::STRING:
775     case C2FieldDescriptor::UINT32:
776     case C2FieldDescriptor::UINT64:
777         return true;
778     default:
779         return false;
780     }
781 }
782 
isFlexible() const783 bool C2FieldUtils::Info::isFlexible() const {
784     return _mImpl->field.extent() == 0;
785 }
786 
name() const787 C2String C2FieldUtils::Info::name() const {
788     return _mImpl->field.name();
789 }
790 
namedValues() const791 const C2FieldUtils::Info::NamedValuesType &C2FieldUtils::Info::namedValues() const {
792     return _mImpl->field.namedValues();
793 }
794 
offset() const795 size_t C2FieldUtils::Info::offset() const {
796     return _C2ParamInspector::GetOffset(_mImpl->field);
797 }
798 
parent() const799 C2FieldUtils::Info C2FieldUtils::Info::parent() const {
800     return Info(_mImpl->parent);
801 };
802 
size() const803 size_t C2FieldUtils::Info::size() const {
804     return _C2ParamInspector::GetSize(_mImpl->field);
805 }
806 
type() const807 C2FieldUtils::Info::type_t C2FieldUtils::Info::type() const {
808     return _mImpl->field.type();
809 }
810 
811 /* ------------------------- C2FieldUtils::Iterator ------------------------- */
812 
813 struct C2_HIDE C2FieldUtils::Iterator::Impl : public _C2ParamInspector {
814     Impl() = default;
815 
816     virtual ~Impl() = default;
817 
818     /// implements object equality
equalsC2FieldUtils::Iterator::Impl819     virtual bool equals(const std::shared_ptr<Impl> &other) const {
820         return other != nullptr && mHead == other->mHead;
821     };
822 
823     /// returns the info pointed to by this iterator
getC2FieldUtils::Iterator::Impl824     virtual value_type get() const {
825         return Info(mHead);
826     }
827 
828     /// increments this iterator
incrementC2FieldUtils::Iterator::Impl829     virtual void increment() {
830         // note: this cannot be abstract as we instantiate this for List::end(). increment to end()
831         // instead.
832         mHead.reset();
833     }
834 
835 protected:
ImplC2FieldUtils::Iterator::Impl836     Impl(std::shared_ptr<C2FieldUtils::Info::Impl> head)
837         : mHead(head) { }
838 
839     std::shared_ptr<Info::Impl> mHead; ///< current field
840 };
841 
Iterator(std::shared_ptr<Impl> impl)842 C2FieldUtils::Iterator::Iterator(std::shared_ptr<Impl> impl)
843     : mImpl(impl) { }
844 
operator *() const845 C2FieldUtils::Iterator::value_type C2FieldUtils::Iterator::operator*() const {
846     return mImpl->get();
847 }
848 
operator ++()849 C2FieldUtils::Iterator& C2FieldUtils::Iterator::operator++() {
850     mImpl->increment();
851     return *this;
852 }
853 
operator ==(const Iterator & other) const854 bool C2FieldUtils::Iterator::operator==(const Iterator &other) const {
855     return mImpl->equals(other.mImpl);
856 }
857 
858 /* ------------------------- C2FieldUtils::List ------------------------- */
859 
860 struct C2_HIDE C2FieldUtils::List::Impl {
861     virtual std::shared_ptr<Iterator::Impl> begin() const = 0;
862 
863     /// returns an iterator to the end of the list
endC2FieldUtils::List::Impl864     virtual std::shared_ptr<Iterator::Impl> end() const {
865         return std::make_shared<Iterator::Impl>();
866     }
867 
868     virtual ~Impl() = default;
869 };
870 
List(std::shared_ptr<Impl> impl)871 C2FieldUtils::List::List(std::shared_ptr<Impl> impl)
872     : mImpl(impl) { }
873 
begin() const874 C2FieldUtils::Iterator C2FieldUtils::List::begin() const {
875     return C2FieldUtils::Iterator(mImpl->begin());
876 }
877 
end() const878 C2FieldUtils::Iterator C2FieldUtils::List::end() const {
879     return C2FieldUtils::Iterator(mImpl->end());
880 }
881 
882 /* ------------------------- C2FieldUtils::enumerateFields ------------------------- */
883 
884 namespace {
885 
886 /**
887  * Iterator base class helper that allows descending into the field hierarchy.
888  */
889 struct C2FieldUtilsFieldsIteratorHelper : public C2FieldUtils::Iterator::Impl {
890     virtual ~C2FieldUtilsFieldsIteratorHelper() override = default;
891 
892     /// returns the base-field's offset of the parent field (or the param offset if no parent)
GetParentBaseFieldOffset__anon1a17b1a40811::C2FieldUtilsFieldsIteratorHelper893     static inline uint32_t GetParentBaseFieldOffset(
894             const std::shared_ptr<C2FieldUtils::Info::Impl> parent) {
895         return parent == nullptr ? sizeof(C2Param) : parent->baseFieldOffset;
896     }
897 
898     /// returns the offset of the parent field (or the param)
GetParentOffset__anon1a17b1a40811::C2FieldUtilsFieldsIteratorHelper899     static inline uint32_t GetParentOffset(const std::shared_ptr<C2FieldUtils::Info::Impl> parent) {
900         return parent == nullptr ? sizeof(C2Param) : GetOffset(parent->field);
901     }
902 
903 protected:
C2FieldUtilsFieldsIteratorHelper__anon1a17b1a40811::C2FieldUtilsFieldsIteratorHelper904     C2FieldUtilsFieldsIteratorHelper(
905             std::shared_ptr<C2ParamReflector> reflector,
906             uint32_t paramSize,
907             std::shared_ptr<C2FieldUtils::Info::Impl> head = nullptr)
908         : C2FieldUtils::Iterator::Impl(head),
909           mParamSize(paramSize),
910           mReflector(reflector) { }
911 
912     /// returns a leaf info object at a specific index for a child field
makeLeaf__anon1a17b1a40811::C2FieldUtilsFieldsIteratorHelper913     std::shared_ptr<C2FieldUtils::Info::Impl> makeLeaf(
914             const C2FieldDescriptor &field, uint32_t index) {
915         uint32_t parentOffset = GetParentOffset(mHead);
916         uint32_t arrayOffset = parentOffset + GetOffset(field);
917         uint32_t usedExtent = field.extent() ? :
918                 (std::max(arrayOffset, mParamSize) - arrayOffset) / GetSize(field);
919 
920         return std::make_shared<C2FieldUtils::Info::Impl>(
921                 OffsetFieldDescriptor(field, parentOffset + index * GetSize(field)),
922                 mHead /* parent */, index, mHead == nullptr ? 0 : mHead->depth + 1,
923                 GetParentBaseFieldOffset(mHead) + GetOffset(field),
924                 arrayOffset, usedExtent);
925     }
926 
927     /// returns whether this struct index have been traversed to get to this field
visited__anon1a17b1a40811::C2FieldUtilsFieldsIteratorHelper928     bool visited(C2Param::CoreIndex index) const {
929         for (const std::shared_ptr<C2StructDescriptor> &sd : mHistory) {
930             if (sd->coreIndex() == index) {
931                 return true;
932             }
933         }
934         return false;
935     }
936 
937     uint32_t mParamSize;
938     std::shared_ptr<C2ParamReflector> mReflector;
939     std::vector<std::shared_ptr<C2StructDescriptor>> mHistory; // structure types visited
940 };
941 
942 /**
943  * Iterator implementing enumerateFields() that visits each base field.
944  */
945 struct C2FieldUtilsFieldsIterator : public C2FieldUtilsFieldsIteratorHelper {
946     /// enumerate base fields of a parameter
C2FieldUtilsFieldsIterator__anon1a17b1a40811::C2FieldUtilsFieldsIterator947     C2FieldUtilsFieldsIterator(const C2Param &param, std::shared_ptr<C2ParamReflector> reflector)
948         : C2FieldUtilsFieldsIteratorHelper(reflector, param.size()) {
949         descendInto(param.coreIndex());
950     }
951 
952     /// enumerate base fields of a field
C2FieldUtilsFieldsIterator__anon1a17b1a40811::C2FieldUtilsFieldsIterator953     C2FieldUtilsFieldsIterator(std::shared_ptr<C2FieldUtilsFieldsIterator> impl)
954         : C2FieldUtilsFieldsIteratorHelper(impl->mReflector, impl->mParamSize, impl->mHead) {
955         mHistory = impl->mHistory;
956         if (mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) {
957             C2Param::CoreIndex index = { mHead->field.type() &~C2FieldDescriptor::STRUCT_FLAG };
958             if (!visited(index)) {
959                 descendInto(index);
960             }
961         }
962     }
963 
964     virtual ~C2FieldUtilsFieldsIterator() override = default;
965 
966     /// Increments this iterator by visiting each base field.
increment__anon1a17b1a40811::C2FieldUtilsFieldsIterator967     virtual void increment() override {
968         // don't go past end
969         if (mHead == nullptr || _mFields.empty()) {
970             return;
971         }
972 
973         // descend into structures
974         if (mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) {
975             C2Param::CoreIndex index = { mHead->field.type() &~C2FieldDescriptor::STRUCT_FLAG };
976             // do not recurse into the same structs
977             if (!visited(index) && descendInto(index)) {
978                 return;
979             }
980         }
981 
982         // ascend after the last field in the current struct
983         while (!mHistory.empty() && _mFields.back() == mHistory.back()->end()) {
984             mHead = mHead->parent;
985             mHistory.pop_back();
986             _mFields.pop_back();
987         }
988 
989         // done if history is now empty
990         if (_mFields.empty()) {
991             // we could be traversing a sub-tree so clear head
992             mHead.reset();
993             return;
994         }
995 
996         // move to the next field in the current struct
997         C2StructDescriptor::field_iterator next = _mFields.back();
998         mHead->field = OffsetFieldDescriptor(*next, GetParentOffset(mHead->parent));
999         mHead->index = 0; // reset index just in case for correctness
1000         mHead->baseFieldOffset = GetParentBaseFieldOffset(mHead->parent) + GetOffset(*next);
1001         mHead->arrayOffset = GetOffset(mHead->field);
1002         mHead->usedExtent = mHead->field.extent() ? :
1003                 (std::max(mHead->arrayOffset, mParamSize) - mHead->arrayOffset)
1004                         / GetSize(mHead->field);
1005         ++_mFields.back();
1006     }
1007 
1008 private:
1009     /// If the current field is a known, valid (untraversed) structure, it modifies this iterator
1010     /// to point to the first field of the structure and returns true. Otherwise, it does not
1011     /// modify this iterator and returns false.
descendInto__anon1a17b1a40811::C2FieldUtilsFieldsIterator1012     bool descendInto(C2Param::CoreIndex index) {
1013         std::unique_ptr<C2StructDescriptor> descUnique = mReflector->describe(index);
1014         // descend into known structs (as long as they have at least one field)
1015         if (descUnique && descUnique->begin() != descUnique->end()) {
1016             std::shared_ptr<C2StructDescriptor> desc(std::move(descUnique));
1017             mHistory.emplace_back(desc);
1018             C2StructDescriptor::field_iterator first = desc->begin();
1019             mHead = makeLeaf(*first, 0 /* index */);
1020             _mFields.emplace_back(++first);
1021             return true;
1022         }
1023         return false;
1024     }
1025 
1026     /// next field pointers for each depth.
1027     /// note: _mFields may be shorted than mHistory, if iterating at a depth
1028     std::vector<C2StructDescriptor::field_iterator> _mFields;
1029 };
1030 
1031 /**
1032  * Iterable implementing enumerateFields().
1033  */
1034 struct C2FieldUtilsFieldIterable : public C2FieldUtils::List::Impl {
1035     /// returns an iterator to the beginning of the list
begin__anon1a17b1a40811::C2FieldUtilsFieldIterable1036     virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
1037         return std::make_shared<C2FieldUtilsFieldsIterator>(*_mParam, _mReflector);
1038     };
1039 
C2FieldUtilsFieldIterable__anon1a17b1a40811::C2FieldUtilsFieldIterable1040     C2FieldUtilsFieldIterable(const C2Param &param, std::shared_ptr<C2ParamReflector> reflector)
1041         : _mParam(&param), _mReflector(reflector) { }
1042 
1043 private:
1044     const C2Param *_mParam;
1045     std::shared_ptr<C2ParamReflector> _mReflector;
1046 };
1047 
1048 }
1049 
enumerateFields(const C2Param & param,const std::shared_ptr<C2ParamReflector> & reflector)1050 C2FieldUtils::List C2FieldUtils::enumerateFields(
1051         const C2Param &param, const std::shared_ptr<C2ParamReflector> &reflector) {
1052     return C2FieldUtils::List(std::make_shared<C2FieldUtilsFieldIterable>(param, reflector));
1053 }
1054 
1055 /* ------------------------- C2FieldUtils::enumerate siblings ------------------------- */
1056 
1057 namespace {
1058 
1059 struct C2FieldUtilsCousinsIterator : public C2FieldUtils::Iterator::Impl {
C2FieldUtilsCousinsIterator__anon1a17b1a40911::C2FieldUtilsCousinsIterator1060     C2FieldUtilsCousinsIterator(
1061                 const std::shared_ptr<C2FieldUtils::Info::Impl> &info, size_t level)
1062           // clone info chain as this iterator will change it
1063         : C2FieldUtils::Iterator::Impl(C2FieldUtils::Info::Impl::Clone(info)) {
1064         if (level == 0) {
1065             return;
1066         }
1067 
1068         // store parent chain (up to level) for quick access
1069         std::shared_ptr<C2FieldUtils::Info::Impl> node = mHead;
1070         size_t ix = 0;
1071         for (; ix < level && node; ++ix) {
1072             node->index = 0;
1073             _mPath.emplace_back(node);
1074             node = node->parent;
1075         }
1076         setupPath(ix);
1077     }
1078 
1079     virtual ~C2FieldUtilsCousinsIterator() override = default;
1080 
1081     /// Increments this iterator by visiting each index.
increment__anon1a17b1a40911::C2FieldUtilsCousinsIterator1082     virtual void increment() override {
1083         size_t ix = 0;
1084         while (ix < _mPath.size()) {
1085             if (++_mPath[ix]->index < _mPath[ix]->usedExtent) {
1086                 setupPath(ix + 1);
1087                 return;
1088             }
1089             _mPath[ix++]->index = 0;
1090         }
1091         mHead.reset();
1092     }
1093 
1094 private:
1095     /// adjusts field offsets along the path up to the specific level - 1.
1096     /// This in-fact has to be done down the path from parent to child as child fields must
1097     /// fall within parent fields.
setupPath__anon1a17b1a40911::C2FieldUtilsCousinsIterator1098     void setupPath(size_t level) {
1099         C2_CHECK_LE(level, _mPath.size());
1100         uint32_t oldArrayOffset = level ? _mPath[level - 1]->arrayOffset : 0 /* unused */;
1101         while (level) {
1102             --level;
1103             C2FieldUtils::Info::Impl &path = *_mPath[level];
1104             uint32_t size = GetSize(path.field);
1105             uint32_t offset = path.arrayOffset + size * path.index;
1106             SetOffset(path.field, offset);
1107             if (level) {
1108                 // reset child's array offset to fall within current index, but hold onto the
1109                 // original value of the arrayOffset so that we can adjust subsequent children.
1110                 // This is because the modulo is only defined within the current array.
1111                 uint32_t childArrayOffset =
1112                     offset + (_mPath[level - 1]->arrayOffset - oldArrayOffset) % size;
1113                 oldArrayOffset = _mPath[level - 1]->arrayOffset;
1114                 _mPath[level - 1]->arrayOffset = childArrayOffset;
1115             }
1116         }
1117     }
1118 
1119     std::vector<std::shared_ptr<C2FieldUtils::Info::Impl>> _mPath;
1120 };
1121 
1122 /**
1123  * Iterable implementing enumerateFields().
1124  */
1125 struct C2FieldUtilsCousinsIterable : public C2FieldUtils::List::Impl {
1126     /// returns an iterator to the beginning of the list
begin__anon1a17b1a40911::C2FieldUtilsCousinsIterable1127     virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
1128         return std::make_shared<C2FieldUtilsCousinsIterator>(_mHead, _mLevel);
1129     };
1130 
C2FieldUtilsCousinsIterable__anon1a17b1a40911::C2FieldUtilsCousinsIterable1131     C2FieldUtilsCousinsIterable(const C2FieldUtils::Info &field, uint32_t level)
1132         : _mHead(C2FieldUtils::_Inspector::GetImpl(field)), _mLevel(level) { }
1133 
1134 private:
1135     std::shared_ptr<C2FieldUtils::Info::Impl> _mHead;
1136     size_t _mLevel;
1137 };
1138 
1139 }
1140 
enumerateCousins(const C2FieldUtils::Info & field,uint32_t level)1141 C2FieldUtils::List C2FieldUtils::enumerateCousins(const C2FieldUtils::Info &field, uint32_t level) {
1142     return C2FieldUtils::List(std::make_shared<C2FieldUtilsCousinsIterable>(field, level));
1143 }
1144 
1145 /* ------------------------- C2FieldUtils::locateField ------------------------- */
1146 
1147 namespace {
1148 
1149 /**
1150  * Iterator implementing locateField().
1151  */
1152 struct C2FieldUtilsFieldLocator : public C2FieldUtilsFieldsIteratorHelper {
C2FieldUtilsFieldLocator__anon1a17b1a40a11::C2FieldUtilsFieldLocator1153     C2FieldUtilsFieldLocator(
1154             C2Param::CoreIndex index, const _C2FieldId &field, uint32_t paramSize,
1155             std::shared_ptr<C2ParamReflector> reflector)
1156         : C2FieldUtilsFieldsIteratorHelper(reflector, paramSize),
1157           _mField(field) {
1158         while (descendInto(index)) {
1159             if ((mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) == 0) {
1160                 break;
1161             }
1162             index = C2Param::CoreIndex(mHead->field.type() &~ C2FieldDescriptor::STRUCT_FLAG);
1163         }
1164     }
1165 
increment__anon1a17b1a40a11::C2FieldUtilsFieldLocator1166     void increment() {
1167         mHead = _mTail;
1168         _mTail = nullptr;
1169     }
1170 
1171 private:
1172     /// If the current field is a known, valid (untraversed) structure, it modifies this iterator
1173     /// to point to the field at the beginning/end of the given field of the structure and returns
1174     /// true. Otherwise, including if no such field exists in the structure, it does not modify this
1175     /// iterator and returns false.
descendInto__anon1a17b1a40a11::C2FieldUtilsFieldLocator1176     bool descendInto(C2Param::CoreIndex index) {
1177         // check that the boundaries of the field to be located are still within the same parent
1178         // field
1179         if (mHead != _mTail) {
1180             return false;
1181         }
1182 
1183         std::unique_ptr<C2StructDescriptor> descUnique = mReflector->describe(index);
1184         // descend into known structs (as long as they have at least one field)
1185         if (descUnique && descUnique->begin() != descUnique->end()) {
1186             std::shared_ptr<C2StructDescriptor> desc(std::move(descUnique));
1187             mHistory.emplace_back(desc);
1188 
1189             uint32_t parentOffset = GetParentOffset(mHead);
1190 
1191             // locate field using a dummy field descriptor
1192             C2FieldDescriptor dummy = {
1193                 C2FieldDescriptor::BLOB, 1 /* extent */, "name",
1194                 GetOffset(_mField) - parentOffset, GetSize(_mField)
1195             };
1196 
1197             // locate first field where offset is greater than dummy offset (which is one past)
1198             auto it = std::upper_bound(
1199                     desc->cbegin(), desc->cend(), dummy,
1200                     [](const C2FieldDescriptor &a, const C2FieldDescriptor &b) -> bool {
1201                 return _C2ParamInspector::GetOffset(a) < _C2ParamInspector::GetOffset(b);
1202             });
1203             if (it == desc->begin()) {
1204                 // field is prior to first field
1205                 return false;
1206             }
1207             --it;
1208             const C2FieldDescriptor &field = *it;
1209 
1210             // check that dummy end-offset is within this field
1211             uint32_t structSize = std::max(mParamSize, parentOffset) - parentOffset;
1212             if (GetEndOffset(dummy) > GetEndOffset(field, structSize)) {
1213                 return false;
1214             }
1215 
1216             uint32_t startIndex = (GetOffset(dummy) - GetOffset(field)) / GetSize(field);
1217             uint32_t endIndex =
1218                 (GetEndOffset(dummy) - GetOffset(field) + GetSize(field) - 1) / GetSize(field);
1219             if (endIndex > startIndex) {
1220                 // Field size could be zero, in which case end index is still on start index.
1221                 // However, for all other cases, endIndex was rounded up to the next index, so
1222                 // decrement it.
1223                 --endIndex;
1224             }
1225             std::shared_ptr<C2FieldUtils::Info::Impl> startLeaf =
1226                 makeLeaf(field, startIndex);
1227             if (endIndex == startIndex) {
1228                 _mTail = startLeaf;
1229                 mHead = startLeaf;
1230             } else {
1231                 _mTail = makeLeaf(field, endIndex);
1232                 mHead = startLeaf;
1233             }
1234             return true;
1235         }
1236         return false;
1237     }
1238 
1239     _C2FieldId _mField;
1240     std::shared_ptr<C2FieldUtils::Info::Impl> _mTail;
1241 };
1242 
1243 /**
1244  * Iterable implementing locateField().
1245  */
1246 struct C2FieldUtilsFieldLocation : public C2FieldUtils::List::Impl {
1247     /// returns an iterator to the beginning of the list
begin__anon1a17b1a40a11::C2FieldUtilsFieldLocation1248     virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
1249         return std::make_shared<C2FieldUtilsFieldLocator>(
1250                 _mIndex, _mField, _mParamSize, _mReflector);
1251     };
1252 
C2FieldUtilsFieldLocation__anon1a17b1a40a11::C2FieldUtilsFieldLocation1253     C2FieldUtilsFieldLocation(
1254             const C2ParamField &pf, std::shared_ptr<C2ParamReflector> reflector)
1255         : _mIndex(C2Param::CoreIndex(_C2ParamInspector::GetIndex(pf))),
1256           _mField(_C2ParamInspector::GetField(pf)),
1257           _mParamSize(0),
1258           _mReflector(reflector) { }
1259 
1260 
C2FieldUtilsFieldLocation__anon1a17b1a40a11::C2FieldUtilsFieldLocation1261     C2FieldUtilsFieldLocation(
1262             const C2Param &param, const _C2FieldId &field,
1263             std::shared_ptr<C2ParamReflector> reflector)
1264         : _mIndex(param.coreIndex()),
1265           _mField(field),
1266           _mParamSize(param.size()),
1267           _mReflector(reflector) { }
1268 
1269 private:
1270     C2Param::CoreIndex _mIndex;
1271     _C2FieldId _mField;
1272     uint32_t _mParamSize;
1273     std::shared_ptr<C2ParamReflector> _mReflector;
1274 };
1275 
1276 }
1277 
locateField(const C2ParamField & pf,const std::shared_ptr<C2ParamReflector> & reflector)1278 std::vector<C2FieldUtils::Info> C2FieldUtils::locateField(
1279         const C2ParamField &pf, const std::shared_ptr<C2ParamReflector> &reflector) {
1280     C2FieldUtils::List location = { std::make_shared<C2FieldUtilsFieldLocation>(pf, reflector) };
1281     return std::vector<Info>(location.begin(), location.end());
1282 }
1283 
locateField(const C2Param & param,const _C2FieldId & field,const std::shared_ptr<C2ParamReflector> & reflector)1284 std::vector<C2FieldUtils::Info> C2FieldUtils::locateField(
1285         const C2Param &param, const _C2FieldId &field,
1286         const std::shared_ptr<C2ParamReflector> &reflector) {
1287     C2FieldUtils::List location = {
1288         std::make_shared<C2FieldUtilsFieldLocation>(param, field, reflector)
1289     };
1290     return std::vector<Info>(location.begin(), location.end());
1291 }
1292 
1293