/* * Copyright (C) 2018 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. */ #include #include #include #include using ::android::base::StringPrintf; /* --------------------------------- ReflectorHelper --------------------------------- */ void C2ReflectorHelper::addStructDescriptors( std::vector &structs, _Tuple<> *) { std::lock_guard lock(_mMutex); for (C2StructDescriptor &strukt : structs) { // TODO: check if structure descriptions conflict with existing ones addStructDescriptor(std::move(strukt)); } } std::unique_ptr C2ReflectorHelper::describe(C2Param::CoreIndex paramIndex) const { std::lock_guard lock(_mMutex); auto it = _mStructs.find(paramIndex); if (it == _mStructs.end()) { return nullptr; } else { return std::make_unique(it->second); } }; void C2ReflectorHelper::addStructDescriptor(C2StructDescriptor &&strukt) { if (_mStructs.find(strukt.coreIndex()) != _mStructs.end()) { // already added // TODO: validate that descriptor matches stored descriptor } // validate that all struct fields are known to this reflector for (const C2FieldDescriptor &fd : strukt) { if (fd.type() & C2FieldDescriptor::STRUCT_FLAG) { C2Param::CoreIndex coreIndex = fd.type() &~ C2FieldDescriptor::STRUCT_FLAG; if (_mStructs.find(coreIndex) == _mStructs.end()) { C2_LOG(INFO) << "missing struct descriptor #" << coreIndex << " for field " << fd.name() << " of struct #" << strukt.coreIndex(); } } } _mStructs.emplace(strukt.coreIndex(), strukt); } /* ---------------------------- ParamHelper ---------------------------- */ class C2InterfaceHelper::ParamHelper::Impl { public: Impl(ParamRef param, C2StringLiteral name, C2StructDescriptor &&strukt) : mParam(param), mName(name), _mStruct(strukt) { } Impl(Impl&&) = default; void addDownDependency(C2Param::Index index) { mDownDependencies.push_back(index); } C2InterfaceHelper::ParamHelper::attrib_t& attrib() { return mAttrib; } void build() { // move dependencies into descriptor mDescriptor = std::make_shared( index(), (C2ParamDescriptor::attrib_t)mAttrib, std::move(mName), std::move(mDependencies)); } void createFieldsAndSupportedValues(const std::shared_ptr &reflector) { for (const C2FieldUtils::Info &f : C2FieldUtils::enumerateFields(*mDefaultValue, reflector)) { if (!f.isArithmetic()) { continue; } std::unique_ptr fsvPointer; // create a breakable structure do { C2FieldSupportedValues fsv; switch (f.type()) { case C2FieldDescriptor::INT32: fsv = C2SupportedRange::Any(); break; case C2FieldDescriptor::UINT32: fsv = C2SupportedRange::Any(); break; case C2FieldDescriptor::INT64: fsv = C2SupportedRange::Any(); break; case C2FieldDescriptor::UINT64: fsv = C2SupportedRange::Any(); break; case C2FieldDescriptor::FLOAT: fsv = C2SupportedRange::Any(); break; case C2FieldDescriptor::BLOB: fsv = C2SupportedRange::Any(); break; case C2FieldDescriptor::STRING: fsv = C2SupportedRange::Any(); break; default: continue; // break out of do {} while } fsvPointer = std::make_unique(fsv); } while (false); mFields.emplace_hint( mFields.end(), _C2FieldId(f.offset(), f.size()), std::make_shared( mParam, _C2FieldId(f.offset(), f.size()), std::move(fsvPointer))); } } /** * Finds a field descriptor. */ std::shared_ptr findField(size_t baseOffs, size_t baseSize) const { auto it = mFields.find(_C2FieldId(baseOffs, baseSize)); if (it == mFields.end()) { return nullptr; } return it->second; } const std::vector getDependenciesAsRefs() const { return mDependenciesAsRefs; } std::shared_ptr getDescriptor() const { return mDescriptor; } const std::vector getDownDependencies() const { return mDownDependencies; } C2Param::Index index() const { if (!mDefaultValue) { fprintf(stderr, "%s missing default value\n", mName.c_str()); } return mDefaultValue->index(); } C2String name() const { return mName; } const ParamRef ref() const { return mParam; } C2StructDescriptor retrieveStructDescriptor() { return std::move(_mStruct); } void setDefaultValue(std::shared_ptr default_) { mDefaultValue = default_; } void setDependencies(std::vector indices, std::vector refs) { mDependencies = indices; mDependenciesAsRefs = refs; } void setFields(std::vector &&fields) { // do not allow adding fields multiple times, or to const values if (!mFields.empty()) { C2_LOG(FATAL) << "Trying to add fields to param " << mName << " multiple times"; } else if (mAttrib & attrib_t::IS_CONST) { C2_LOG(FATAL) << "Trying to add fields to const param " << mName; } for (C2ParamFieldValues &pfv : fields) { mFields.emplace_hint( mFields.end(), // _C2FieldId constructor _C2ParamInspector::GetField(pfv.paramOrField), // Field constructor std::make_shared(mParam, _C2ParamInspector::GetField(pfv.paramOrField), std::move(pfv.values))); } } void setGetter(std::function(bool)> getter) { mGetter = getter; } void setSetter(std::function setter) { mSetter = setter; } c2_status_t trySet( const C2Param *value, bool mayBlock, bool *changed, Factory &f, std::vector>* const failures) { C2R result = mSetter(value, mayBlock, changed, f); return result.retrieveFailures(failures); } c2_status_t validate(const std::shared_ptr &reflector) { if (!mSetter && mFields.empty()) { C2_LOG(WARNING) << "Param " << mName << " has no setter, making it const"; // dependencies are empty in this case mAttrib |= attrib_t::IS_CONST; } else if (!mSetter) { C2_LOG(FATAL) << "Param " << mName << " has no setter"; } if (mAttrib & attrib_t::IS_CONST) { createFieldsAndSupportedValues(reflector); } else { // TODO: update default based on setter and verify that FSV covers the values } if (mFields.empty()) { C2_LOG(FATAL) << "Param " << mName << " has no fields"; } return C2_OK; } std::shared_ptr value() { return mParam.get(); } std::shared_ptr value() const { return mParam.get(); } private: typedef _C2ParamInspector::attrib_t attrib_t; ParamRef mParam; C2String mName; C2StructDescriptor _mStruct; std::shared_ptr mDefaultValue; attrib_t mAttrib; std::function mSetter; std::function(bool)> mGetter; std::vector mDependencies; std::vector mDependenciesAsRefs; std::vector mDownDependencies; // TODO: this does not work for stream dependencies std::map<_C2FieldId, std::shared_ptr> mFields; std::shared_ptr mDescriptor; }; C2InterfaceHelper::ParamHelper::ParamHelper( ParamRef param, C2StringLiteral name, C2StructDescriptor &&strukt) : mImpl(std::make_unique( param, name, std::move(strukt))) { } C2InterfaceHelper::ParamHelper::ParamHelper(C2InterfaceHelper::ParamHelper &&) = default; C2InterfaceHelper::ParamHelper::~ParamHelper() = default; void C2InterfaceHelper::ParamHelper::addDownDependency(C2Param::Index index) { return mImpl->addDownDependency(index); } C2InterfaceHelper::ParamHelper::attrib_t& C2InterfaceHelper::ParamHelper::attrib() { return mImpl->attrib(); } std::shared_ptr C2InterfaceHelper::ParamHelper::build() { mImpl->build(); return std::make_shared(std::move(*this)); } std::shared_ptr C2InterfaceHelper::ParamHelper::findField(size_t baseOffs, size_t baseSize) const { return mImpl->findField(baseOffs, baseSize); } const std::vector C2InterfaceHelper::ParamHelper::getDependenciesAsRefs() const { return mImpl->getDependenciesAsRefs(); } std::shared_ptr C2InterfaceHelper::ParamHelper::getDescriptor() const { return mImpl->getDescriptor(); } const std::vector C2InterfaceHelper::ParamHelper::getDownDependencies() const { return mImpl->getDownDependencies(); } C2Param::Index C2InterfaceHelper::ParamHelper::index() const { return mImpl->index(); } C2String C2InterfaceHelper::ParamHelper::name() const { return mImpl->name(); } const C2InterfaceHelper::ParamRef C2InterfaceHelper::ParamHelper::ref() const { return mImpl->ref(); } C2StructDescriptor C2InterfaceHelper::ParamHelper::retrieveStructDescriptor() { return mImpl->retrieveStructDescriptor(); } void C2InterfaceHelper::ParamHelper::setDefaultValue(std::shared_ptr default_) { mImpl->setDefaultValue(default_); } void C2InterfaceHelper::ParamHelper::setDependencies( std::vector indices, std::vector refs) { mImpl->setDependencies(indices, refs); } void C2InterfaceHelper::ParamHelper::setFields(std::vector &&fields) { return mImpl->setFields(std::move(fields)); } void C2InterfaceHelper::ParamHelper::setGetter( std::function(bool)> getter) { mImpl->setGetter(getter); } void C2InterfaceHelper::ParamHelper::setSetter( std::function setter) { mImpl->setSetter(setter); } c2_status_t C2InterfaceHelper::ParamHelper::trySet( const C2Param *value, bool mayBlock, bool *changed, Factory &f, std::vector>* const failures) { return mImpl->trySet(value, mayBlock, changed, f, failures); } c2_status_t C2InterfaceHelper::ParamHelper::validate( const std::shared_ptr &reflector) { return mImpl->validate(reflector); } std::shared_ptr C2InterfaceHelper::ParamHelper::value() { return mImpl->value(); } std::shared_ptr C2InterfaceHelper::ParamHelper::value() const { return mImpl->value(); } /* ---------------------------- FieldHelper ---------------------------- */ C2ParamField C2InterfaceHelper::FieldHelper::makeParamField(C2Param::Index index) const { return _C2ParamInspector::CreateParamField(index, mFieldId); } C2InterfaceHelper::FieldHelper::FieldHelper(const ParamRef ¶m, const _C2FieldId &field, std::unique_ptr &&values) : mParam(param), mFieldId(field), mPossible(std::move(values)) { C2_LOG(VERBOSE) << "Creating field helper " << field << " " << C2FieldSupportedValuesHelper(*mPossible); } void C2InterfaceHelper::FieldHelper::setSupportedValues( std::unique_ptr &&values) { mSupported = std::move(values); } const C2FieldSupportedValues *C2InterfaceHelper::FieldHelper::getSupportedValues() const { return (mSupported ? mSupported : mPossible).get(); } const C2FieldSupportedValues *C2InterfaceHelper::FieldHelper::getPossibleValues() const { return mPossible.get(); } /* ---------------------------- Field ---------------------------- */ /** * Wrapper around field-supported-values builder that gets stored in the * field helper when the builder goes out of scope. */ template struct SupportedValuesBuilder : C2ParamFieldValuesBuilder { SupportedValuesBuilder( C2ParamField &field, std::shared_ptr helper) : C2ParamFieldValuesBuilder(field), _mHelper(helper), _mField(field) { } /** * Save builder values on destruction. */ virtual ~SupportedValuesBuilder() override { _mHelper->setSupportedValues(std::move(C2ParamFieldValues(*this).values)); } private: std::shared_ptr _mHelper; C2ParamField _mField; }; template C2ParamFieldValuesBuilder C2InterfaceHelper::Field::shouldBe() const { return C2ParamFieldValuesBuilder(_mField); } template C2ParamFieldValuesBuilder C2InterfaceHelper::Field::mustBe() { return SupportedValuesBuilder(_mField, _mHelper); } /* template C2SettingResultsBuilder C2InterfaceHelper::Field::validatePossible(T &value) const { /// TODO return C2SettingResultsBuilder::Ok(); } */ template C2InterfaceHelper::Field::Field(std::shared_ptr helper, C2Param::Index index) : _mHelper(helper), _mField(helper->makeParamField(index)) { } template struct C2InterfaceHelper::Field; template struct C2InterfaceHelper::Field; template struct C2InterfaceHelper::Field; template struct C2InterfaceHelper::Field; //template struct C2InterfaceHelper::Field; template struct C2InterfaceHelper::Field; template struct C2InterfaceHelper::Field; //template struct C2InterfaceHelper::Field; template struct C2InterfaceHelper::Field; /* --------------------------------- Factory --------------------------------- */ struct C2InterfaceHelper::FactoryImpl : public C2InterfaceHelper::Factory { virtual std::shared_ptr getReflector() const override { return _mReflector; } virtual std::shared_ptr getParamHelper(const ParamRef ¶m) const override { return _mParams.find(param)->second; } public: FactoryImpl(std::shared_ptr reflector) : _mReflector(reflector) { } virtual ~FactoryImpl() = default; void addParam(std::shared_ptr param) { _mParams.insert({ param->ref(), param }); _mIndexToHelper.insert({param->index(), param}); // add down-dependencies (and validate dependencies as a result) size_t ix = 0; for (const ParamRef &ref : param->getDependenciesAsRefs()) { // dependencies must already be defined if (!_mParams.count(ref)) { C2_LOG(FATAL) << "Parameter " << param->name() << " has a dependency at index " << ix << " that is not yet defined"; } _mParams.find(ref)->second->addDownDependency(param->index()); ++ix; } _mDependencyIndex.emplace(param->index(), _mDependencyIndex.size()); } std::shared_ptr getParam(C2Param::Index ix) const { // TODO: handle streams separately const auto it = _mIndexToHelper.find(ix); if (it == _mIndexToHelper.end()) { return nullptr; } return it->second; } /** * TODO: this could return a copy using proper pointer cast. */ std::shared_ptr getParamValue(C2Param::Index ix) const { std::shared_ptr helper = getParam(ix); return helper ? helper->value() : nullptr; } c2_status_t querySupportedParams( std::vector> *const params) const { for (const auto &it : _mParams) { // TODO: change querySupportedParams signature? params->push_back( std::const_pointer_cast(it.second->getDescriptor())); } // TODO: handle errors return C2_OK; } size_t getDependencyIndex(C2Param::Index ix) { // in this version of the helper there is only a single stream so // we can look up directly by index auto it = _mDependencyIndex.find(ix); return it == _mDependencyIndex.end() ? SIZE_MAX : it->second; } private: std::map> _mParams; std::map> _mIndexToHelper; std::shared_ptr _mReflector; std::map _mDependencyIndex; }; /* --------------------------------- Helper --------------------------------- */ namespace { static std::string asString(C2Param *p) { char addr[20]; sprintf(addr, "%p:[", p); std::string v = addr; for (size_t i = 0; i < p->size(); ++i) { char d[4]; sprintf(d, " %02x", *(((uint8_t *)p) + i)); v += d + (i == 0); } return v + "]"; } } C2InterfaceHelper::C2InterfaceHelper(std::shared_ptr reflector) : mReflector(reflector), _mFactory(std::make_shared(reflector)) { } size_t C2InterfaceHelper::GetBaseOffset(const std::shared_ptr &reflector, C2Param::CoreIndex index, size_t offset) { std::unique_ptr param = reflector->describe(index); if (param == nullptr) { return ~(size_t)0; // param structure not described } for (const C2FieldDescriptor &field : *param) { size_t fieldOffset = _C2ParamInspector::GetOffset(field); size_t fieldSize = _C2ParamInspector::GetSize(field); size_t fieldExtent = field.extent(); if (offset < fieldOffset) { return ~(size_t)0; // not found } if (offset == fieldOffset) { // exact match return offset; } if (field.extent() == 0 || offset < fieldOffset + fieldSize * fieldExtent) { // reduce to first element in case of array offset = fieldOffset + (offset - fieldOffset) % fieldSize; if (field.type() >= C2FieldDescriptor::STRUCT_FLAG) { // this offset is within a field offset = GetBaseOffset( reflector, field.type() & ~C2FieldDescriptor::STRUCT_FLAG, offset - fieldOffset); return ~offset ? fieldOffset + offset : offset; } } } return ~(size_t)0; // not found } void C2InterfaceHelper::addParameter(std::shared_ptr param) { std::lock_guard lock(mMutex); mReflector->addStructDescriptor(param->retrieveStructDescriptor()); c2_status_t err = param->validate(mReflector); if (err != C2_CORRUPTED) { _mFactory->addParam(param); // run setter to ensure correct values bool changed = false; std::vector> failures; (void)param->trySet(param->value().get(), C2_MAY_BLOCK, &changed, *_mFactory, &failures); } } c2_status_t C2InterfaceHelper::config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector>* const failures, bool updateParams, std::vector> *changes __unused /* TODO */) { std::lock_guard lock(mMutex); bool paramWasInvalid = false; // TODO is this the same as bad value? bool paramNotFound = false; bool paramBadValue = false; bool paramNoMemory = false; bool paramBlocking = false; bool paramTimedOut = false; bool paramCorrupted = false; // dependencies // down dependencies are marked dirty, but params set are not immediately // marked dirty (unless they become down dependency) so that we can // avoid setting them if they did not change // TODO: there could be multiple indices for the same dependency index // { depIx, paramIx } may be a suitable key std::map> dependencies; // we cannot determine the last valid parameter, so add an extra // loop iteration after the last parameter for (size_t p_ix = 0; p_ix <= params.size(); ++p_ix) { C2Param *p = nullptr; C2Param::Index paramIx = 0u; size_t paramDepIx = SIZE_MAX; bool last = p_ix == params.size(); if (!last) { p = params[p_ix]; if (!*p) { paramWasInvalid = true; p->invalidate(); continue; } paramIx = p->index(); paramDepIx = getDependencyIndex_l(paramIx); if (paramDepIx == SIZE_MAX) { // unsupported parameter paramNotFound = true; continue; } // // first insert - mark not dirty // it may have been marked dirty by a dependency update // this does not overrwrite(!) (void)dependencies.insert({ paramDepIx, { paramIx, false /* dirty */ }}); auto it = dependencies.find(paramDepIx); C2_LOG(VERBOSE) << "marking dependency for setting at #" << paramDepIx << ": " << it->second.first << ", update " << (it->second.second ? "always (dirty)" : "only if changed"); } else { // process any remaining dependencies if (dependencies.empty()) { continue; } C2_LOG(VERBOSE) << "handling dirty down dependencies after last setting"; } // process any dirtied down-dependencies until the next param while (dependencies.size() && dependencies.begin()->first <= paramDepIx) { auto min = dependencies.begin(); C2Param::Index ix = min->second.first; bool dirty = min->second.second; dependencies.erase(min); std::shared_ptr param = _mFactory->getParam(ix); C2_LOG(VERBOSE) << "old value " << asString(param->value().get()); if (!last) { C2_LOG(VERBOSE) << "new value " << asString(p); } if (!last && !dirty && ix == paramIx && *param->value() == *p) { // no change in value - and dependencies were not updated // no need to update C2_LOG(VERBOSE) << "ignoring setting unchanged param " << ix; continue; } // apply setting bool changed = false; C2_LOG(VERBOSE) << "setting param " << ix; std::shared_ptr oldValue = param->value(); c2_status_t res = param->trySet( (!last && paramIx == ix) ? p : param->value().get(), mayBlock, &changed, *_mFactory, failures); std::shared_ptr newValue = param->value(); C2_CHECK_EQ(oldValue == newValue, *oldValue == *newValue); switch (res) { case C2_OK: break; case C2_BAD_VALUE: paramBadValue = true; break; case C2_NO_MEMORY: paramNoMemory = true; break; case C2_TIMED_OUT: paramTimedOut = true; break; case C2_BLOCKING: paramBlocking = true; break; case C2_CORRUPTED: paramCorrupted = true; break; default: ;// TODO fatal } // copy back result for configured values (or invalidate if it does not fit or match) if (updateParams && !last && paramIx == ix) { if (!p->updateFrom(*param->value())) { p->invalidate(); } } // compare ptrs as params are copy on write if (changed) { C2_LOG(VERBOSE) << "param " << ix << " value changed"; // value changed update down-dependencies and mark them dirty for (const C2Param::Index ix : param->getDownDependencies()) { C2_LOG(VERBOSE) << 1; auto insert_res = dependencies.insert( { getDependencyIndex_l(ix), { ix, true /* dirty */ }}); if (!insert_res.second) { (*insert_res.first).second.second = true; // mark dirty } auto it = dependencies.find(getDependencyIndex_l(ix)); C2_CHECK(it->second.second); C2_LOG(VERBOSE) << "marking down dependencies to update at #" << getDependencyIndex_l(ix) << ": " << it->second.first; } } } } return (paramCorrupted ? C2_CORRUPTED : paramBlocking ? C2_BLOCKING : paramTimedOut ? C2_TIMED_OUT : paramNoMemory ? C2_NO_MEMORY : (paramBadValue || paramWasInvalid) ? C2_BAD_VALUE : paramNotFound ? C2_BAD_INDEX : C2_OK); } size_t C2InterfaceHelper::getDependencyIndex_l(C2Param::Index ix) const { return _mFactory->getDependencyIndex(ix); } c2_status_t C2InterfaceHelper::query( const std::vector &stackParams, const std::vector &heapParamIndices, c2_blocking_t mayBlock __unused /* TODO */, std::vector>* const heapParams) const { std::lock_guard lock(mMutex); bool paramWasInvalid = false; bool paramNotFound = false; bool paramDidNotFit = false; bool paramNoMemory = false; for (C2Param* const p : stackParams) { if (!*p) { paramWasInvalid = true; p->invalidate(); } else { std::shared_ptr value = _mFactory->getParamValue(p->index()); if (!value) { paramNotFound = true; p->invalidate(); } else if (!p->updateFrom(*value)) { paramDidNotFit = true; p->invalidate(); } } } for (const C2Param::Index ix : heapParamIndices) { std::shared_ptr value = _mFactory->getParamValue(ix); if (value) { std::unique_ptr p = C2Param::Copy(*value); if (p != nullptr) { heapParams->push_back(std::move(p)); } else { paramNoMemory = true; } } else { paramNotFound = true; } } return paramNoMemory ? C2_NO_MEMORY : paramNotFound ? C2_BAD_INDEX : // the following errors are not marked in the return value paramDidNotFit ? C2_OK : paramWasInvalid ? C2_OK : C2_OK; } c2_status_t C2InterfaceHelper::querySupportedParams( std::vector> *const params) const { std::lock_guard lock(mMutex); return _mFactory->querySupportedParams(params); } c2_status_t C2InterfaceHelper::querySupportedValues( std::vector &fields, c2_blocking_t mayBlock __unused) const { std::lock_guard lock(mMutex); for (C2FieldSupportedValuesQuery &query : fields) { C2_LOG(VERBOSE) << "querying field " << query.field(); C2Param::Index ix = _C2ParamInspector::GetIndex(query.field()); std::shared_ptr param = _mFactory->getParam(ix); if (!param) { C2_LOG(VERBOSE) << "bad param"; query.status = C2_BAD_INDEX; continue; } size_t offs = GetBaseOffset( mReflector, ix, _C2ParamInspector::GetOffset(query.field()) - sizeof(C2Param)); if (~offs == 0) { C2_LOG(VERBOSE) << "field could not be found"; query.status = C2_NOT_FOUND; continue; } offs += sizeof(C2Param); C2_LOG(VERBOSE) << "field resolved to " << StringPrintf("@%02zx+%02x", offs, _C2ParamInspector::GetSize(query.field())); std::shared_ptr field = param->findField(offs, _C2ParamInspector::GetSize(query.field())); if (!field) { C2_LOG(VERBOSE) << "bad field"; query.status = C2_NOT_FOUND; continue; } const C2FieldSupportedValues *values = nullptr; switch (query.type()) { case C2FieldSupportedValuesQuery::CURRENT: values = field->getSupportedValues(); break; case C2FieldSupportedValuesQuery::POSSIBLE: values = field->getPossibleValues(); break; default: C2_LOG(VERBOSE) << "bad query type: " << query.type(); query.status = C2_BAD_VALUE; } if (values) { query.values = *values; query.status = C2_OK; } else { C2_LOG(DEBUG) << "no values published by component"; query.status = C2_CORRUPTED; } } return C2_OK; } std::unique_lock C2InterfaceHelper::lock() const { return std::unique_lock(mMutex); }