/* * 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()); }