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         values.erase(std::remove_if(values.begin(), values.end(),
220                                     [&limit, minMask](
221                                         const C2Value::Primitive &v) -> bool {
222                                       T value = v.ref<ValueType>() | minMask;
223                                       return value == minMask ||
224                                              !limit.contains(value);
225                                     }),
226                      values.end());
227         // we also need to do it vice versa
228         for (const C2Value::Primitive &v : _mValues) {
229             T value = v.ref<ValueType>() | minMask;
230             if (value != minMask && contains(value)) {
231                 values.emplace_back((ValueType)value);
232             }
233         }
234     }
235     return C2SupportedFlags(std::move(values));
236 }
237 
238 template<typename T>
min() const239 T C2SupportedFlags<T>::min() const {
240     if (!_mValues.empty()) {
241         return _mValues.front().template ref<ValueType>();
242     } else {
243         return T(0);
244     }
245 }
246 
247 template class C2SupportedFlags<uint8_t>;
248 template class C2SupportedFlags<char>;
249 template class C2SupportedFlags<int32_t>;
250 template class C2SupportedFlags<uint32_t>;
251 //template class C2SupportedFlags<c2_cntr32_t>;
252 template class C2SupportedFlags<int64_t>;
253 template class C2SupportedFlags<uint64_t>;
254 //template class C2SupportedFlags<c2_cntr64_t>;
255 
256 /* -------------------------- C2SupportedValueSet -------------------------- */
257 
258 /**
259  * Ordered supported value set for a field of a given type.
260  */
261 template<typename T>
contains(T value) const262 bool C2SupportedValueSet<T>::contains(T value) const {
263     return std::find_if(_mValues.cbegin(), _mValues.cend(),
264             [value](const C2Value::Primitive &p) -> bool {
265                 return value == p.ref<ValueType>();
266             }) != _mValues.cend();
267 }
268 
269 template<typename T>
limitedTo(const C2SupportedValueSet<T> & limit) const270 C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedValueSet<T> &limit) const {
271     std::vector<C2Value::Primitive> values = _mValues; // make a copy
272     values.erase(std::remove_if(values.begin(), values.end(),
273                                 [&limit](const C2Value::Primitive &v) -> bool {
274                                   return !limit.contains(v.ref<ValueType>());
275                                 }),
276                  values.end());
277     return C2SupportedValueSet(std::move(values));
278 }
279 
280 template<typename T>
limitedTo(const C2SupportedRange<T> & limit) const281 C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedRange<T> &limit) const {
282     std::vector<C2Value::Primitive> values = _mValues; // make a copy
283     values.erase(std::remove_if(values.begin(), values.end(),
284                                 [&limit](const C2Value::Primitive &v) -> bool {
285                                   return !limit.contains(v.ref<ValueType>());
286                                 }),
287                  values.end());
288     return C2SupportedValueSet(std::move(values));
289 }
290 
291 template<typename T>
limitedTo(const C2SupportedFlags<T> & limit) const292 C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
293     std::vector<C2Value::Primitive> values = _mValues; // make a copy
294     values.erase(std::remove_if(values.begin(), values.end(),
295                                 [&limit](const C2Value::Primitive &v) -> bool {
296                                   return !limit.contains(v.ref<ValueType>());
297                                 }),
298                  values.end());
299     return C2SupportedValueSet(std::move(values));
300 }
301 
302 template<typename T>
values() const303 const std::vector<T> C2SupportedValueSet<T>::values() const {
304     std::vector<T> vals(_mValues.size());
305     std::transform(_mValues.cbegin(), _mValues.cend(), vals.begin(), [](const C2Value::Primitive &p) -> T {
306         return p.ref<ValueType>();
307     });
308     return vals;
309 }
310 
311 template class C2SupportedValueSet<uint8_t>;
312 template class C2SupportedValueSet<char>;
313 template class C2SupportedValueSet<int32_t>;
314 template class C2SupportedValueSet<uint32_t>;
315 //template class C2SupportedValueSet<c2_cntr32_t>;
316 template class C2SupportedValueSet<int64_t>;
317 template class C2SupportedValueSet<uint64_t>;
318 //template class C2SupportedValueSet<c2_cntr64_t>;
319 template class C2SupportedValueSet<float>;
320 
321 /* ---------------------- C2FieldSupportedValuesHelper ---------------------- */
322 
323 template<typename T>
324 struct C2FieldSupportedValuesHelper<T>::Impl {
ImplC2FieldSupportedValuesHelper::Impl325     Impl(const C2FieldSupportedValues &values)
326         : _mType(values.type),
327           _mRange(values),
328           _mValues(values),
329           _mFlags(values) { }
330 
331     bool supports(T value) const;
332 
333 private:
334     typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
335     C2FieldSupportedValues::type_t _mType;
336     C2SupportedRange<ValueType> _mRange;
337     C2SupportedValueSet<ValueType> _mValues;
338     C2SupportedValueSet<ValueType> _mFlags;
339 
340 //    friend std::ostream& operator<< <T>(std::ostream& os, const C2FieldSupportedValuesHelper<T>::Impl &i);
341 //    friend std::ostream& operator<<(std::ostream& os, const Impl &i);
342     std::ostream& streamOut(std::ostream& os) const;
343 };
344 
345 template<typename T>
supports(T value) const346 bool C2FieldSupportedValuesHelper<T>::Impl::supports(T value) const {
347     switch (_mType) {
348         case C2FieldSupportedValues::RANGE: return _mRange.contains(value);
349         case C2FieldSupportedValues::VALUES: return _mValues.contains(value);
350         case C2FieldSupportedValues::FLAGS: return _mFlags.contains(value);
351         default: return false;
352     }
353 }
354 
355 template<typename T>
C2FieldSupportedValuesHelper(const C2FieldSupportedValues & values)356 C2FieldSupportedValuesHelper<T>::C2FieldSupportedValuesHelper(const C2FieldSupportedValues &values)
357     : _mImpl(std::make_unique<C2FieldSupportedValuesHelper<T>::Impl>(values)) { }
358 
359 template<typename T>
360 C2FieldSupportedValuesHelper<T>::~C2FieldSupportedValuesHelper() = default;
361 
362 template<typename T>
supports(T value) const363 bool C2FieldSupportedValuesHelper<T>::supports(T value) const {
364     return _mImpl->supports(value);
365 }
366 
367 template class C2FieldSupportedValuesHelper<uint8_t>;
368 template class C2FieldSupportedValuesHelper<char>;
369 template class C2FieldSupportedValuesHelper<int32_t>;
370 template class C2FieldSupportedValuesHelper<uint32_t>;
371 //template class C2FieldSupportedValuesHelper<c2_cntr32_t>;
372 template class C2FieldSupportedValuesHelper<int64_t>;
373 template class C2FieldSupportedValuesHelper<uint64_t>;
374 //template class C2FieldSupportedValuesHelper<c2_cntr64_t>;
375 template class C2FieldSupportedValuesHelper<float>;
376 
377 /* ----------------------- C2ParamFieldValuesBuilder ----------------------- */
378 
379 template<typename T>
380 struct C2ParamFieldValuesBuilder<T>::Impl {
ImplC2ParamFieldValuesBuilder::Impl381     Impl(const C2ParamField &field)
382         : _mParamField(field),
383           _mType(type_t::RANGE),
384           _mDefined(false),
385           _mRange(C2SupportedRange<T>::Any()),
386           _mValues(C2SupportedValueSet<T>::None()),
387           _mFlags(C2SupportedFlags<T>::None()) { }
388 
389     /**
390      * Get C2ParamFieldValues from this builder.
391      */
operator C2ParamFieldValuesC2ParamFieldValuesBuilder::Impl392     operator C2ParamFieldValues() const {
393         if (!_mDefined) {
394             return C2ParamFieldValues(_mParamField);
395         }
396         switch (_mType) {
397         case type_t::EMPTY:
398         case type_t::VALUES:
399             return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mValues);
400         case type_t::RANGE:
401             return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mRange);
402         case type_t::FLAGS:
403             return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mFlags);
404         default:
405             // TRESPASS
406             // should never get here
407             return C2ParamFieldValues(_mParamField);
408         }
409     }
410 
411     /** Define the supported values as the currently supported values of this builder. */
anyC2ParamFieldValuesBuilder::Impl412     void any() {
413         _mDefined = true;
414     }
415 
416     /** Restrict (and thus define) the supported values to none. */
noneC2ParamFieldValuesBuilder::Impl417     void none() {
418         _mDefined = true;
419         _mType = type_t::VALUES;
420         _mValues.clear();
421     }
422 
423     /** Restrict (and thus define) the supported values to |value| alone. */
equalToC2ParamFieldValuesBuilder::Impl424     void equalTo(T value) {
425          return limitTo(C2SupportedValueSet<T>::OneOf({value}));
426     }
427 
428     /** Restrict (and thus define) the supported values to a value set. */
limitToC2ParamFieldValuesBuilder::Impl429     void limitTo(const C2SupportedValueSet<T> &limit) {
430         if (!_mDefined) {
431             C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
432 
433             // shortcut for first limit applied
434             _mDefined = true;
435             _mValues = limit;
436             _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
437         } else {
438             switch (_mType) {
439             case type_t::EMPTY:
440             case type_t::VALUES:
441                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
442                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
443 
444                 _mValues = _mValues.limitedTo(limit);
445                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
446                 break;
447             case type_t::RANGE:
448                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
449                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
450 
451                 _mValues = limit.limitedTo(_mRange);
452                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
453                 break;
454             case type_t::FLAGS:
455                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
456                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
457 
458                 _mValues = limit.limitedTo(_mFlags);
459                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
460                 break;
461             default:
462                 C2_LOG(FATAL); // should not be here
463             }
464             // TODO: support flags
465         }
466         C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
467     }
468 
469     /** Restrict (and thus define) the supported values to a flag set. */
limitToC2ParamFieldValuesBuilder::Impl470     void limitTo(const C2SupportedFlags<T> &limit) {
471         if (!_mDefined) {
472             C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
473 
474             // shortcut for first limit applied
475             _mDefined = true;
476             _mFlags = limit;
477             _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS;
478         } else {
479             switch (_mType) {
480             case type_t::EMPTY:
481             case type_t::VALUES:
482                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
483                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
484 
485                 _mValues = _mValues.limitedTo(limit);
486                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
487                 C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
488                 break;
489             case type_t::FLAGS:
490                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mFlags) << ").limitTo("
491                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
492 
493                 _mFlags = _mFlags.limitedTo(limit);
494                 _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS;
495                 C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mFlags);
496                 break;
497             case type_t::RANGE:
498                 C2_LOG(FATAL) << "limiting ranges to flags is not supported";
499                 _mType = type_t::EMPTY;
500                 break;
501             default:
502                 C2_LOG(FATAL); // should not be here
503             }
504         }
505     }
506 
limitToC2ParamFieldValuesBuilder::Impl507     void limitTo(const C2SupportedRange<T> &limit) {
508         if (!_mDefined) {
509             C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
510 
511             // shortcut for first limit applied
512             _mDefined = true;
513             _mRange = limit;
514             _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE;
515             C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange);
516         } else {
517             switch (_mType) {
518             case type_t::EMPTY:
519             case type_t::VALUES:
520                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
521                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
522                 _mValues = _mValues.limitedTo(limit);
523                 _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
524                 C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
525                 break;
526             case type_t::FLAGS:
527                 C2_LOG(FATAL) << "limiting flags to ranges is not supported";
528                 _mType = type_t::EMPTY;
529                 break;
530             case type_t::RANGE:
531                 C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
532                         << C2FieldSupportedValuesHelper<T>(limit) << ")";
533                 _mRange = _mRange.limitedTo(limit);
534                 C2_DCHECK(_mValues.isEmpty());
535                 _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE;
536                 C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange);
537                 break;
538             default:
539                 C2_LOG(FATAL); // should not be here
540             }
541         }
542     }
543 
544 private:
instantiateC2ParamFieldValuesBuilder::Impl545     void instantiate() __unused {
546         (void)_mValues.values(); // instantiate non-const values()
547     }
548 
instantiateC2ParamFieldValuesBuilder::Impl549     void instantiate() const __unused {
550         (void)_mValues.values(); // instantiate const values()
551     }
552 
553     typedef C2FieldSupportedValues::type_t type_t;
554 
555     C2ParamField _mParamField;
556     type_t _mType;
557     bool _mDefined;
558     C2SupportedRange<T> _mRange;
559     C2SupportedValueSet<T> _mValues;
560     C2SupportedFlags<T> _mFlags;
561 
562 };
563 
564 template<typename T>
operator C2ParamFieldValues() const565 C2ParamFieldValuesBuilder<T>::operator C2ParamFieldValues() const {
566     return (C2ParamFieldValues)(*_mImpl.get());
567 }
568 
569 template<typename T>
C2ParamFieldValuesBuilder(const C2ParamField & field)570 C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamField &field)
571     : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(field)) { }
572 
573 template<typename T>
any()574 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::any() {
575     _mImpl->any();
576     return *this;
577 }
578 
579 template<typename T>
none()580 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::none() {
581     _mImpl->none();
582     return *this;
583 }
584 
585 template<typename T>
equalTo(T value)586 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::equalTo(T value) {
587     _mImpl->equalTo(value);
588     return *this;
589 }
590 
591 template<typename T>
limitTo(const C2SupportedValueSet<T> & limit)592 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedValueSet<T> &limit) {
593     _mImpl->limitTo(limit);
594     return *this;
595 }
596 
597 template<typename T>
limitTo(const C2SupportedFlags<T> & limit)598 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedFlags<T> &limit) {
599     _mImpl->limitTo(limit);
600     return *this;
601 }
602 
603 template<typename T>
limitTo(const C2SupportedRange<T> & limit)604 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedRange<T> &limit) {
605     _mImpl->limitTo(limit);
606     return *this;
607 }
608 
609 template<typename T>
C2ParamFieldValuesBuilder(const C2ParamFieldValuesBuilder<T> & other)610 C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamFieldValuesBuilder<T> &other)
611     : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get())) { }
612 
613 template<typename T>
operator =(const C2ParamFieldValuesBuilder<T> & other)614 C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::operator=(
615         const C2ParamFieldValuesBuilder<T> &other) {
616     _mImpl = std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get());
617     return *this;
618 }
619 
620 template<typename T>
621 C2ParamFieldValuesBuilder<T>::~C2ParamFieldValuesBuilder() = default;
622 
623 template class C2ParamFieldValuesBuilder<uint8_t>;
624 template class C2ParamFieldValuesBuilder<char>;
625 template class C2ParamFieldValuesBuilder<int32_t>;
626 template class C2ParamFieldValuesBuilder<uint32_t>;
627 //template class C2ParamFieldValuesBuilder<c2_cntr32_t>;
628 template class C2ParamFieldValuesBuilder<int64_t>;
629 template class C2ParamFieldValuesBuilder<uint64_t>;
630 //template class C2ParamFieldValuesBuilder<c2_cntr64_t>;
631 template class C2ParamFieldValuesBuilder<float>;
632 
633 /* ------------------------- C2SettingResultBuilder ------------------------- */
634 
C2SettingConflictsBuilder()635 C2SettingConflictsBuilder::C2SettingConflictsBuilder() : _mConflicts() { }
636 
C2SettingConflictsBuilder(C2ParamFieldValues && conflict)637 C2SettingConflictsBuilder::C2SettingConflictsBuilder(C2ParamFieldValues &&conflict) {
638     _mConflicts.emplace_back(std::move(conflict));
639 }
640 
with(C2ParamFieldValues && conflict)641 C2SettingConflictsBuilder& C2SettingConflictsBuilder::with(C2ParamFieldValues &&conflict) {
642     _mConflicts.emplace_back(std::move(conflict));
643     return *this;
644 }
645 
retrieveConflicts()646 std::vector<C2ParamFieldValues> C2SettingConflictsBuilder::retrieveConflicts() {
647     return std::move(_mConflicts);
648 }
649 
650 /* ------------------------- C2SettingResult/sBuilder ------------------------- */
651 
ReadOnly(const C2ParamField & param)652 C2SettingResult C2SettingResultBuilder::ReadOnly(const C2ParamField &param) {
653     return C2SettingResult { C2SettingResult::READ_ONLY, { param }, { } };
654 }
655 
BadValue(const C2ParamField & paramField,bool isInfo)656 C2SettingResult C2SettingResultBuilder::BadValue(const C2ParamField &paramField, bool isInfo) {
657     return { isInfo ? C2SettingResult::INFO_BAD_VALUE : C2SettingResult::BAD_VALUE,
658              { paramField }, { } };
659 }
660 
Conflict(C2ParamFieldValues && paramFieldValues,C2SettingConflictsBuilder & conflicts,bool isInfo)661 C2SettingResult C2SettingResultBuilder::Conflict(
662         C2ParamFieldValues &&paramFieldValues, C2SettingConflictsBuilder &conflicts, bool isInfo) {
663     C2_CHECK(!conflicts.empty());
664     if (isInfo) {
665         return C2SettingResult {
666             C2SettingResult::INFO_CONFLICT,
667             std::move(paramFieldValues), conflicts.retrieveConflicts()
668         };
669     } else {
670         return C2SettingResult {
671             C2SettingResult::CONFLICT,
672             std::move(paramFieldValues), conflicts.retrieveConflicts()
673         };
674     }
675 }
676 
C2SettingResultsBuilder(C2SettingResult && result)677 C2SettingResultsBuilder::C2SettingResultsBuilder(C2SettingResult &&result)
678         : _mStatus(C2_BAD_VALUE) {
679     _mResults.emplace_back(new C2SettingResult(std::move(result)));
680 }
681 
plus(C2SettingResultsBuilder && results)682 C2SettingResultsBuilder C2SettingResultsBuilder::plus(C2SettingResultsBuilder&& results) {
683     for (std::unique_ptr<C2SettingResult> &r : results._mResults) {
684         _mResults.emplace_back(std::move(r));
685     }
686     results._mResults.clear();
687     // TODO: mStatus
688     return std::move(*this);
689 }
690 
retrieveFailures(std::vector<std::unique_ptr<C2SettingResult>> * const failures)691 c2_status_t C2SettingResultsBuilder::retrieveFailures(
692         std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
693     for (std::unique_ptr<C2SettingResult> &r : _mResults) {
694         failures->emplace_back(std::move(r));
695     }
696     _mResults.clear();
697     return _mStatus;
698 }
699 
C2SettingResultsBuilder(c2_status_t status)700 C2SettingResultsBuilder::C2SettingResultsBuilder(c2_status_t status) : _mStatus(status) {
701     // status must be one of OK, BAD_STATE, TIMED_OUT or CORRUPTED
702     // mainly: BLOCKING, BAD_INDEX, BAD_VALUE and NO_MEMORY requires a setting attempt
703 }
704 
705 #pragma clang diagnostic pop
706 
707 /* ------------------------- C2FieldUtils ------------------------- */
708 
709 struct C2_HIDE C2FieldUtils::_Inspector {
710     /// returns the implementation object
GetImplC2FieldUtils::_Inspector711     inline static std::shared_ptr<Info::Impl> GetImpl(const Info &info) {
712         return info._mImpl;
713     }
714 };
715 
716 /* ------------------------- C2FieldUtils::Info ------------------------- */
717 
718 struct C2_HIDE C2FieldUtils::Info::Impl {
719     C2FieldDescriptor field;
720     std::shared_ptr<Impl> parent;
721     uint32_t index;
722     uint32_t depth;
723     uint32_t baseFieldOffset;
724     uint32_t arrayOffset;
725     uint32_t usedExtent;
726 
727     /// creates a copy of this object including copies of its parent chain
728     Impl clone() const;
729 
730     /// creates a copy of a shared pointer to an object
731     static std::shared_ptr<Impl> Clone(const std::shared_ptr<Impl> &);
732 
ImplC2FieldUtils::Info::Impl733     Impl(const C2FieldDescriptor &field_, std::shared_ptr<Impl> parent_,
734             uint32_t index_, uint32_t depth_, uint32_t baseFieldOffset_,
735             uint32_t arrayOffset_, uint32_t usedExtent_)
736         : field(field_), parent(parent_), index(index_), depth(depth_),
737           baseFieldOffset(baseFieldOffset_), arrayOffset(arrayOffset_), usedExtent(usedExtent_) { }
738 };
739 
Clone(const std::shared_ptr<Impl> & info)740 std::shared_ptr<C2FieldUtils::Info::Impl> C2FieldUtils::Info::Impl::Clone(const std::shared_ptr<Impl> &info) {
741     if (info) {
742         return std::make_shared<Impl>(info->clone());
743     }
744     return nullptr;
745 }
746 
clone() const747 C2FieldUtils::Info::Impl C2FieldUtils::Info::Impl::clone() const {
748     Impl res = Impl(*this);
749     res.parent = Clone(res.parent);
750     return res;
751 }
752 
Info(std::shared_ptr<Impl> impl)753 C2FieldUtils::Info::Info(std::shared_ptr<Impl> impl)
754     : _mImpl(impl) { }
755 
arrayOffset() const756 size_t C2FieldUtils::Info::arrayOffset() const {
757     return _mImpl->arrayOffset;
758 }
759 
arraySize() const760 size_t C2FieldUtils::Info::arraySize() const {
761     return extent() * size();
762 }
763 
baseFieldOffset() const764 size_t C2FieldUtils::Info::baseFieldOffset() const {
765     return _mImpl->baseFieldOffset;
766 };
767 
depth() const768 size_t C2FieldUtils::Info::depth() const {
769     return _mImpl->depth;
770 }
771 
extent() const772 size_t C2FieldUtils::Info::extent() const {
773     return _mImpl->usedExtent;
774 }
775 
index() const776 size_t C2FieldUtils::Info::index() const {
777     return _mImpl->index;
778 }
779 
isArithmetic() const780 bool C2FieldUtils::Info::isArithmetic() const {
781     switch (_mImpl->field.type()) {
782     case C2FieldDescriptor::BLOB:
783     case C2FieldDescriptor::CNTR32:
784     case C2FieldDescriptor::CNTR64:
785     case C2FieldDescriptor::FLOAT:
786     case C2FieldDescriptor::INT32:
787     case C2FieldDescriptor::INT64:
788     case C2FieldDescriptor::STRING:
789     case C2FieldDescriptor::UINT32:
790     case C2FieldDescriptor::UINT64:
791         return true;
792     default:
793         return false;
794     }
795 }
796 
isFlexible() const797 bool C2FieldUtils::Info::isFlexible() const {
798     return _mImpl->field.extent() == 0;
799 }
800 
name() const801 C2String C2FieldUtils::Info::name() const {
802     return _mImpl->field.name();
803 }
804 
namedValues() const805 const C2FieldUtils::Info::NamedValuesType &C2FieldUtils::Info::namedValues() const {
806     return _mImpl->field.namedValues();
807 }
808 
offset() const809 size_t C2FieldUtils::Info::offset() const {
810     return _C2ParamInspector::GetOffset(_mImpl->field);
811 }
812 
parent() const813 C2FieldUtils::Info C2FieldUtils::Info::parent() const {
814     return Info(_mImpl->parent);
815 };
816 
size() const817 size_t C2FieldUtils::Info::size() const {
818     return _C2ParamInspector::GetSize(_mImpl->field);
819 }
820 
type() const821 C2FieldUtils::Info::type_t C2FieldUtils::Info::type() const {
822     return _mImpl->field.type();
823 }
824 
825 /* ------------------------- C2FieldUtils::Iterator ------------------------- */
826 
827 struct C2_HIDE C2FieldUtils::Iterator::Impl : public _C2ParamInspector {
828     Impl() = default;
829 
830     virtual ~Impl() = default;
831 
832     /// implements object equality
equalsC2FieldUtils::Iterator::Impl833     virtual bool equals(const std::shared_ptr<Impl> &other) const {
834         return other != nullptr && mHead == other->mHead;
835     };
836 
837     /// returns the info pointed to by this iterator
getC2FieldUtils::Iterator::Impl838     virtual value_type get() const {
839         return Info(mHead);
840     }
841 
842     /// increments this iterator
incrementC2FieldUtils::Iterator::Impl843     virtual void increment() {
844         // note: this cannot be abstract as we instantiate this for List::end(). increment to end()
845         // instead.
846         mHead.reset();
847     }
848 
849 protected:
ImplC2FieldUtils::Iterator::Impl850     Impl(std::shared_ptr<C2FieldUtils::Info::Impl> head)
851         : mHead(head) { }
852 
853     std::shared_ptr<Info::Impl> mHead; ///< current field
854 };
855 
Iterator(std::shared_ptr<Impl> impl)856 C2FieldUtils::Iterator::Iterator(std::shared_ptr<Impl> impl)
857     : mImpl(impl) { }
858 
operator *() const859 C2FieldUtils::Iterator::value_type C2FieldUtils::Iterator::operator*() const {
860     return mImpl->get();
861 }
862 
operator ++()863 C2FieldUtils::Iterator& C2FieldUtils::Iterator::operator++() {
864     mImpl->increment();
865     return *this;
866 }
867 
operator ==(const Iterator & other) const868 bool C2FieldUtils::Iterator::operator==(const Iterator &other) const {
869     return mImpl->equals(other.mImpl);
870 }
871 
872 /* ------------------------- C2FieldUtils::List ------------------------- */
873 
874 struct C2_HIDE C2FieldUtils::List::Impl {
875     virtual std::shared_ptr<Iterator::Impl> begin() const = 0;
876 
877     /// returns an iterator to the end of the list
endC2FieldUtils::List::Impl878     virtual std::shared_ptr<Iterator::Impl> end() const {
879         return std::make_shared<Iterator::Impl>();
880     }
881 
882     virtual ~Impl() = default;
883 };
884 
List(std::shared_ptr<Impl> impl)885 C2FieldUtils::List::List(std::shared_ptr<Impl> impl)
886     : mImpl(impl) { }
887 
begin() const888 C2FieldUtils::Iterator C2FieldUtils::List::begin() const {
889     return C2FieldUtils::Iterator(mImpl->begin());
890 }
891 
end() const892 C2FieldUtils::Iterator C2FieldUtils::List::end() const {
893     return C2FieldUtils::Iterator(mImpl->end());
894 }
895 
896 /* ------------------------- C2FieldUtils::enumerateFields ------------------------- */
897 
898 namespace {
899 
900 /**
901  * Iterator base class helper that allows descending into the field hierarchy.
902  */
903 struct C2FieldUtilsFieldsIteratorHelper : public C2FieldUtils::Iterator::Impl {
904     virtual ~C2FieldUtilsFieldsIteratorHelper() override = default;
905 
906     /// returns the base-field's offset of the parent field (or the param offset if no parent)
GetParentBaseFieldOffset__anonb94a135a0811::C2FieldUtilsFieldsIteratorHelper907     static inline uint32_t GetParentBaseFieldOffset(
908             const std::shared_ptr<C2FieldUtils::Info::Impl> parent) {
909         return parent == nullptr ? sizeof(C2Param) : parent->baseFieldOffset;
910     }
911 
912     /// returns the offset of the parent field (or the param)
GetParentOffset__anonb94a135a0811::C2FieldUtilsFieldsIteratorHelper913     static inline uint32_t GetParentOffset(const std::shared_ptr<C2FieldUtils::Info::Impl> parent) {
914         return parent == nullptr ? sizeof(C2Param) : GetOffset(parent->field);
915     }
916 
917 protected:
C2FieldUtilsFieldsIteratorHelper__anonb94a135a0811::C2FieldUtilsFieldsIteratorHelper918     C2FieldUtilsFieldsIteratorHelper(
919             std::shared_ptr<C2ParamReflector> reflector,
920             uint32_t paramSize,
921             std::shared_ptr<C2FieldUtils::Info::Impl> head = nullptr)
922         : C2FieldUtils::Iterator::Impl(head),
923           mParamSize(paramSize),
924           mReflector(reflector) { }
925 
926     /// returns a leaf info object at a specific index for a child field
makeLeaf__anonb94a135a0811::C2FieldUtilsFieldsIteratorHelper927     std::shared_ptr<C2FieldUtils::Info::Impl> makeLeaf(
928             const C2FieldDescriptor &field, uint32_t index) {
929         uint32_t parentOffset = GetParentOffset(mHead);
930         uint32_t arrayOffset = parentOffset + GetOffset(field);
931         uint32_t usedExtent = field.extent() ? :
932                 (std::max(arrayOffset, mParamSize) - arrayOffset) / GetSize(field);
933 
934         return std::make_shared<C2FieldUtils::Info::Impl>(
935                 OffsetFieldDescriptor(field, parentOffset + index * GetSize(field)),
936                 mHead /* parent */, index, mHead == nullptr ? 0 : mHead->depth + 1,
937                 GetParentBaseFieldOffset(mHead) + GetOffset(field),
938                 arrayOffset, usedExtent);
939     }
940 
941     /// returns whether this struct index have been traversed to get to this field
visited__anonb94a135a0811::C2FieldUtilsFieldsIteratorHelper942     bool visited(C2Param::CoreIndex index) const {
943         for (const std::shared_ptr<C2StructDescriptor> &sd : mHistory) {
944             if (sd->coreIndex() == index) {
945                 return true;
946             }
947         }
948         return false;
949     }
950 
951     uint32_t mParamSize;
952     std::shared_ptr<C2ParamReflector> mReflector;
953     std::vector<std::shared_ptr<C2StructDescriptor>> mHistory; // structure types visited
954 };
955 
956 /**
957  * Iterator implementing enumerateFields() that visits each base field.
958  */
959 struct C2FieldUtilsFieldsIterator : public C2FieldUtilsFieldsIteratorHelper {
960     /// enumerate base fields of a parameter
C2FieldUtilsFieldsIterator__anonb94a135a0811::C2FieldUtilsFieldsIterator961     C2FieldUtilsFieldsIterator(const C2Param &param, std::shared_ptr<C2ParamReflector> reflector)
962         : C2FieldUtilsFieldsIteratorHelper(reflector, param.size()) {
963         descendInto(param.coreIndex());
964     }
965 
966     /// enumerate base fields of a field
C2FieldUtilsFieldsIterator__anonb94a135a0811::C2FieldUtilsFieldsIterator967     C2FieldUtilsFieldsIterator(std::shared_ptr<C2FieldUtilsFieldsIterator> impl)
968         : C2FieldUtilsFieldsIteratorHelper(impl->mReflector, impl->mParamSize, impl->mHead) {
969         mHistory = impl->mHistory;
970         if (mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) {
971             C2Param::CoreIndex index = { mHead->field.type() &~C2FieldDescriptor::STRUCT_FLAG };
972             if (!visited(index)) {
973                 descendInto(index);
974             }
975         }
976     }
977 
978     virtual ~C2FieldUtilsFieldsIterator() override = default;
979 
980     /// Increments this iterator by visiting each base field.
increment__anonb94a135a0811::C2FieldUtilsFieldsIterator981     virtual void increment() override {
982         // don't go past end
983         if (mHead == nullptr || _mFields.empty()) {
984             return;
985         }
986 
987         // descend into structures
988         if (mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) {
989             C2Param::CoreIndex index = { mHead->field.type() &~C2FieldDescriptor::STRUCT_FLAG };
990             // do not recurse into the same structs
991             if (!visited(index) && descendInto(index)) {
992                 return;
993             }
994         }
995 
996         // ascend after the last field in the current struct
997         while (!mHistory.empty() && _mFields.back() == mHistory.back()->end()) {
998             mHead = mHead->parent;
999             mHistory.pop_back();
1000             _mFields.pop_back();
1001         }
1002 
1003         // done if history is now empty
1004         if (_mFields.empty()) {
1005             // we could be traversing a sub-tree so clear head
1006             mHead.reset();
1007             return;
1008         }
1009 
1010         // move to the next field in the current struct
1011         C2StructDescriptor::field_iterator next = _mFields.back();
1012         mHead->field = OffsetFieldDescriptor(*next, GetParentOffset(mHead->parent));
1013         mHead->index = 0; // reset index just in case for correctness
1014         mHead->baseFieldOffset = GetParentBaseFieldOffset(mHead->parent) + GetOffset(*next);
1015         mHead->arrayOffset = GetOffset(mHead->field);
1016         mHead->usedExtent = mHead->field.extent() ? :
1017                 (std::max(mHead->arrayOffset, mParamSize) - mHead->arrayOffset)
1018                         / GetSize(mHead->field);
1019         ++_mFields.back();
1020     }
1021 
1022 private:
1023     /// If the current field is a known, valid (untraversed) structure, it modifies this iterator
1024     /// to point to the first field of the structure and returns true. Otherwise, it does not
1025     /// modify this iterator and returns false.
descendInto__anonb94a135a0811::C2FieldUtilsFieldsIterator1026     bool descendInto(C2Param::CoreIndex index) {
1027         std::unique_ptr<C2StructDescriptor> descUnique = mReflector->describe(index);
1028         // descend into known structs (as long as they have at least one field)
1029         if (descUnique && descUnique->begin() != descUnique->end()) {
1030             std::shared_ptr<C2StructDescriptor> desc(std::move(descUnique));
1031             mHistory.emplace_back(desc);
1032             C2StructDescriptor::field_iterator first = desc->begin();
1033             mHead = makeLeaf(*first, 0 /* index */);
1034             _mFields.emplace_back(++first);
1035             return true;
1036         }
1037         return false;
1038     }
1039 
1040     /// next field pointers for each depth.
1041     /// note: _mFields may be shorted than mHistory, if iterating at a depth
1042     std::vector<C2StructDescriptor::field_iterator> _mFields;
1043 };
1044 
1045 /**
1046  * Iterable implementing enumerateFields().
1047  */
1048 struct C2FieldUtilsFieldIterable : public C2FieldUtils::List::Impl {
1049     /// returns an iterator to the beginning of the list
begin__anonb94a135a0811::C2FieldUtilsFieldIterable1050     virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
1051         return std::make_shared<C2FieldUtilsFieldsIterator>(*_mParam, _mReflector);
1052     };
1053 
C2FieldUtilsFieldIterable__anonb94a135a0811::C2FieldUtilsFieldIterable1054     C2FieldUtilsFieldIterable(const C2Param &param, std::shared_ptr<C2ParamReflector> reflector)
1055         : _mParam(&param), _mReflector(reflector) { }
1056 
1057 private:
1058     const C2Param *_mParam;
1059     std::shared_ptr<C2ParamReflector> _mReflector;
1060 };
1061 
1062 }
1063 
enumerateFields(const C2Param & param,const std::shared_ptr<C2ParamReflector> & reflector)1064 C2FieldUtils::List C2FieldUtils::enumerateFields(
1065         const C2Param &param, const std::shared_ptr<C2ParamReflector> &reflector) {
1066     return C2FieldUtils::List(std::make_shared<C2FieldUtilsFieldIterable>(param, reflector));
1067 }
1068 
1069 /* ------------------------- C2FieldUtils::enumerate siblings ------------------------- */
1070 
1071 namespace {
1072 
1073 struct C2FieldUtilsCousinsIterator : public C2FieldUtils::Iterator::Impl {
C2FieldUtilsCousinsIterator__anonb94a135a0911::C2FieldUtilsCousinsIterator1074     C2FieldUtilsCousinsIterator(
1075                 const std::shared_ptr<C2FieldUtils::Info::Impl> &info, size_t level)
1076           // clone info chain as this iterator will change it
1077         : C2FieldUtils::Iterator::Impl(C2FieldUtils::Info::Impl::Clone(info)) {
1078         if (level == 0) {
1079             return;
1080         }
1081 
1082         // store parent chain (up to level) for quick access
1083         std::shared_ptr<C2FieldUtils::Info::Impl> node = mHead;
1084         size_t ix = 0;
1085         for (; ix < level && node; ++ix) {
1086             node->index = 0;
1087             _mPath.emplace_back(node);
1088             node = node->parent;
1089         }
1090         setupPath(ix);
1091     }
1092 
1093     virtual ~C2FieldUtilsCousinsIterator() override = default;
1094 
1095     /// Increments this iterator by visiting each index.
increment__anonb94a135a0911::C2FieldUtilsCousinsIterator1096     virtual void increment() override {
1097         size_t ix = 0;
1098         while (ix < _mPath.size()) {
1099             if (++_mPath[ix]->index < _mPath[ix]->usedExtent) {
1100                 setupPath(ix + 1);
1101                 return;
1102             }
1103             _mPath[ix++]->index = 0;
1104         }
1105         mHead.reset();
1106     }
1107 
1108 private:
1109     /// adjusts field offsets along the path up to the specific level - 1.
1110     /// This in-fact has to be done down the path from parent to child as child fields must
1111     /// fall within parent fields.
setupPath__anonb94a135a0911::C2FieldUtilsCousinsIterator1112     void setupPath(size_t level) {
1113         C2_CHECK_LE(level, _mPath.size());
1114         uint32_t oldArrayOffset = level ? _mPath[level - 1]->arrayOffset : 0 /* unused */;
1115         while (level) {
1116             --level;
1117             C2FieldUtils::Info::Impl &path = *_mPath[level];
1118             uint32_t size = GetSize(path.field);
1119             uint32_t offset = path.arrayOffset + size * path.index;
1120             SetOffset(path.field, offset);
1121             if (level) {
1122                 // reset child's array offset to fall within current index, but hold onto the
1123                 // original value of the arrayOffset so that we can adjust subsequent children.
1124                 // This is because the modulo is only defined within the current array.
1125                 uint32_t childArrayOffset =
1126                     offset + (_mPath[level - 1]->arrayOffset - oldArrayOffset) % size;
1127                 oldArrayOffset = _mPath[level - 1]->arrayOffset;
1128                 _mPath[level - 1]->arrayOffset = childArrayOffset;
1129             }
1130         }
1131     }
1132 
1133     std::vector<std::shared_ptr<C2FieldUtils::Info::Impl>> _mPath;
1134 };
1135 
1136 /**
1137  * Iterable implementing enumerateFields().
1138  */
1139 struct C2FieldUtilsCousinsIterable : public C2FieldUtils::List::Impl {
1140     /// returns an iterator to the beginning of the list
begin__anonb94a135a0911::C2FieldUtilsCousinsIterable1141     virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
1142         return std::make_shared<C2FieldUtilsCousinsIterator>(_mHead, _mLevel);
1143     };
1144 
C2FieldUtilsCousinsIterable__anonb94a135a0911::C2FieldUtilsCousinsIterable1145     C2FieldUtilsCousinsIterable(const C2FieldUtils::Info &field, uint32_t level)
1146         : _mHead(C2FieldUtils::_Inspector::GetImpl(field)), _mLevel(level) { }
1147 
1148 private:
1149     std::shared_ptr<C2FieldUtils::Info::Impl> _mHead;
1150     size_t _mLevel;
1151 };
1152 
1153 }
1154 
enumerateCousins(const C2FieldUtils::Info & field,uint32_t level)1155 C2FieldUtils::List C2FieldUtils::enumerateCousins(const C2FieldUtils::Info &field, uint32_t level) {
1156     return C2FieldUtils::List(std::make_shared<C2FieldUtilsCousinsIterable>(field, level));
1157 }
1158 
1159 /* ------------------------- C2FieldUtils::locateField ------------------------- */
1160 
1161 namespace {
1162 
1163 /**
1164  * Iterator implementing locateField().
1165  */
1166 struct C2FieldUtilsFieldLocator : public C2FieldUtilsFieldsIteratorHelper {
C2FieldUtilsFieldLocator__anonb94a135a0a11::C2FieldUtilsFieldLocator1167     C2FieldUtilsFieldLocator(
1168             C2Param::CoreIndex index, const _C2FieldId &field, uint32_t paramSize,
1169             std::shared_ptr<C2ParamReflector> reflector)
1170         : C2FieldUtilsFieldsIteratorHelper(reflector, paramSize),
1171           _mField(field) {
1172         while (descendInto(index)) {
1173             if ((mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) == 0) {
1174                 break;
1175             }
1176             index = C2Param::CoreIndex(mHead->field.type() &~ C2FieldDescriptor::STRUCT_FLAG);
1177         }
1178     }
1179 
increment__anonb94a135a0a11::C2FieldUtilsFieldLocator1180     void increment() {
1181         mHead = _mTail;
1182         _mTail = nullptr;
1183     }
1184 
1185 private:
1186     /// If the current field is a known, valid (untraversed) structure, it modifies this iterator
1187     /// to point to the field at the beginning/end of the given field of the structure and returns
1188     /// true. Otherwise, including if no such field exists in the structure, it does not modify this
1189     /// iterator and returns false.
descendInto__anonb94a135a0a11::C2FieldUtilsFieldLocator1190     bool descendInto(C2Param::CoreIndex index) {
1191         // check that the boundaries of the field to be located are still within the same parent
1192         // field
1193         if (mHead != _mTail) {
1194             return false;
1195         }
1196 
1197         std::unique_ptr<C2StructDescriptor> descUnique = mReflector->describe(index);
1198         // descend into known structs (as long as they have at least one field)
1199         if (descUnique && descUnique->begin() != descUnique->end()) {
1200             std::shared_ptr<C2StructDescriptor> desc(std::move(descUnique));
1201             mHistory.emplace_back(desc);
1202 
1203             uint32_t parentOffset = GetParentOffset(mHead);
1204 
1205             // locate field using a dummy field descriptor
1206             C2FieldDescriptor dummy = {
1207                 C2FieldDescriptor::BLOB, 1 /* extent */, "name",
1208                 GetOffset(_mField) - parentOffset, GetSize(_mField)
1209             };
1210 
1211             // locate first field where offset is greater than dummy offset (which is one past)
1212             auto it = std::upper_bound(
1213                     desc->cbegin(), desc->cend(), dummy,
1214                     [](const C2FieldDescriptor &a, const C2FieldDescriptor &b) -> bool {
1215                 return _C2ParamInspector::GetOffset(a) < _C2ParamInspector::GetOffset(b);
1216             });
1217             if (it == desc->begin()) {
1218                 // field is prior to first field
1219                 return false;
1220             }
1221             --it;
1222             const C2FieldDescriptor &field = *it;
1223 
1224             // check that dummy end-offset is within this field
1225             uint32_t structSize = std::max(mParamSize, parentOffset) - parentOffset;
1226             if (GetEndOffset(dummy) > GetEndOffset(field, structSize)) {
1227                 return false;
1228             }
1229 
1230             uint32_t startIndex = (GetOffset(dummy) - GetOffset(field)) / GetSize(field);
1231             uint32_t endIndex =
1232                 (GetEndOffset(dummy) - GetOffset(field) + GetSize(field) - 1) / GetSize(field);
1233             if (endIndex > startIndex) {
1234                 // Field size could be zero, in which case end index is still on start index.
1235                 // However, for all other cases, endIndex was rounded up to the next index, so
1236                 // decrement it.
1237                 --endIndex;
1238             }
1239             std::shared_ptr<C2FieldUtils::Info::Impl> startLeaf =
1240                 makeLeaf(field, startIndex);
1241             if (endIndex == startIndex) {
1242                 _mTail = startLeaf;
1243                 mHead = startLeaf;
1244             } else {
1245                 _mTail = makeLeaf(field, endIndex);
1246                 mHead = startLeaf;
1247             }
1248             return true;
1249         }
1250         return false;
1251     }
1252 
1253     _C2FieldId _mField;
1254     std::shared_ptr<C2FieldUtils::Info::Impl> _mTail;
1255 };
1256 
1257 /**
1258  * Iterable implementing locateField().
1259  */
1260 struct C2FieldUtilsFieldLocation : public C2FieldUtils::List::Impl {
1261     /// returns an iterator to the beginning of the list
begin__anonb94a135a0a11::C2FieldUtilsFieldLocation1262     virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override {
1263         return std::make_shared<C2FieldUtilsFieldLocator>(
1264                 _mIndex, _mField, _mParamSize, _mReflector);
1265     };
1266 
C2FieldUtilsFieldLocation__anonb94a135a0a11::C2FieldUtilsFieldLocation1267     C2FieldUtilsFieldLocation(
1268             const C2ParamField &pf, std::shared_ptr<C2ParamReflector> reflector)
1269         : _mIndex(C2Param::CoreIndex(_C2ParamInspector::GetIndex(pf))),
1270           _mField(_C2ParamInspector::GetField(pf)),
1271           _mParamSize(0),
1272           _mReflector(reflector) { }
1273 
1274 
C2FieldUtilsFieldLocation__anonb94a135a0a11::C2FieldUtilsFieldLocation1275     C2FieldUtilsFieldLocation(
1276             const C2Param &param, const _C2FieldId &field,
1277             std::shared_ptr<C2ParamReflector> reflector)
1278         : _mIndex(param.coreIndex()),
1279           _mField(field),
1280           _mParamSize(param.size()),
1281           _mReflector(reflector) { }
1282 
1283 private:
1284     C2Param::CoreIndex _mIndex;
1285     _C2FieldId _mField;
1286     uint32_t _mParamSize;
1287     std::shared_ptr<C2ParamReflector> _mReflector;
1288 };
1289 
1290 }
1291 
locateField(const C2ParamField & pf,const std::shared_ptr<C2ParamReflector> & reflector)1292 std::vector<C2FieldUtils::Info> C2FieldUtils::locateField(
1293         const C2ParamField &pf, const std::shared_ptr<C2ParamReflector> &reflector) {
1294     C2FieldUtils::List location = { std::make_shared<C2FieldUtilsFieldLocation>(pf, reflector) };
1295     return std::vector<Info>(location.begin(), location.end());
1296 }
1297 
locateField(const C2Param & param,const _C2FieldId & field,const std::shared_ptr<C2ParamReflector> & reflector)1298 std::vector<C2FieldUtils::Info> C2FieldUtils::locateField(
1299         const C2Param &param, const _C2FieldId &field,
1300         const std::shared_ptr<C2ParamReflector> &reflector) {
1301     C2FieldUtils::List location = {
1302         std::make_shared<C2FieldUtilsFieldLocation>(param, field, reflector)
1303     };
1304     return std::vector<Info>(location.begin(), location.end());
1305 }
1306 
1307