| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wunused-parameter" |
| #pragma clang diagnostic ignored "-Wunused-variable" |
| #pragma clang diagnostic ignored "-Wunused-value" |
| |
| #define C2_LOG_VERBOSE |
| |
| #include <C2Debug.h> |
| #include <C2Param.h> |
| #include <C2ParamDef.h> |
| #include <C2ParamInternal.h> |
| #include <util/C2InterfaceUtils.h> |
| |
| #include <cmath> |
| #include <limits> |
| #include <map> |
| #include <type_traits> |
| |
| #include <android-base/stringprintf.h> |
| |
| std::ostream& operator<<(std::ostream& os, const _C2FieldId &i); |
| |
| std::ostream& operator<<(std::ostream& os, const C2ParamField &i); |
| |
| /* ---------------------------- C2SupportedRange ---------------------------- */ |
| |
| /** |
| * Helper class for supported values range calculations. |
| */ |
| template<typename T, bool FP=std::is_floating_point<T>::value> |
| struct _C2TypedSupportedRangeHelper { |
| /** |
| * type of range size: a - b if a >= b and a and b are of type T |
| */ |
| typedef typename std::make_unsigned<T>::type DiffType; |
| |
| /** |
| * calculate (high - low) mod step |
| */ |
| static DiffType mod(T low, T high, T step) { |
| return DiffType(high - low) % DiffType(step); |
| } |
| }; |
| |
| template<typename T> |
| struct _C2TypedSupportedRangeHelper<T, true> { |
| typedef T DiffType; |
| |
| static DiffType mod(T low, T high, T step) { |
| return fmod(high - low, step); |
| } |
| }; |
| |
| template<typename T> |
| C2SupportedRange<T>::C2SupportedRange(const C2FieldSupportedValues &values) { |
| if (values.type == C2FieldSupportedValues::RANGE) { |
| _mMin = values.range.min.ref<ValueType>(); |
| _mMax = values.range.max.ref<ValueType>(); |
| _mStep = values.range.step.ref<ValueType>(); |
| _mNum = values.range.num.ref<ValueType>(); |
| _mDenom = values.range.denom.ref<ValueType>(); |
| } else { |
| _mMin = MAX_VALUE; |
| _mMax = MIN_VALUE; |
| _mStep = MIN_STEP; |
| _mNum = 0; |
| _mDenom = 0; |
| } |
| } |
| |
| template<typename T> |
| bool C2SupportedRange<T>::contains(T value) const { |
| // value must fall between min and max |
| if (value < _mMin || value > _mMax) { |
| return false; |
| } |
| // simple ranges contain all values between min and max |
| if (isSimpleRange()) { |
| return true; |
| } |
| // min is always part of the range |
| if (value == _mMin) { |
| return true; |
| } |
| // stepped ranges require (val - min) % step to be zero |
| if (isArithmeticSeries()) { |
| return _C2TypedSupportedRangeHelper<T>::mod(_mMin, value, _mStep) == 0; |
| } |
| // pure geometric series require (val / min) to be integer multiple of (num/denom) |
| if (isGeometricSeries()) { |
| if (value <= 0) { |
| return false; |
| } |
| double log2base = log2(_mNum / _mDenom); |
| double power = llround(log2(value / double(_mMin)) / log2base); |
| // TODO: validate that result falls within precision (other than round) |
| return value == T(_mMin * pow(_mNum / _mDenom, power) + MIN_STEP / 2); |
| } |
| // multiply-accumulate series require validating by walking through the series |
| if (isMacSeries()) { |
| double lastValue = _mMin; |
| double base = _mNum / _mDenom; |
| while (true) { |
| // this cast is safe as _mMin <= lastValue <= _mMax |
| if (T(lastValue + MIN_STEP / 2) == value) { |
| return true; |
| } |
| double nextValue = fma(lastValue, base, _mStep); |
| if (nextValue <= lastValue || nextValue > _mMax) { |
| return false; // series is no longer monotonic or within range |
| } |
| lastValue = nextValue; |
| }; |
| } |
| // if we are here, this must be an invalid range |
| return false; |
| } |
| |
| template<typename T> |
| C2SupportedRange<T> C2SupportedRange<T>::limitedTo(const C2SupportedRange<T> &limit) const { |
| // TODO - this only works for simple ranges |
| return C2SupportedRange(std::max(_mMin, limit._mMin), std::min(_mMax, limit._mMax), |
| std::max(_mStep, limit._mStep)); |
| } |
| |
| template class C2SupportedRange<uint8_t>; |
| template class C2SupportedRange<char>; |
| template class C2SupportedRange<int32_t>; |
| template class C2SupportedRange<uint32_t>; |
| //template class C2SupportedRange<c2_cntr32_t>; |
| template class C2SupportedRange<int64_t>; |
| template class C2SupportedRange<uint64_t>; |
| //template class C2SupportedRange<c2_cntr64_t>; |
| template class C2SupportedRange<float>; |
| |
| /* -------------------------- C2SupportedFlags -------------------------- */ |
| |
| /** |
| * Ordered supported flag set for a field of a given type. |
| */ |
| // float flags are not supported, but define a few methods to support generic supported values code |
| template<> |
| bool C2SupportedFlags<float>::contains(float value) const { |
| return false; |
| } |
| |
| template<> |
| const std::vector<float> C2SupportedFlags<float>::flags() const { |
| return std::vector<float>(); |
| } |
| |
| template<> |
| C2SupportedFlags<float> C2SupportedFlags<float>::limitedTo(const C2SupportedFlags<float> &limit) const { |
| std::vector<C2Value::Primitive> values; |
| return C2SupportedFlags(std::move(values)); |
| } |
| |
| template<> |
| float C2SupportedFlags<float>::min() const { |
| return 0; |
| } |
| |
| template<typename T> |
| bool C2SupportedFlags<T>::contains(T value) const { |
| // value must contain the minimal mask |
| T minMask = min(); |
| if (~value & minMask) { |
| return false; |
| } |
| value &= ~minMask; |
| // otherwise, remove flags from value and see if we arrive at 0 |
| for (const C2Value::Primitive &v : _mValues) { |
| if (value == 0) { |
| break; |
| } |
| if ((~value & v.ref<ValueType>()) == 0) { |
| value &= ~v.ref<ValueType>(); |
| } |
| } |
| return value == 0; |
| } |
| |
| template<typename T> |
| const std::vector<T> C2SupportedFlags<T>::flags() const { |
| std::vector<T> vals(c2_max(_mValues.size(), 1u) - 1); |
| if (!_mValues.empty()) { |
| std::transform(_mValues.cbegin() + 1, _mValues.cend(), vals.begin(), |
| [](const C2Value::Primitive &p)->T { |
| return p.ref<ValueType>(); |
| }); |
| } |
| return vals; |
| } |
| |
| template<typename T> |
| C2SupportedFlags<T> C2SupportedFlags<T>::limitedTo(const C2SupportedFlags<T> &limit) const { |
| std::vector<C2Value::Primitive> values = _mValues; // make a copy |
| T minMask = min() | limit.min(); |
| // minimum mask must be covered by both this and other |
| if (limit.contains(minMask) && contains(minMask)) { |
| values[0] = minMask; |
| // keep only flags that are covered by limit |
| std::remove_if(values.begin(), values.end(), [&limit, minMask](const C2Value::Primitive &v) -> bool { |
| T value = v.ref<ValueType>() | minMask; |
| return value == minMask || !limit.contains(value); }); |
| // we also need to do it vice versa |
| for (const C2Value::Primitive &v : _mValues) { |
| T value = v.ref<ValueType>() | minMask; |
| if (value != minMask && contains(value)) { |
| values.emplace_back((ValueType)value); |
| } |
| } |
| } |
| return C2SupportedFlags(std::move(values)); |
| } |
| |
| template<typename T> |
| T C2SupportedFlags<T>::min() const { |
| if (!_mValues.empty()) { |
| return _mValues.front().template ref<ValueType>(); |
| } else { |
| return T(0); |
| } |
| } |
| |
| template class C2SupportedFlags<uint8_t>; |
| template class C2SupportedFlags<char>; |
| template class C2SupportedFlags<int32_t>; |
| template class C2SupportedFlags<uint32_t>; |
| //template class C2SupportedFlags<c2_cntr32_t>; |
| template class C2SupportedFlags<int64_t>; |
| template class C2SupportedFlags<uint64_t>; |
| //template class C2SupportedFlags<c2_cntr64_t>; |
| |
| /* -------------------------- C2SupportedValueSet -------------------------- */ |
| |
| /** |
| * Ordered supported value set for a field of a given type. |
| */ |
| template<typename T> |
| bool C2SupportedValueSet<T>::contains(T value) const { |
| return std::find_if(_mValues.cbegin(), _mValues.cend(), |
| [value](const C2Value::Primitive &p) -> bool { |
| return value == p.ref<ValueType>(); |
| }) != _mValues.cend(); |
| } |
| |
| template<typename T> |
| C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedValueSet<T> &limit) const { |
| std::vector<C2Value::Primitive> values = _mValues; // make a copy |
| std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool { |
| return !limit.contains(v.ref<ValueType>()); }); |
| return C2SupportedValueSet(std::move(values)); |
| } |
| |
| template<typename T> |
| C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedRange<T> &limit) const { |
| std::vector<C2Value::Primitive> values = _mValues; // make a copy |
| std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool { |
| return !limit.contains(v.ref<ValueType>()); }); |
| return C2SupportedValueSet(std::move(values)); |
| } |
| |
| template<typename T> |
| C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedFlags<T> &limit) const { |
| std::vector<C2Value::Primitive> values = _mValues; // make a copy |
| std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool { |
| return !limit.contains(v.ref<ValueType>()); }); |
| return C2SupportedValueSet(std::move(values)); |
| } |
| |
| template<typename T> |
| const std::vector<T> C2SupportedValueSet<T>::values() const { |
| std::vector<T> vals(_mValues.size()); |
| std::transform(_mValues.cbegin(), _mValues.cend(), vals.begin(), [](const C2Value::Primitive &p) -> T { |
| return p.ref<ValueType>(); |
| }); |
| return vals; |
| } |
| |
| template class C2SupportedValueSet<uint8_t>; |
| template class C2SupportedValueSet<char>; |
| template class C2SupportedValueSet<int32_t>; |
| template class C2SupportedValueSet<uint32_t>; |
| //template class C2SupportedValueSet<c2_cntr32_t>; |
| template class C2SupportedValueSet<int64_t>; |
| template class C2SupportedValueSet<uint64_t>; |
| //template class C2SupportedValueSet<c2_cntr64_t>; |
| template class C2SupportedValueSet<float>; |
| |
| /* ---------------------- C2FieldSupportedValuesHelper ---------------------- */ |
| |
| template<typename T> |
| struct C2FieldSupportedValuesHelper<T>::Impl { |
| Impl(const C2FieldSupportedValues &values) |
| : _mType(values.type), |
| _mRange(values), |
| _mValues(values), |
| _mFlags(values) { } |
| |
| bool supports(T value) const; |
| |
| private: |
| typedef typename _C2FieldValueHelper<T>::ValueType ValueType; |
| C2FieldSupportedValues::type_t _mType; |
| C2SupportedRange<ValueType> _mRange; |
| C2SupportedValueSet<ValueType> _mValues; |
| C2SupportedValueSet<ValueType> _mFlags; |
| |
| // friend std::ostream& operator<< <T>(std::ostream& os, const C2FieldSupportedValuesHelper<T>::Impl &i); |
| // friend std::ostream& operator<<(std::ostream& os, const Impl &i); |
| std::ostream& streamOut(std::ostream& os) const; |
| }; |
| |
| template<typename T> |
| bool C2FieldSupportedValuesHelper<T>::Impl::supports(T value) const { |
| switch (_mType) { |
| case C2FieldSupportedValues::RANGE: return _mRange.contains(value); |
| case C2FieldSupportedValues::VALUES: return _mValues.contains(value); |
| case C2FieldSupportedValues::FLAGS: return _mFlags.contains(value); |
| default: return false; |
| } |
| } |
| |
| template<typename T> |
| C2FieldSupportedValuesHelper<T>::C2FieldSupportedValuesHelper(const C2FieldSupportedValues &values) |
| : _mImpl(std::make_unique<C2FieldSupportedValuesHelper<T>::Impl>(values)) { } |
| |
| template<typename T> |
| C2FieldSupportedValuesHelper<T>::~C2FieldSupportedValuesHelper() = default; |
| |
| template<typename T> |
| bool C2FieldSupportedValuesHelper<T>::supports(T value) const { |
| return _mImpl->supports(value); |
| } |
| |
| template class C2FieldSupportedValuesHelper<uint8_t>; |
| template class C2FieldSupportedValuesHelper<char>; |
| template class C2FieldSupportedValuesHelper<int32_t>; |
| template class C2FieldSupportedValuesHelper<uint32_t>; |
| //template class C2FieldSupportedValuesHelper<c2_cntr32_t>; |
| template class C2FieldSupportedValuesHelper<int64_t>; |
| template class C2FieldSupportedValuesHelper<uint64_t>; |
| //template class C2FieldSupportedValuesHelper<c2_cntr64_t>; |
| template class C2FieldSupportedValuesHelper<float>; |
| |
| /* ----------------------- C2ParamFieldValuesBuilder ----------------------- */ |
| |
| template<typename T> |
| struct C2ParamFieldValuesBuilder<T>::Impl { |
| Impl(const C2ParamField &field) |
| : _mParamField(field), |
| _mType(type_t::RANGE), |
| _mDefined(false), |
| _mRange(C2SupportedRange<T>::Any()), |
| _mValues(C2SupportedValueSet<T>::None()), |
| _mFlags(C2SupportedFlags<T>::None()) { } |
| |
| /** |
| * Get C2ParamFieldValues from this builder. |
| */ |
| operator C2ParamFieldValues() const { |
| if (!_mDefined) { |
| return C2ParamFieldValues(_mParamField); |
| } |
| switch (_mType) { |
| case type_t::EMPTY: |
| case type_t::VALUES: |
| return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mValues); |
| case type_t::RANGE: |
| return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mRange); |
| case type_t::FLAGS: |
| return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mFlags); |
| default: |
| // TRESPASS |
| // should never get here |
| return C2ParamFieldValues(_mParamField); |
| } |
| } |
| |
| /** Define the supported values as the currently supported values of this builder. */ |
| void any() { |
| _mDefined = true; |
| } |
| |
| /** Restrict (and thus define) the supported values to none. */ |
| void none() { |
| _mDefined = true; |
| _mType = type_t::VALUES; |
| _mValues.clear(); |
| } |
| |
| /** Restrict (and thus define) the supported values to |value| alone. */ |
| void equalTo(T value) { |
| return limitTo(C2SupportedValueSet<T>::OneOf({value})); |
| } |
| |
| /** Restrict (and thus define) the supported values to a value set. */ |
| void limitTo(const C2SupportedValueSet<T> &limit) { |
| if (!_mDefined) { |
| C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| |
| // shortcut for first limit applied |
| _mDefined = true; |
| _mValues = limit; |
| _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES; |
| } else { |
| switch (_mType) { |
| case type_t::EMPTY: |
| case type_t::VALUES: |
| C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo(" |
| << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| |
| _mValues = _mValues.limitedTo(limit); |
| _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES; |
| break; |
| case type_t::RANGE: |
| C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo(" |
| << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| |
| _mValues = limit.limitedTo(_mRange); |
| _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES; |
| break; |
| case type_t::FLAGS: |
| C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo(" |
| << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| |
| _mValues = limit.limitedTo(_mFlags); |
| _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES; |
| break; |
| default: |
| C2_LOG(FATAL); // should not be here |
| } |
| // TODO: support flags |
| } |
| C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues); |
| } |
| |
| /** Restrict (and thus define) the supported values to a flag set. */ |
| void limitTo(const C2SupportedFlags<T> &limit) { |
| if (!_mDefined) { |
| C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| |
| // shortcut for first limit applied |
| _mDefined = true; |
| _mFlags = limit; |
| _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS; |
| } else { |
| switch (_mType) { |
| case type_t::EMPTY: |
| case type_t::VALUES: |
| C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo(" |
| << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| |
| _mValues = _mValues.limitedTo(limit); |
| _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES; |
| C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues); |
| break; |
| case type_t::FLAGS: |
| C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mFlags) << ").limitTo(" |
| << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| |
| _mFlags = _mFlags.limitedTo(limit); |
| _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS; |
| C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mFlags); |
| break; |
| case type_t::RANGE: |
| C2_LOG(FATAL) << "limiting ranges to flags is not supported"; |
| _mType = type_t::EMPTY; |
| break; |
| default: |
| C2_LOG(FATAL); // should not be here |
| } |
| } |
| } |
| |
| void limitTo(const C2SupportedRange<T> &limit) { |
| if (!_mDefined) { |
| C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| |
| // shortcut for first limit applied |
| _mDefined = true; |
| _mRange = limit; |
| _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE; |
| C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange); |
| } else { |
| switch (_mType) { |
| case type_t::EMPTY: |
| case type_t::VALUES: |
| C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo(" |
| << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| _mValues = _mValues.limitedTo(limit); |
| _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES; |
| C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues); |
| break; |
| case type_t::FLAGS: |
| C2_LOG(FATAL) << "limiting flags to ranges is not supported"; |
| _mType = type_t::EMPTY; |
| break; |
| case type_t::RANGE: |
| C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo(" |
| << C2FieldSupportedValuesHelper<T>(limit) << ")"; |
| _mRange = _mRange.limitedTo(limit); |
| C2_DCHECK(_mValues.isEmpty()); |
| _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE; |
| C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange); |
| break; |
| default: |
| C2_LOG(FATAL); // should not be here |
| } |
| } |
| } |
| |
| private: |
| void instantiate() __unused { |
| (void)_mValues.values(); // instantiate non-const values() |
| } |
| |
| void instantiate() const __unused { |
| (void)_mValues.values(); // instantiate const values() |
| } |
| |
| typedef C2FieldSupportedValues::type_t type_t; |
| |
| C2ParamField _mParamField; |
| type_t _mType; |
| bool _mDefined; |
| C2SupportedRange<T> _mRange; |
| C2SupportedValueSet<T> _mValues; |
| C2SupportedFlags<T> _mFlags; |
| |
| }; |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T>::operator C2ParamFieldValues() const { |
| return (C2ParamFieldValues)(*_mImpl.get()); |
| } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamField &field) |
| : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(field)) { } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::any() { |
| _mImpl->any(); |
| return *this; |
| } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::none() { |
| _mImpl->none(); |
| return *this; |
| } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::equalTo(T value) { |
| _mImpl->equalTo(value); |
| return *this; |
| } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedValueSet<T> &limit) { |
| _mImpl->limitTo(limit); |
| return *this; |
| } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedFlags<T> &limit) { |
| _mImpl->limitTo(limit); |
| return *this; |
| } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedRange<T> &limit) { |
| _mImpl->limitTo(limit); |
| return *this; |
| } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamFieldValuesBuilder<T> &other) |
| : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get())) { } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::operator=( |
| const C2ParamFieldValuesBuilder<T> &other) { |
| _mImpl = std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get()); |
| return *this; |
| } |
| |
| template<typename T> |
| C2ParamFieldValuesBuilder<T>::~C2ParamFieldValuesBuilder() = default; |
| |
| template class C2ParamFieldValuesBuilder<uint8_t>; |
| template class C2ParamFieldValuesBuilder<char>; |
| template class C2ParamFieldValuesBuilder<int32_t>; |
| template class C2ParamFieldValuesBuilder<uint32_t>; |
| //template class C2ParamFieldValuesBuilder<c2_cntr32_t>; |
| template class C2ParamFieldValuesBuilder<int64_t>; |
| template class C2ParamFieldValuesBuilder<uint64_t>; |
| //template class C2ParamFieldValuesBuilder<c2_cntr64_t>; |
| template class C2ParamFieldValuesBuilder<float>; |
| |
| /* ------------------------- C2SettingResultBuilder ------------------------- */ |
| |
| C2SettingConflictsBuilder::C2SettingConflictsBuilder() : _mConflicts() { } |
| |
| C2SettingConflictsBuilder::C2SettingConflictsBuilder(C2ParamFieldValues &&conflict) { |
| _mConflicts.emplace_back(std::move(conflict)); |
| } |
| |
| C2SettingConflictsBuilder& C2SettingConflictsBuilder::with(C2ParamFieldValues &&conflict) { |
| _mConflicts.emplace_back(std::move(conflict)); |
| return *this; |
| } |
| |
| std::vector<C2ParamFieldValues> C2SettingConflictsBuilder::retrieveConflicts() { |
| return std::move(_mConflicts); |
| } |
| |
| /* ------------------------- C2SettingResult/sBuilder ------------------------- */ |
| |
| C2SettingResult C2SettingResultBuilder::ReadOnly(const C2ParamField ¶m) { |
| return C2SettingResult { C2SettingResult::READ_ONLY, { param }, { } }; |
| } |
| |
| C2SettingResult C2SettingResultBuilder::BadValue(const C2ParamField ¶mField, bool isInfo) { |
| return { isInfo ? C2SettingResult::INFO_BAD_VALUE : C2SettingResult::BAD_VALUE, |
| { paramField }, { } }; |
| } |
| |
| C2SettingResult C2SettingResultBuilder::Conflict( |
| C2ParamFieldValues &¶mFieldValues, C2SettingConflictsBuilder &conflicts, bool isInfo) { |
| C2_CHECK(!conflicts.empty()); |
| if (isInfo) { |
| return C2SettingResult { |
| C2SettingResult::INFO_CONFLICT, |
| std::move(paramFieldValues), conflicts.retrieveConflicts() |
| }; |
| } else { |
| return C2SettingResult { |
| C2SettingResult::CONFLICT, |
| std::move(paramFieldValues), conflicts.retrieveConflicts() |
| }; |
| } |
| } |
| |
| C2SettingResultsBuilder::C2SettingResultsBuilder(C2SettingResult &&result) |
| : _mStatus(C2_BAD_VALUE) { |
| _mResults.emplace_back(new C2SettingResult(std::move(result))); |
| } |
| |
| C2SettingResultsBuilder C2SettingResultsBuilder::plus(C2SettingResultsBuilder&& results) { |
| for (std::unique_ptr<C2SettingResult> &r : results._mResults) { |
| _mResults.emplace_back(std::move(r)); |
| } |
| results._mResults.clear(); |
| // TODO: mStatus |
| return std::move(*this); |
| } |
| |
| c2_status_t C2SettingResultsBuilder::retrieveFailures( |
| std::vector<std::unique_ptr<C2SettingResult>>* const failures) { |
| for (std::unique_ptr<C2SettingResult> &r : _mResults) { |
| failures->emplace_back(std::move(r)); |
| } |
| _mResults.clear(); |
| return _mStatus; |
| } |
| |
| C2SettingResultsBuilder::C2SettingResultsBuilder(c2_status_t status) : _mStatus(status) { |
| // status must be one of OK, BAD_STATE, TIMED_OUT or CORRUPTED |
| // mainly: BLOCKING, BAD_INDEX, BAD_VALUE and NO_MEMORY requires a setting attempt |
| } |
| |
| #pragma clang diagnostic pop |
| |
| /* ------------------------- C2FieldUtils ------------------------- */ |
| |
| struct C2_HIDE C2FieldUtils::_Inspector { |
| /// returns the implementation object |
| inline static std::shared_ptr<Info::Impl> GetImpl(const Info &info) { |
| return info._mImpl; |
| } |
| }; |
| |
| /* ------------------------- C2FieldUtils::Info ------------------------- */ |
| |
| struct C2_HIDE C2FieldUtils::Info::Impl { |
| C2FieldDescriptor field; |
| std::shared_ptr<Impl> parent; |
| uint32_t index; |
| uint32_t depth; |
| uint32_t baseFieldOffset; |
| uint32_t arrayOffset; |
| uint32_t usedExtent; |
| |
| /// creates a copy of this object including copies of its parent chain |
| Impl clone() const; |
| |
| /// creates a copy of a shared pointer to an object |
| static std::shared_ptr<Impl> Clone(const std::shared_ptr<Impl> &); |
| |
| Impl(const C2FieldDescriptor &field_, std::shared_ptr<Impl> parent_, |
| uint32_t index_, uint32_t depth_, uint32_t baseFieldOffset_, |
| uint32_t arrayOffset_, uint32_t usedExtent_) |
| : field(field_), parent(parent_), index(index_), depth(depth_), |
| baseFieldOffset(baseFieldOffset_), arrayOffset(arrayOffset_), usedExtent(usedExtent_) { } |
| }; |
| |
| std::shared_ptr<C2FieldUtils::Info::Impl> C2FieldUtils::Info::Impl::Clone(const std::shared_ptr<Impl> &info) { |
| if (info) { |
| return std::make_shared<Impl>(info->clone()); |
| } |
| return nullptr; |
| } |
| |
| C2FieldUtils::Info::Impl C2FieldUtils::Info::Impl::clone() const { |
| Impl res = Impl(*this); |
| res.parent = Clone(res.parent); |
| return res; |
| } |
| |
| C2FieldUtils::Info::Info(std::shared_ptr<Impl> impl) |
| : _mImpl(impl) { } |
| |
| size_t C2FieldUtils::Info::arrayOffset() const { |
| return _mImpl->arrayOffset; |
| } |
| |
| size_t C2FieldUtils::Info::arraySize() const { |
| return extent() * size(); |
| } |
| |
| size_t C2FieldUtils::Info::baseFieldOffset() const { |
| return _mImpl->baseFieldOffset; |
| }; |
| |
| size_t C2FieldUtils::Info::depth() const { |
| return _mImpl->depth; |
| } |
| |
| size_t C2FieldUtils::Info::extent() const { |
| return _mImpl->usedExtent; |
| } |
| |
| size_t C2FieldUtils::Info::index() const { |
| return _mImpl->index; |
| } |
| |
| bool C2FieldUtils::Info::isArithmetic() const { |
| switch (_mImpl->field.type()) { |
| case C2FieldDescriptor::BLOB: |
| case C2FieldDescriptor::CNTR32: |
| case C2FieldDescriptor::CNTR64: |
| case C2FieldDescriptor::FLOAT: |
| case C2FieldDescriptor::INT32: |
| case C2FieldDescriptor::INT64: |
| case C2FieldDescriptor::STRING: |
| case C2FieldDescriptor::UINT32: |
| case C2FieldDescriptor::UINT64: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool C2FieldUtils::Info::isFlexible() const { |
| return _mImpl->field.extent() == 0; |
| } |
| |
| C2String C2FieldUtils::Info::name() const { |
| return _mImpl->field.name(); |
| } |
| |
| const C2FieldUtils::Info::NamedValuesType &C2FieldUtils::Info::namedValues() const { |
| return _mImpl->field.namedValues(); |
| } |
| |
| size_t C2FieldUtils::Info::offset() const { |
| return _C2ParamInspector::GetOffset(_mImpl->field); |
| } |
| |
| C2FieldUtils::Info C2FieldUtils::Info::parent() const { |
| return Info(_mImpl->parent); |
| }; |
| |
| size_t C2FieldUtils::Info::size() const { |
| return _C2ParamInspector::GetSize(_mImpl->field); |
| } |
| |
| C2FieldUtils::Info::type_t C2FieldUtils::Info::type() const { |
| return _mImpl->field.type(); |
| } |
| |
| /* ------------------------- C2FieldUtils::Iterator ------------------------- */ |
| |
| struct C2_HIDE C2FieldUtils::Iterator::Impl : public _C2ParamInspector { |
| Impl() = default; |
| |
| virtual ~Impl() = default; |
| |
| /// implements object equality |
| virtual bool equals(const std::shared_ptr<Impl> &other) const { |
| return other != nullptr && mHead == other->mHead; |
| }; |
| |
| /// returns the info pointed to by this iterator |
| virtual value_type get() const { |
| return Info(mHead); |
| } |
| |
| /// increments this iterator |
| virtual void increment() { |
| // note: this cannot be abstract as we instantiate this for List::end(). increment to end() |
| // instead. |
| mHead.reset(); |
| } |
| |
| protected: |
| Impl(std::shared_ptr<C2FieldUtils::Info::Impl> head) |
| : mHead(head) { } |
| |
| std::shared_ptr<Info::Impl> mHead; ///< current field |
| }; |
| |
| C2FieldUtils::Iterator::Iterator(std::shared_ptr<Impl> impl) |
| : mImpl(impl) { } |
| |
| C2FieldUtils::Iterator::value_type C2FieldUtils::Iterator::operator*() const { |
| return mImpl->get(); |
| } |
| |
| C2FieldUtils::Iterator& C2FieldUtils::Iterator::operator++() { |
| mImpl->increment(); |
| return *this; |
| } |
| |
| bool C2FieldUtils::Iterator::operator==(const Iterator &other) const { |
| return mImpl->equals(other.mImpl); |
| } |
| |
| /* ------------------------- C2FieldUtils::List ------------------------- */ |
| |
| struct C2_HIDE C2FieldUtils::List::Impl { |
| virtual std::shared_ptr<Iterator::Impl> begin() const = 0; |
| |
| /// returns an iterator to the end of the list |
| virtual std::shared_ptr<Iterator::Impl> end() const { |
| return std::make_shared<Iterator::Impl>(); |
| } |
| |
| virtual ~Impl() = default; |
| }; |
| |
| C2FieldUtils::List::List(std::shared_ptr<Impl> impl) |
| : mImpl(impl) { } |
| |
| C2FieldUtils::Iterator C2FieldUtils::List::begin() const { |
| return C2FieldUtils::Iterator(mImpl->begin()); |
| } |
| |
| C2FieldUtils::Iterator C2FieldUtils::List::end() const { |
| return C2FieldUtils::Iterator(mImpl->end()); |
| } |
| |
| /* ------------------------- C2FieldUtils::enumerateFields ------------------------- */ |
| |
| namespace { |
| |
| /** |
| * Iterator base class helper that allows descending into the field hierarchy. |
| */ |
| struct C2FieldUtilsFieldsIteratorHelper : public C2FieldUtils::Iterator::Impl { |
| virtual ~C2FieldUtilsFieldsIteratorHelper() override = default; |
| |
| /// returns the base-field's offset of the parent field (or the param offset if no parent) |
| static inline uint32_t GetParentBaseFieldOffset( |
| const std::shared_ptr<C2FieldUtils::Info::Impl> parent) { |
| return parent == nullptr ? sizeof(C2Param) : parent->baseFieldOffset; |
| } |
| |
| /// returns the offset of the parent field (or the param) |
| static inline uint32_t GetParentOffset(const std::shared_ptr<C2FieldUtils::Info::Impl> parent) { |
| return parent == nullptr ? sizeof(C2Param) : GetOffset(parent->field); |
| } |
| |
| protected: |
| C2FieldUtilsFieldsIteratorHelper( |
| std::shared_ptr<C2ParamReflector> reflector, |
| uint32_t paramSize, |
| std::shared_ptr<C2FieldUtils::Info::Impl> head = nullptr) |
| : C2FieldUtils::Iterator::Impl(head), |
| mParamSize(paramSize), |
| mReflector(reflector) { } |
| |
| /// returns a leaf info object at a specific index for a child field |
| std::shared_ptr<C2FieldUtils::Info::Impl> makeLeaf( |
| const C2FieldDescriptor &field, uint32_t index) { |
| uint32_t parentOffset = GetParentOffset(mHead); |
| uint32_t arrayOffset = parentOffset + GetOffset(field); |
| uint32_t usedExtent = field.extent() ? : |
| (std::max(arrayOffset, mParamSize) - arrayOffset) / GetSize(field); |
| |
| return std::make_shared<C2FieldUtils::Info::Impl>( |
| OffsetFieldDescriptor(field, parentOffset + index * GetSize(field)), |
| mHead /* parent */, index, mHead == nullptr ? 0 : mHead->depth + 1, |
| GetParentBaseFieldOffset(mHead) + GetOffset(field), |
| arrayOffset, usedExtent); |
| } |
| |
| /// returns whether this struct index have been traversed to get to this field |
| bool visited(C2Param::CoreIndex index) const { |
| for (const std::shared_ptr<C2StructDescriptor> &sd : mHistory) { |
| if (sd->coreIndex() == index) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| uint32_t mParamSize; |
| std::shared_ptr<C2ParamReflector> mReflector; |
| std::vector<std::shared_ptr<C2StructDescriptor>> mHistory; // structure types visited |
| }; |
| |
| /** |
| * Iterator implementing enumerateFields() that visits each base field. |
| */ |
| struct C2FieldUtilsFieldsIterator : public C2FieldUtilsFieldsIteratorHelper { |
| /// enumerate base fields of a parameter |
| C2FieldUtilsFieldsIterator(const C2Param ¶m, std::shared_ptr<C2ParamReflector> reflector) |
| : C2FieldUtilsFieldsIteratorHelper(reflector, param.size()) { |
| descendInto(param.coreIndex()); |
| } |
| |
| /// enumerate base fields of a field |
| C2FieldUtilsFieldsIterator(std::shared_ptr<C2FieldUtilsFieldsIterator> impl) |
| : C2FieldUtilsFieldsIteratorHelper(impl->mReflector, impl->mParamSize, impl->mHead) { |
| mHistory = impl->mHistory; |
| if (mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) { |
| C2Param::CoreIndex index = { mHead->field.type() &~C2FieldDescriptor::STRUCT_FLAG }; |
| if (!visited(index)) { |
| descendInto(index); |
| } |
| } |
| } |
| |
| virtual ~C2FieldUtilsFieldsIterator() override = default; |
| |
| /// Increments this iterator by visiting each base field. |
| virtual void increment() override { |
| // don't go past end |
| if (mHead == nullptr || _mFields.empty()) { |
| return; |
| } |
| |
| // descend into structures |
| if (mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) { |
| C2Param::CoreIndex index = { mHead->field.type() &~C2FieldDescriptor::STRUCT_FLAG }; |
| // do not recurse into the same structs |
| if (!visited(index) && descendInto(index)) { |
| return; |
| } |
| } |
| |
| // ascend after the last field in the current struct |
| while (!mHistory.empty() && _mFields.back() == mHistory.back()->end()) { |
| mHead = mHead->parent; |
| mHistory.pop_back(); |
| _mFields.pop_back(); |
| } |
| |
| // done if history is now empty |
| if (_mFields.empty()) { |
| // we could be traversing a sub-tree so clear head |
| mHead.reset(); |
| return; |
| } |
| |
| // move to the next field in the current struct |
| C2StructDescriptor::field_iterator next = _mFields.back(); |
| mHead->field = OffsetFieldDescriptor(*next, GetParentOffset(mHead->parent)); |
| mHead->index = 0; // reset index just in case for correctness |
| mHead->baseFieldOffset = GetParentBaseFieldOffset(mHead->parent) + GetOffset(*next); |
| mHead->arrayOffset = GetOffset(mHead->field); |
| mHead->usedExtent = mHead->field.extent() ? : |
| (std::max(mHead->arrayOffset, mParamSize) - mHead->arrayOffset) |
| / GetSize(mHead->field); |
| ++_mFields.back(); |
| } |
| |
| private: |
| /// If the current field is a known, valid (untraversed) structure, it modifies this iterator |
| /// to point to the first field of the structure and returns true. Otherwise, it does not |
| /// modify this iterator and returns false. |
| bool descendInto(C2Param::CoreIndex index) { |
| std::unique_ptr<C2StructDescriptor> descUnique = mReflector->describe(index); |
| // descend into known structs (as long as they have at least one field) |
| if (descUnique && descUnique->begin() != descUnique->end()) { |
| std::shared_ptr<C2StructDescriptor> desc(std::move(descUnique)); |
| mHistory.emplace_back(desc); |
| C2StructDescriptor::field_iterator first = desc->begin(); |
| mHead = makeLeaf(*first, 0 /* index */); |
| _mFields.emplace_back(++first); |
| return true; |
| } |
| return false; |
| } |
| |
| /// next field pointers for each depth. |
| /// note: _mFields may be shorted than mHistory, if iterating at a depth |
| std::vector<C2StructDescriptor::field_iterator> _mFields; |
| }; |
| |
| /** |
| * Iterable implementing enumerateFields(). |
| */ |
| struct C2FieldUtilsFieldIterable : public C2FieldUtils::List::Impl { |
| /// returns an iterator to the beginning of the list |
| virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override { |
| return std::make_shared<C2FieldUtilsFieldsIterator>(*_mParam, _mReflector); |
| }; |
| |
| C2FieldUtilsFieldIterable(const C2Param ¶m, std::shared_ptr<C2ParamReflector> reflector) |
| : _mParam(¶m), _mReflector(reflector) { } |
| |
| private: |
| const C2Param *_mParam; |
| std::shared_ptr<C2ParamReflector> _mReflector; |
| }; |
| |
| } |
| |
| C2FieldUtils::List C2FieldUtils::enumerateFields( |
| const C2Param ¶m, const std::shared_ptr<C2ParamReflector> &reflector) { |
| return C2FieldUtils::List(std::make_shared<C2FieldUtilsFieldIterable>(param, reflector)); |
| } |
| |
| /* ------------------------- C2FieldUtils::enumerate siblings ------------------------- */ |
| |
| namespace { |
| |
| struct C2FieldUtilsCousinsIterator : public C2FieldUtils::Iterator::Impl { |
| C2FieldUtilsCousinsIterator( |
| const std::shared_ptr<C2FieldUtils::Info::Impl> &info, size_t level) |
| // clone info chain as this iterator will change it |
| : C2FieldUtils::Iterator::Impl(C2FieldUtils::Info::Impl::Clone(info)) { |
| if (level == 0) { |
| return; |
| } |
| |
| // store parent chain (up to level) for quick access |
| std::shared_ptr<C2FieldUtils::Info::Impl> node = mHead; |
| size_t ix = 0; |
| for (; ix < level && node; ++ix) { |
| node->index = 0; |
| _mPath.emplace_back(node); |
| node = node->parent; |
| } |
| setupPath(ix); |
| } |
| |
| virtual ~C2FieldUtilsCousinsIterator() override = default; |
| |
| /// Increments this iterator by visiting each index. |
| virtual void increment() override { |
| size_t ix = 0; |
| while (ix < _mPath.size()) { |
| if (++_mPath[ix]->index < _mPath[ix]->usedExtent) { |
| setupPath(ix + 1); |
| return; |
| } |
| _mPath[ix++]->index = 0; |
| } |
| mHead.reset(); |
| } |
| |
| private: |
| /// adjusts field offsets along the path up to the specific level - 1. |
| /// This in-fact has to be done down the path from parent to child as child fields must |
| /// fall within parent fields. |
| void setupPath(size_t level) { |
| C2_CHECK_LE(level, _mPath.size()); |
| uint32_t oldArrayOffset = level ? _mPath[level - 1]->arrayOffset : 0 /* unused */; |
| while (level) { |
| --level; |
| C2FieldUtils::Info::Impl &path = *_mPath[level]; |
| uint32_t size = GetSize(path.field); |
| uint32_t offset = path.arrayOffset + size * path.index; |
| SetOffset(path.field, offset); |
| if (level) { |
| // reset child's array offset to fall within current index, but hold onto the |
| // original value of the arrayOffset so that we can adjust subsequent children. |
| // This is because the modulo is only defined within the current array. |
| uint32_t childArrayOffset = |
| offset + (_mPath[level - 1]->arrayOffset - oldArrayOffset) % size; |
| oldArrayOffset = _mPath[level - 1]->arrayOffset; |
| _mPath[level - 1]->arrayOffset = childArrayOffset; |
| } |
| } |
| } |
| |
| std::vector<std::shared_ptr<C2FieldUtils::Info::Impl>> _mPath; |
| }; |
| |
| /** |
| * Iterable implementing enumerateFields(). |
| */ |
| struct C2FieldUtilsCousinsIterable : public C2FieldUtils::List::Impl { |
| /// returns an iterator to the beginning of the list |
| virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override { |
| return std::make_shared<C2FieldUtilsCousinsIterator>(_mHead, _mLevel); |
| }; |
| |
| C2FieldUtilsCousinsIterable(const C2FieldUtils::Info &field, uint32_t level) |
| : _mHead(C2FieldUtils::_Inspector::GetImpl(field)), _mLevel(level) { } |
| |
| private: |
| std::shared_ptr<C2FieldUtils::Info::Impl> _mHead; |
| size_t _mLevel; |
| }; |
| |
| } |
| |
| C2FieldUtils::List C2FieldUtils::enumerateCousins(const C2FieldUtils::Info &field, uint32_t level) { |
| return C2FieldUtils::List(std::make_shared<C2FieldUtilsCousinsIterable>(field, level)); |
| } |
| |
| /* ------------------------- C2FieldUtils::locateField ------------------------- */ |
| |
| namespace { |
| |
| /** |
| * Iterator implementing locateField(). |
| */ |
| struct C2FieldUtilsFieldLocator : public C2FieldUtilsFieldsIteratorHelper { |
| C2FieldUtilsFieldLocator( |
| C2Param::CoreIndex index, const _C2FieldId &field, uint32_t paramSize, |
| std::shared_ptr<C2ParamReflector> reflector) |
| : C2FieldUtilsFieldsIteratorHelper(reflector, paramSize), |
| _mField(field) { |
| while (descendInto(index)) { |
| if ((mHead->field.type() & C2FieldDescriptor::STRUCT_FLAG) == 0) { |
| break; |
| } |
| index = C2Param::CoreIndex(mHead->field.type() &~ C2FieldDescriptor::STRUCT_FLAG); |
| } |
| } |
| |
| void increment() { |
| mHead = _mTail; |
| _mTail = nullptr; |
| } |
| |
| private: |
| /// If the current field is a known, valid (untraversed) structure, it modifies this iterator |
| /// to point to the field at the beginning/end of the given field of the structure and returns |
| /// true. Otherwise, including if no such field exists in the structure, it does not modify this |
| /// iterator and returns false. |
| bool descendInto(C2Param::CoreIndex index) { |
| // check that the boundaries of the field to be located are still within the same parent |
| // field |
| if (mHead != _mTail) { |
| return false; |
| } |
| |
| std::unique_ptr<C2StructDescriptor> descUnique = mReflector->describe(index); |
| // descend into known structs (as long as they have at least one field) |
| if (descUnique && descUnique->begin() != descUnique->end()) { |
| std::shared_ptr<C2StructDescriptor> desc(std::move(descUnique)); |
| mHistory.emplace_back(desc); |
| |
| uint32_t parentOffset = GetParentOffset(mHead); |
| |
| // locate field using a dummy field descriptor |
| C2FieldDescriptor dummy = { |
| C2FieldDescriptor::BLOB, 1 /* extent */, "name", |
| GetOffset(_mField) - parentOffset, GetSize(_mField) |
| }; |
| |
| // locate first field where offset is greater than dummy offset (which is one past) |
| auto it = std::upper_bound( |
| desc->cbegin(), desc->cend(), dummy, |
| [](const C2FieldDescriptor &a, const C2FieldDescriptor &b) -> bool { |
| return _C2ParamInspector::GetOffset(a) < _C2ParamInspector::GetOffset(b); |
| }); |
| if (it == desc->begin()) { |
| // field is prior to first field |
| return false; |
| } |
| --it; |
| const C2FieldDescriptor &field = *it; |
| |
| // check that dummy end-offset is within this field |
| uint32_t structSize = std::max(mParamSize, parentOffset) - parentOffset; |
| if (GetEndOffset(dummy) > GetEndOffset(field, structSize)) { |
| return false; |
| } |
| |
| uint32_t startIndex = (GetOffset(dummy) - GetOffset(field)) / GetSize(field); |
| uint32_t endIndex = |
| (GetEndOffset(dummy) - GetOffset(field) + GetSize(field) - 1) / GetSize(field); |
| if (endIndex > startIndex) { |
| // Field size could be zero, in which case end index is still on start index. |
| // However, for all other cases, endIndex was rounded up to the next index, so |
| // decrement it. |
| --endIndex; |
| } |
| std::shared_ptr<C2FieldUtils::Info::Impl> startLeaf = |
| makeLeaf(field, startIndex); |
| if (endIndex == startIndex) { |
| _mTail = startLeaf; |
| mHead = startLeaf; |
| } else { |
| _mTail = makeLeaf(field, endIndex); |
| mHead = startLeaf; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| _C2FieldId _mField; |
| std::shared_ptr<C2FieldUtils::Info::Impl> _mTail; |
| }; |
| |
| /** |
| * Iterable implementing locateField(). |
| */ |
| struct C2FieldUtilsFieldLocation : public C2FieldUtils::List::Impl { |
| /// returns an iterator to the beginning of the list |
| virtual std::shared_ptr<C2FieldUtils::Iterator::Impl> begin() const override { |
| return std::make_shared<C2FieldUtilsFieldLocator>( |
| _mIndex, _mField, _mParamSize, _mReflector); |
| }; |
| |
| C2FieldUtilsFieldLocation( |
| const C2ParamField &pf, std::shared_ptr<C2ParamReflector> reflector) |
| : _mIndex(C2Param::CoreIndex(_C2ParamInspector::GetIndex(pf))), |
| _mField(_C2ParamInspector::GetField(pf)), |
| _mParamSize(0), |
| _mReflector(reflector) { } |
| |
| |
| C2FieldUtilsFieldLocation( |
| const C2Param ¶m, const _C2FieldId &field, |
| std::shared_ptr<C2ParamReflector> reflector) |
| : _mIndex(param.coreIndex()), |
| _mField(field), |
| _mParamSize(param.size()), |
| _mReflector(reflector) { } |
| |
| private: |
| C2Param::CoreIndex _mIndex; |
| _C2FieldId _mField; |
| uint32_t _mParamSize; |
| std::shared_ptr<C2ParamReflector> _mReflector; |
| }; |
| |
| } |
| |
| std::vector<C2FieldUtils::Info> C2FieldUtils::locateField( |
| const C2ParamField &pf, const std::shared_ptr<C2ParamReflector> &reflector) { |
| C2FieldUtils::List location = { std::make_shared<C2FieldUtilsFieldLocation>(pf, reflector) }; |
| return std::vector<Info>(location.begin(), location.end()); |
| } |
| |
| std::vector<C2FieldUtils::Info> C2FieldUtils::locateField( |
| const C2Param ¶m, const _C2FieldId &field, |
| const std::shared_ptr<C2ParamReflector> &reflector) { |
| C2FieldUtils::List location = { |
| std::make_shared<C2FieldUtilsFieldLocation>(param, field, reflector) |
| }; |
| return std::vector<Info>(location.begin(), location.end()); |
| } |
| |