You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1387 lines
49 KiB
1387 lines
49 KiB
/*
|
|
* 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 <C2Config.h>
|
|
#include <C2Debug.h>
|
|
#include <C2Param.h>
|
|
#include <C2ParamDef.h>
|
|
#include <C2ParamInternal.h>
|
|
#include <util/C2InterfaceUtils.h>
|
|
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <map>
|
|
#include <sstream>
|
|
#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
|
|
values.erase(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);
|
|
}),
|
|
values.end());
|
|
// 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
|
|
values.erase(std::remove_if(values.begin(), values.end(),
|
|
[&limit](const C2Value::Primitive &v) -> bool {
|
|
return !limit.contains(v.ref<ValueType>());
|
|
}),
|
|
values.end());
|
|
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
|
|
values.erase(std::remove_if(values.begin(), values.end(),
|
|
[&limit](const C2Value::Primitive &v) -> bool {
|
|
return !limit.contains(v.ref<ValueType>());
|
|
}),
|
|
values.end());
|
|
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
|
|
values.erase(std::remove_if(values.begin(), values.end(),
|
|
[&limit](const C2Value::Primitive &v) -> bool {
|
|
return !limit.contains(v.ref<ValueType>());
|
|
}),
|
|
values.end());
|
|
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());
|
|
}
|
|
|
|
//static
|
|
bool C2InterfaceUtils::FillTraitsFromInterface(
|
|
C2Component::Traits *traits,
|
|
const std::shared_ptr<C2ComponentInterface> &intf) {
|
|
if (!traits) {
|
|
return false;
|
|
}
|
|
traits->name = intf->getName();
|
|
|
|
C2ComponentKindSetting kind;
|
|
C2ComponentDomainSetting domain;
|
|
c2_status_t res = intf->query_vb({ &kind, &domain }, {}, C2_MAY_BLOCK, nullptr);
|
|
bool fixDomain = res != C2_OK;
|
|
if (res == C2_OK) {
|
|
traits->kind = kind.value;
|
|
traits->domain = domain.value;
|
|
} else {
|
|
// TODO: remove this fall-back
|
|
C2_LOG(DEBUG) << "failed to query interface for kind and domain: " << res;
|
|
|
|
traits->kind =
|
|
(traits->name.find("encoder") != std::string::npos) ? C2Component::KIND_ENCODER :
|
|
(traits->name.find("decoder") != std::string::npos) ? C2Component::KIND_DECODER :
|
|
C2Component::KIND_OTHER;
|
|
}
|
|
|
|
uint32_t mediaTypeIndex = traits->kind == C2Component::KIND_ENCODER
|
|
? C2PortMediaTypeSetting::output::PARAM_TYPE
|
|
: C2PortMediaTypeSetting::input::PARAM_TYPE;
|
|
std::vector<std::unique_ptr<C2Param>> params;
|
|
res = intf->query_vb({}, { mediaTypeIndex }, C2_MAY_BLOCK, ¶ms);
|
|
if (res != C2_OK) {
|
|
C2_LOG(DEBUG) << "failed to query interface: " << res;
|
|
return false;
|
|
}
|
|
if (params.size() != 1u) {
|
|
C2_LOG(DEBUG) << "failed to query interface: unexpected vector size: " << params.size();
|
|
return false;
|
|
}
|
|
C2PortMediaTypeSetting *mediaTypeConfig = C2PortMediaTypeSetting::From(params[0].get());
|
|
if (mediaTypeConfig == nullptr) {
|
|
C2_LOG(DEBUG) << "failed to query media type";
|
|
return false;
|
|
}
|
|
traits->mediaType =
|
|
std::string(mediaTypeConfig->m.value,
|
|
strnlen(mediaTypeConfig->m.value, mediaTypeConfig->flexCount()));
|
|
|
|
if (fixDomain) {
|
|
if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) {
|
|
traits->domain = C2Component::DOMAIN_AUDIO;
|
|
} else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) {
|
|
traits->domain = C2Component::DOMAIN_VIDEO;
|
|
} else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) {
|
|
traits->domain = C2Component::DOMAIN_IMAGE;
|
|
} else {
|
|
traits->domain = C2Component::DOMAIN_OTHER;
|
|
}
|
|
}
|
|
|
|
params.clear();
|
|
res = intf->query_vb({}, { C2ComponentAliasesSetting::PARAM_TYPE }, C2_MAY_BLOCK, ¶ms);
|
|
if (res == C2_OK && params.size() == 1u) {
|
|
C2ComponentAliasesSetting *aliasesSetting =
|
|
C2ComponentAliasesSetting::From(params[0].get());
|
|
if (aliasesSetting) {
|
|
std::istringstream iss(
|
|
std::string(aliasesSetting->m.value, aliasesSetting->flexCount()));
|
|
C2_LOG(DEBUG) << intf->getName() << " has aliases: " << iss.str();
|
|
|
|
for (std::string tok; std::getline(iss, tok, ','); ) {
|
|
traits->aliases.push_back(tok);
|
|
C2_LOG(DEBUG) << "adding alias: " << tok;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|