1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <C2Debug.h>
18 #include <C2ParamInternal.h>
19 #include <util/C2InterfaceHelper.h>
20 
21 #include <android-base/stringprintf.h>
22 
23 using ::android::base::StringPrintf;
24 
25 /* --------------------------------- ReflectorHelper --------------------------------- */
26 
addStructDescriptors(std::vector<C2StructDescriptor> & structs,_Tuple<> *)27 void C2ReflectorHelper::addStructDescriptors(
28         std::vector<C2StructDescriptor> &structs, _Tuple<> *) {
29     std::lock_guard<std::mutex> lock(_mMutex);
30     for (C2StructDescriptor &strukt : structs) {
31         // TODO: check if structure descriptions conflict with existing ones
32         addStructDescriptor(std::move(strukt));
33     }
34 }
35 
36 std::unique_ptr<C2StructDescriptor>
describe(C2Param::CoreIndex paramIndex) const37 C2ReflectorHelper::describe(C2Param::CoreIndex paramIndex) const {
38     std::lock_guard<std::mutex> lock(_mMutex);
39     auto it = _mStructs.find(paramIndex);
40     if (it == _mStructs.end()) {
41         return nullptr;
42     } else {
43         return std::make_unique<C2StructDescriptor>(it->second);
44     }
45 };
46 
addStructDescriptor(C2StructDescriptor && strukt)47 void C2ReflectorHelper::addStructDescriptor(C2StructDescriptor &&strukt) {
48     if (_mStructs.find(strukt.coreIndex()) != _mStructs.end()) {
49         // already added
50         // TODO: validate that descriptor matches stored descriptor
51     }
52     // validate that all struct fields are known to this reflector
53     for (const C2FieldDescriptor &fd : strukt) {
54         if (fd.type() & C2FieldDescriptor::STRUCT_FLAG) {
55             C2Param::CoreIndex coreIndex = fd.type() &~ C2FieldDescriptor::STRUCT_FLAG;
56             if (_mStructs.find(coreIndex) == _mStructs.end()) {
57                 C2_LOG(INFO) << "missing struct descriptor #" << coreIndex << " for field "
58                         << fd.name() << " of struct #" << strukt.coreIndex();
59             }
60         }
61     }
62     _mStructs.emplace(strukt.coreIndex(), strukt);
63 }
64 
65 
66 /* ---------------------------- ParamHelper ---------------------------- */
67 
68 class C2InterfaceHelper::ParamHelper::Impl {
69 public:
Impl(ParamRef param,C2StringLiteral name,C2StructDescriptor && strukt)70     Impl(ParamRef param, C2StringLiteral name, C2StructDescriptor &&strukt)
71         : mParam(param), mName(name), _mStruct(strukt) { }
72 
73     Impl(Impl&&) = default;
74 
addDownDependency(C2Param::Index index)75     void addDownDependency(C2Param::Index index) {
76         mDownDependencies.push_back(index);
77     }
78 
attrib()79     C2InterfaceHelper::ParamHelper::attrib_t& attrib() {
80         return mAttrib;
81     }
82 
build()83     void build() {
84         // move dependencies into descriptor
85         mDescriptor = std::make_shared<C2ParamDescriptor>(
86                 index(), (C2ParamDescriptor::attrib_t)mAttrib,
87                 std::move(mName), std::move(mDependencies));
88     }
89 
createFieldsAndSupportedValues(const std::shared_ptr<C2ParamReflector> & reflector)90     void createFieldsAndSupportedValues(const std::shared_ptr<C2ParamReflector> &reflector) {
91         for (const C2FieldUtils::Info &f :
92                 C2FieldUtils::enumerateFields(*mDefaultValue, reflector)) {
93             if (!f.isArithmetic()) {
94                 continue;
95             }
96             std::unique_ptr<C2FieldSupportedValues> fsvPointer;
97 
98             // create a breakable structure
99             do {
100                 C2FieldSupportedValues fsv;
101                 switch (f.type()) {
102                     case C2FieldDescriptor::INT32:  fsv = C2SupportedRange<int32_t>::Any(); break;
103                     case C2FieldDescriptor::UINT32: fsv = C2SupportedRange<uint32_t>::Any(); break;
104                     case C2FieldDescriptor::INT64:  fsv = C2SupportedRange<int64_t>::Any(); break;
105                     case C2FieldDescriptor::UINT64: fsv = C2SupportedRange<uint64_t>::Any(); break;
106                     case C2FieldDescriptor::FLOAT:  fsv = C2SupportedRange<float>::Any(); break;
107                     case C2FieldDescriptor::BLOB:   fsv = C2SupportedRange<uint8_t>::Any(); break;
108                     case C2FieldDescriptor::STRING: fsv = C2SupportedRange<char>::Any(); break;
109                 default:
110                     continue; // break out of do {} while
111                 }
112                 fsvPointer = std::make_unique<C2FieldSupportedValues>(fsv);
113             } while (false);
114 
115             mFields.emplace_hint(
116                     mFields.end(),
117                     _C2FieldId(f.offset(), f.size()),
118                     std::make_shared<FieldHelper>(
119                             mParam, _C2FieldId(f.offset(), f.size()), std::move(fsvPointer)));
120         }
121     }
122 
123     /**
124      * Finds a field descriptor.
125      */
findField(size_t baseOffs,size_t baseSize) const126     std::shared_ptr<FieldHelper> findField(size_t baseOffs, size_t baseSize) const {
127         auto it = mFields.find(_C2FieldId(baseOffs, baseSize));
128         if (it == mFields.end()) {
129             return nullptr;
130         }
131         return it->second;
132     }
133 
getDependenciesAsRefs() const134     const std::vector<ParamRef> getDependenciesAsRefs() const {
135         return mDependenciesAsRefs;
136     }
137 
getDescriptor() const138     std::shared_ptr<const C2ParamDescriptor> getDescriptor() const {
139         return mDescriptor;
140     }
141 
getDownDependencies() const142     const std::vector<C2Param::Index> getDownDependencies() const {
143         return mDownDependencies;
144     }
145 
index() const146     C2Param::Index index() const {
147         if (!mDefaultValue) {
148             fprintf(stderr, "%s missing default value\n", mName.c_str());
149         }
150         return mDefaultValue->index();
151     }
152 
name() const153     C2String name() const {
154         return mName;
155     }
156 
ref() const157     const ParamRef ref() const {
158         return mParam;
159     }
160 
retrieveStructDescriptor()161     C2StructDescriptor retrieveStructDescriptor() {
162         return std::move(_mStruct);
163     }
164 
setDefaultValue(std::shared_ptr<C2Param> default_)165     void setDefaultValue(std::shared_ptr<C2Param> default_) {
166         mDefaultValue = default_;
167     }
168 
setDependencies(std::vector<C2Param::Index> indices,std::vector<ParamRef> refs)169     void setDependencies(std::vector<C2Param::Index> indices, std::vector<ParamRef> refs) {
170         mDependencies = indices;
171         mDependenciesAsRefs = refs;
172     }
173 
setFields(std::vector<C2ParamFieldValues> && fields)174     void setFields(std::vector<C2ParamFieldValues> &&fields) {
175         // do not allow adding fields multiple times, or to const values
176         if (!mFields.empty()) {
177             C2_LOG(FATAL) << "Trying to add fields to param " << mName << " multiple times";
178         } else if (mAttrib & attrib_t::IS_CONST) {
179             C2_LOG(FATAL) << "Trying to add fields to const param " << mName;
180         }
181 
182         for (C2ParamFieldValues &pfv : fields) {
183             mFields.emplace_hint(
184                     mFields.end(),
185                     // _C2FieldId constructor
186                     _C2ParamInspector::GetField(pfv.paramOrField),
187                     // Field constructor
188                     std::make_shared<FieldHelper>(mParam,
189                                             _C2ParamInspector::GetField(pfv.paramOrField),
190                                             std::move(pfv.values)));
191         }
192     }
193 
setGetter(std::function<std::shared_ptr<C2Param> (bool)> getter)194     void setGetter(std::function<std::shared_ptr<C2Param>(bool)> getter) {
195         mGetter = getter;
196     }
197 
setSetter(std::function<C2R (const C2Param *,bool,bool *,Factory &)> setter)198     void setSetter(std::function<C2R(const C2Param *, bool, bool *, Factory &)> setter) {
199         mSetter = setter;
200     }
201 
trySet(const C2Param * value,bool mayBlock,bool * changed,Factory & f,std::vector<std::unique_ptr<C2SettingResult>> * const failures)202     c2_status_t trySet(
203             const C2Param *value, bool mayBlock, bool *changed, Factory &f,
204             std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
205         C2R result = mSetter(value, mayBlock, changed, f);
206         return result.retrieveFailures(failures);
207     }
208 
validate(const std::shared_ptr<C2ParamReflector> & reflector)209     c2_status_t validate(const std::shared_ptr<C2ParamReflector> &reflector) {
210         if (!mSetter && mFields.empty()) {
211             C2_LOG(WARNING) << "Param " << mName << " has no setter, making it const";
212             // dependencies are empty in this case
213             mAttrib |= attrib_t::IS_CONST;
214         } else if (!mSetter) {
215             C2_LOG(FATAL) << "Param " << mName << " has no setter";
216         }
217 
218         if (mAttrib & attrib_t::IS_CONST) {
219             createFieldsAndSupportedValues(reflector);
220         } else {
221             // TODO: update default based on setter and verify that FSV covers the values
222         }
223 
224         if (mFields.empty()) {
225             C2_LOG(FATAL) << "Param " << mName << " has no fields";
226         }
227 
228         return C2_OK;
229     }
230 
value()231     std::shared_ptr<C2Param> value() {
232         return mParam.get();
233     }
234 
value() const235     std::shared_ptr<const C2Param> value() const {
236         return mParam.get();
237     }
238 
239 private:
240     typedef _C2ParamInspector::attrib_t attrib_t;
241     ParamRef mParam;
242     C2String mName;
243     C2StructDescriptor _mStruct;
244     std::shared_ptr<C2Param> mDefaultValue;
245     attrib_t mAttrib;
246     std::function<C2R(const C2Param *, bool, bool *, Factory &)> mSetter;
247     std::function<std::shared_ptr<C2Param>(bool)> mGetter;
248     std::vector<C2Param::Index> mDependencies;
249     std::vector<ParamRef> mDependenciesAsRefs;
250     std::vector<C2Param::Index> mDownDependencies; // TODO: this does not work for stream dependencies
251     std::map<_C2FieldId, std::shared_ptr<FieldHelper>> mFields;
252     std::shared_ptr<C2ParamDescriptor> mDescriptor;
253 };
254 
ParamHelper(ParamRef param,C2StringLiteral name,C2StructDescriptor && strukt)255 C2InterfaceHelper::ParamHelper::ParamHelper(
256         ParamRef param, C2StringLiteral name, C2StructDescriptor &&strukt)
257     : mImpl(std::make_unique<C2InterfaceHelper::ParamHelper::Impl>(
258             param, name, std::move(strukt))) { }
259 
260 C2InterfaceHelper::ParamHelper::ParamHelper(C2InterfaceHelper::ParamHelper &&) = default;
261 
262 C2InterfaceHelper::ParamHelper::~ParamHelper() = default;
263 
addDownDependency(C2Param::Index index)264 void C2InterfaceHelper::ParamHelper::addDownDependency(C2Param::Index index) {
265     return mImpl->addDownDependency(index);
266 }
267 
attrib()268 C2InterfaceHelper::ParamHelper::attrib_t& C2InterfaceHelper::ParamHelper::attrib() {
269     return mImpl->attrib();
270 }
271 
build()272 std::shared_ptr<C2InterfaceHelper::ParamHelper> C2InterfaceHelper::ParamHelper::build() {
273     mImpl->build();
274     return std::make_shared<C2InterfaceHelper::ParamHelper>(std::move(*this));
275 }
276 
277 std::shared_ptr<C2InterfaceHelper::FieldHelper>
findField(size_t baseOffs,size_t baseSize) const278 C2InterfaceHelper::ParamHelper::findField(size_t baseOffs, size_t baseSize) const {
279     return mImpl->findField(baseOffs, baseSize);
280 }
281 
282 const std::vector<C2InterfaceHelper::ParamRef>
getDependenciesAsRefs() const283 C2InterfaceHelper::ParamHelper::getDependenciesAsRefs() const {
284     return mImpl->getDependenciesAsRefs();
285 }
286 
287 std::shared_ptr<const C2ParamDescriptor>
getDescriptor() const288 C2InterfaceHelper::ParamHelper::getDescriptor() const {
289     return mImpl->getDescriptor();
290 }
291 
getDownDependencies() const292 const std::vector<C2Param::Index> C2InterfaceHelper::ParamHelper::getDownDependencies() const {
293     return mImpl->getDownDependencies();
294 }
295 
index() const296 C2Param::Index C2InterfaceHelper::ParamHelper::index() const {
297     return mImpl->index();
298 }
299 
name() const300 C2String C2InterfaceHelper::ParamHelper::name() const {
301     return mImpl->name();
302 }
303 
ref() const304 const C2InterfaceHelper::ParamRef C2InterfaceHelper::ParamHelper::ref() const {
305     return mImpl->ref();
306 }
307 
retrieveStructDescriptor()308 C2StructDescriptor C2InterfaceHelper::ParamHelper::retrieveStructDescriptor() {
309     return mImpl->retrieveStructDescriptor();
310 }
311 
setDefaultValue(std::shared_ptr<C2Param> default_)312 void C2InterfaceHelper::ParamHelper::setDefaultValue(std::shared_ptr<C2Param> default_) {
313     mImpl->setDefaultValue(default_);
314 }
315 
setDependencies(std::vector<C2Param::Index> indices,std::vector<ParamRef> refs)316 void C2InterfaceHelper::ParamHelper::setDependencies(
317         std::vector<C2Param::Index> indices, std::vector<ParamRef> refs) {
318     mImpl->setDependencies(indices, refs);
319 }
320 
setFields(std::vector<C2ParamFieldValues> && fields)321 void C2InterfaceHelper::ParamHelper::setFields(std::vector<C2ParamFieldValues> &&fields) {
322     return mImpl->setFields(std::move(fields));
323 }
324 
setGetter(std::function<std::shared_ptr<C2Param> (bool)> getter)325 void C2InterfaceHelper::ParamHelper::setGetter(
326         std::function<std::shared_ptr<C2Param>(bool)> getter) {
327     mImpl->setGetter(getter);
328 }
329 
setSetter(std::function<C2R (const C2Param *,bool,bool *,Factory &)> setter)330 void C2InterfaceHelper::ParamHelper::setSetter(
331         std::function<C2R(const C2Param *, bool, bool *, Factory &)> setter) {
332     mImpl->setSetter(setter);
333 }
334 
trySet(const C2Param * value,bool mayBlock,bool * changed,Factory & f,std::vector<std::unique_ptr<C2SettingResult>> * const failures)335 c2_status_t C2InterfaceHelper::ParamHelper::trySet(
336         const C2Param *value, bool mayBlock, bool *changed, Factory &f,
337         std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
338     return mImpl->trySet(value, mayBlock, changed, f, failures);
339 }
340 
validate(const std::shared_ptr<C2ParamReflector> & reflector)341 c2_status_t C2InterfaceHelper::ParamHelper::validate(
342         const std::shared_ptr<C2ParamReflector> &reflector) {
343     return mImpl->validate(reflector);
344 }
345 
value()346 std::shared_ptr<C2Param> C2InterfaceHelper::ParamHelper::value() {
347     return mImpl->value();
348 }
349 
value() const350 std::shared_ptr<const C2Param> C2InterfaceHelper::ParamHelper::value() const {
351     return mImpl->value();
352 }
353 
354 /* ---------------------------- FieldHelper ---------------------------- */
355 
makeParamField(C2Param::Index index) const356 C2ParamField C2InterfaceHelper::FieldHelper::makeParamField(C2Param::Index index) const {
357     return _C2ParamInspector::CreateParamField(index, mFieldId);
358 }
359 
FieldHelper(const ParamRef & param,const _C2FieldId & field,std::unique_ptr<C2FieldSupportedValues> && values)360 C2InterfaceHelper::FieldHelper::FieldHelper(const ParamRef &param, const _C2FieldId &field,
361             std::unique_ptr<C2FieldSupportedValues> &&values)
362     : mParam(param),
363       mFieldId(field),
364       mPossible(std::move(values)) {
365     C2_LOG(VERBOSE) << "Creating field helper " << field << " "
366             << C2FieldSupportedValuesHelper<uint32_t>(*mPossible);
367 }
368 
setSupportedValues(std::unique_ptr<C2FieldSupportedValues> && values)369 void C2InterfaceHelper::FieldHelper::setSupportedValues(
370         std::unique_ptr<C2FieldSupportedValues> &&values) {
371     mSupported = std::move(values);
372 }
373 
getSupportedValues() const374 const C2FieldSupportedValues *C2InterfaceHelper::FieldHelper::getSupportedValues() const {
375     return (mSupported ? mSupported : mPossible).get();
376 }
377 
getPossibleValues() const378 const C2FieldSupportedValues *C2InterfaceHelper::FieldHelper::getPossibleValues() const {
379     return mPossible.get();
380 }
381 
382 
383 /* ---------------------------- Field ---------------------------- */
384 
385 /**
386  * Wrapper around field-supported-values builder that gets stored in the
387  * field helper when the builder goes out of scope.
388  */
389 template<typename T>
390 struct SupportedValuesBuilder : C2ParamFieldValuesBuilder<T> {
SupportedValuesBuilderSupportedValuesBuilder391     SupportedValuesBuilder(
392             C2ParamField &field, std::shared_ptr<C2InterfaceHelper::FieldHelper> helper)
393         : C2ParamFieldValuesBuilder<T>(field), _mHelper(helper), _mField(field) {
394     }
395 
396     /**
397      * Save builder values on destruction.
398      */
~SupportedValuesBuilderSupportedValuesBuilder399     virtual ~SupportedValuesBuilder() override {
400         _mHelper->setSupportedValues(std::move(C2ParamFieldValues(*this).values));
401     }
402 
403 private:
404     std::shared_ptr<C2InterfaceHelper::FieldHelper> _mHelper;
405     C2ParamField _mField;
406 };
407 
408 
409 template<typename T>
shouldBe() const410 C2ParamFieldValuesBuilder<T> C2InterfaceHelper::Field<T>::shouldBe() const {
411     return C2ParamFieldValuesBuilder<T>(_mField);
412 }
413 
414 template<typename T>
mustBe()415 C2ParamFieldValuesBuilder<T> C2InterfaceHelper::Field<T>::mustBe() {
416     return SupportedValuesBuilder<T>(_mField, _mHelper);
417 }
418 
419 /*
420 template<typename T> C2SettingResultsBuilder C2InterfaceHelper::Field<T>::validatePossible(T &value)
421 const {
422     /// TODO
423     return C2SettingResultsBuilder::Ok();
424 }
425 */
426 
427 template<typename T>
Field(std::shared_ptr<FieldHelper> helper,C2Param::Index index)428 C2InterfaceHelper::Field<T>::Field(std::shared_ptr<FieldHelper> helper, C2Param::Index index)
429     : _mHelper(helper), _mField(helper->makeParamField(index)) { }
430 
431 template struct C2InterfaceHelper::Field<uint8_t>;
432 template struct C2InterfaceHelper::Field<char>;
433 template struct C2InterfaceHelper::Field<int32_t>;
434 template struct C2InterfaceHelper::Field<uint32_t>;
435 //template struct C2InterfaceHelper::Field<c2_cntr32_t>;
436 template struct C2InterfaceHelper::Field<int64_t>;
437 template struct C2InterfaceHelper::Field<uint64_t>;
438 //template struct C2InterfaceHelper::Field<c2_cntr64_t>;
439 template struct C2InterfaceHelper::Field<float>;
440 
441 /* --------------------------------- Factory --------------------------------- */
442 
443 struct C2InterfaceHelper::FactoryImpl : public C2InterfaceHelper::Factory {
getReflectorC2InterfaceHelper::FactoryImpl444     virtual std::shared_ptr<C2ParamReflector> getReflector() const override {
445         return _mReflector;
446     }
447 
448     virtual std::shared_ptr<ParamHelper>
getParamHelperC2InterfaceHelper::FactoryImpl449     getParamHelper(const ParamRef &param) const override {
450         return _mParams.find(param)->second;
451     }
452 
453 public:
FactoryImplC2InterfaceHelper::FactoryImpl454     FactoryImpl(std::shared_ptr<C2ParamReflector> reflector)
455         : _mReflector(reflector) { }
456 
457     virtual ~FactoryImpl() = default;
458 
addParamC2InterfaceHelper::FactoryImpl459     void addParam(std::shared_ptr<ParamHelper> param) {
460         _mParams.insert({ param->ref(), param });
461         _mIndexToHelper.insert({param->index(), param});
462 
463         // add down-dependencies (and validate dependencies as a result)
464         size_t ix = 0;
465         for (const ParamRef &ref : param->getDependenciesAsRefs()) {
466             // dependencies must already be defined
467             if (!_mParams.count(ref)) {
468                 C2_LOG(FATAL) << "Parameter " << param->name() << " has a dependency at index "
469                         << ix << " that is not yet defined";
470             }
471             _mParams.find(ref)->second->addDownDependency(param->index());
472             ++ix;
473         }
474 
475         _mDependencyIndex.emplace(param->index(), _mDependencyIndex.size());
476     }
477 
getParamC2InterfaceHelper::FactoryImpl478     std::shared_ptr<ParamHelper> getParam(C2Param::Index ix) const {
479         // TODO: handle streams separately
480         const auto it = _mIndexToHelper.find(ix);
481         if (it == _mIndexToHelper.end()) {
482             return nullptr;
483         }
484         return it->second;
485     }
486 
487     /**
488      * TODO: this could return a copy using proper pointer cast.
489      */
getParamValueC2InterfaceHelper::FactoryImpl490     std::shared_ptr<C2Param> getParamValue(C2Param::Index ix) const {
491         std::shared_ptr<ParamHelper> helper = getParam(ix);
492         return helper ? helper->value() : nullptr;
493     }
494 
querySupportedParamsC2InterfaceHelper::FactoryImpl495     c2_status_t querySupportedParams(
496             std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
497         for (const auto &it : _mParams) {
498             // TODO: change querySupportedParams signature?
499             params->push_back(
500                     std::const_pointer_cast<C2ParamDescriptor>(it.second->getDescriptor()));
501         }
502         // TODO: handle errors
503         return C2_OK;
504     }
505 
getDependencyIndexC2InterfaceHelper::FactoryImpl506     size_t getDependencyIndex(C2Param::Index ix) {
507         // in this version of the helper there is only a single stream so
508         // we can look up directly by index
509         auto it = _mDependencyIndex.find(ix);
510         return it == _mDependencyIndex.end() ? SIZE_MAX : it->second;
511     }
512 
513 private:
514     std::map<ParamRef, std::shared_ptr<ParamHelper>> _mParams;
515     std::map<C2Param::Index, std::shared_ptr<ParamHelper>> _mIndexToHelper;
516     std::shared_ptr<C2ParamReflector> _mReflector;
517     std::map<C2Param::Index, size_t> _mDependencyIndex;
518 };
519 
520 /* --------------------------------- Helper --------------------------------- */
521 
522 namespace {
523 
asString(C2Param * p)524 static std::string asString(C2Param *p) {
525     char addr[20];
526     sprintf(addr, "%p:[", p);
527     std::string v = addr;
528     for (size_t i = 0; i < p->size(); ++i) {
529         char d[4];
530         sprintf(d, " %02x", *(((uint8_t *)p) + i));
531         v += d + (i == 0);
532     }
533     return v + "]";
534 }
535 
536 }
537 
C2InterfaceHelper(std::shared_ptr<C2ReflectorHelper> reflector)538 C2InterfaceHelper::C2InterfaceHelper(std::shared_ptr<C2ReflectorHelper> reflector)
539     : mReflector(reflector),
540       _mFactory(std::make_shared<FactoryImpl>(reflector)) { }
541 
542 
GetBaseOffset(const std::shared_ptr<C2ParamReflector> & reflector,C2Param::CoreIndex index,size_t offset)543 size_t C2InterfaceHelper::GetBaseOffset(const std::shared_ptr<C2ParamReflector> &reflector,
544         C2Param::CoreIndex index, size_t offset) {
545     std::unique_ptr<C2StructDescriptor> param = reflector->describe(index);
546     if (param == nullptr) {
547         return ~(size_t)0; // param structure not described
548     }
549 
550     for (const C2FieldDescriptor &field : *param) {
551         size_t fieldOffset = _C2ParamInspector::GetOffset(field);
552         size_t fieldSize = _C2ParamInspector::GetSize(field);
553         size_t fieldExtent = field.extent();
554         if (offset < fieldOffset) {
555             return ~(size_t)0; // not found
556         }
557         if (offset == fieldOffset) {
558             // exact match
559             return offset;
560         }
561         if (field.extent() == 0 || offset < fieldOffset + fieldSize * fieldExtent) {
562             // reduce to first element in case of array
563             offset = fieldOffset + (offset - fieldOffset) % fieldSize;
564             if (field.type() >= C2FieldDescriptor::STRUCT_FLAG) {
565                 // this offset is within a field
566                 offset = GetBaseOffset(
567                         reflector, field.type() & ~C2FieldDescriptor::STRUCT_FLAG,
568                         offset - fieldOffset);
569                 return ~offset ? fieldOffset + offset : offset;
570             }
571         }
572     }
573     return ~(size_t)0; // not found
574 }
575 
addParameter(std::shared_ptr<ParamHelper> param)576 void C2InterfaceHelper::addParameter(std::shared_ptr<ParamHelper> param) {
577     std::lock_guard<std::mutex> lock(mMutex);
578     mReflector->addStructDescriptor(param->retrieveStructDescriptor());
579     c2_status_t err = param->validate(mReflector);
580     if (err != C2_CORRUPTED) {
581         _mFactory->addParam(param);
582 
583         // run setter to ensure correct values
584         bool changed = false;
585         std::vector<std::unique_ptr<C2SettingResult>> failures;
586         (void)param->trySet(param->value().get(), C2_MAY_BLOCK, &changed, *_mFactory, &failures);
587     }
588 }
589 
config(const std::vector<C2Param * > & params,c2_blocking_t mayBlock,std::vector<std::unique_ptr<C2SettingResult>> * const failures,bool updateParams,std::vector<std::shared_ptr<C2Param>> * changes __unused)590 c2_status_t C2InterfaceHelper::config(
591        const std::vector<C2Param*> &params, c2_blocking_t mayBlock,
592        std::vector<std::unique_ptr<C2SettingResult>>* const failures, bool updateParams,
593        std::vector<std::shared_ptr<C2Param>> *changes __unused /* TODO */) {
594     std::lock_guard<std::mutex> lock(mMutex);
595     bool paramWasInvalid = false; // TODO is this the same as bad value?
596     bool paramNotFound = false;
597     bool paramBadValue = false;
598     bool paramNoMemory = false;
599     bool paramBlocking = false;
600     bool paramTimedOut = false;
601     bool paramCorrupted = false;
602 
603     // dependencies
604     // down dependencies are marked dirty, but params set are not immediately
605     // marked dirty (unless they become down dependency) so that we can
606     // avoid setting them if they did not change
607 
608     // TODO: there could be multiple indices for the same dependency index
609     // { depIx, paramIx } may be a suitable key
610     std::map<size_t, std::pair<C2Param::Index, bool>> dependencies;
611 
612     std::vector<std::unique_ptr<C2Param>> paramRequests;
613     std::vector<C2Param*> lateReadParams;
614 
615     // we cannot determine the last valid parameter, so add an extra
616     // loop iteration after the last parameter
617     for (size_t p_ix = 0; p_ix <= params.size(); ++p_ix) {
618         C2Param *p = nullptr;
619         C2Param::Index paramIx = 0u;
620         size_t paramDepIx = SIZE_MAX;
621         bool last = p_ix == params.size();
622         if (!last) {
623             p = params[p_ix];
624             if (!*p) {
625                 paramWasInvalid = true;
626                 p->invalidate();
627                 continue;
628             }
629 
630             paramIx = p->index();
631 
632             // convert parameter to request in case this is a split parameter
633             C2Param::Index requestParamIx = paramIx | C2Param::CoreIndex::IS_REQUEST_FLAG;
634 
635             // setting a request directly is handled as normal
636             if (paramIx != requestParamIx) {
637                 paramDepIx = getDependencyIndex_l(requestParamIx);
638                 if (paramDepIx == SIZE_MAX) {
639                     // not a split parameter, handle it normally
640                     paramDepIx = getDependencyIndex_l(paramIx);
641                 } else {
642                     // split parameter - replace with setting for the request - and queue to
643                     // read back actual value
644                     // TODO: read late params at the right time
645                     lateReadParams.emplace_back(p);
646                     std::unique_ptr<C2Param> request(C2Param::CopyAsRequest(*p));
647                     p = request.get();
648                     paramIx = p->index();
649                     paramRequests.emplace_back(std::move(request));
650                 }
651             }
652 
653             if (paramDepIx == SIZE_MAX) {
654                 // unsupported parameter
655                 paramNotFound = true;
656                 continue;
657             }
658 
659             //
660             // first insert - mark not dirty
661             // it may have been marked dirty by a dependency update
662             // this does not overrwrite(!)
663             (void)dependencies.insert({ paramDepIx, { paramIx, false /* dirty */ }});
664             auto it = dependencies.find(paramDepIx);
665             C2_LOG(VERBOSE) << "marking dependency for setting at #" << paramDepIx << ": "
666                     << it->second.first << ", update "
667                     << (it->second.second ? "always (dirty)" : "only if changed");
668         } else {
669             // process any remaining dependencies
670             if (dependencies.empty()) {
671                 continue;
672             }
673             C2_LOG(VERBOSE) << "handling dirty down dependencies after last setting";
674         }
675 
676         // process any dirtied down-dependencies until the next param
677         while (dependencies.size() && dependencies.begin()->first <= paramDepIx) {
678             auto min = dependencies.begin();
679             C2Param::Index ix = min->second.first;
680             bool dirty = min->second.second;
681             dependencies.erase(min);
682 
683             std::shared_ptr<ParamHelper> param = _mFactory->getParam(ix);
684             C2_LOG(VERBOSE) << "old value " << asString(param->value().get());
685             if (!last) {
686                 C2_LOG(VERBOSE) << "new value " << asString(p);
687             }
688             if (!last && !dirty && ix == paramIx && *param->value() == *p) {
689                 // no change in value - and dependencies were not updated
690                 // no need to update
691                 C2_LOG(VERBOSE) << "ignoring setting unchanged param " << ix;
692                 continue;
693             }
694 
695             // apply setting
696             bool changed = false;
697             C2_LOG(VERBOSE) << "setting param " << ix;
698             std::shared_ptr<C2Param> oldValue = param->value();
699             c2_status_t res = param->trySet(
700                     (!last && paramIx == ix) ? p : param->value().get(), mayBlock,
701                     &changed, *_mFactory, failures);
702             std::shared_ptr<C2Param> newValue = param->value();
703             C2_CHECK_EQ(oldValue == newValue, *oldValue == *newValue);
704             switch (res) {
705                 case C2_OK: break;
706                 case C2_BAD_VALUE: paramBadValue = true; break;
707                 case C2_NO_MEMORY: paramNoMemory = true; break;
708                 case C2_TIMED_OUT: paramTimedOut = true; break;
709                 case C2_BLOCKING:  paramBlocking = true; break;
710                 case C2_CORRUPTED: paramCorrupted = true; break;
711                 default: ;// TODO fatal
712             }
713 
714             // copy back result for configured values (or invalidate if it does not fit or match)
715             if (updateParams && !last && paramIx == ix) {
716                 if (!p->updateFrom(*param->value())) {
717                     p->invalidate();
718                 }
719             }
720 
721             // compare ptrs as params are copy on write
722             if (changed) {
723                 C2_LOG(VERBOSE) << "param " << ix << " value changed";
724                 // value changed update down-dependencies and mark them dirty
725                 for (const C2Param::Index ix : param->getDownDependencies()) {
726                     C2_LOG(VERBOSE) << 1;
727                     auto insert_res = dependencies.insert(
728                             { getDependencyIndex_l(ix), { ix, true /* dirty */ }});
729                     if (!insert_res.second) {
730                         (*insert_res.first).second.second = true; // mark dirty
731                     }
732 
733                     auto it = dependencies.find(getDependencyIndex_l(ix));
734                     C2_CHECK(it->second.second);
735                     C2_LOG(VERBOSE) << "marking down dependencies to update at #"
736                             << getDependencyIndex_l(ix) << ": " << it->second.first;
737                 }
738             }
739         }
740     }
741 
742     // get late read parameters
743     for (C2Param *p : lateReadParams) {
744         std::shared_ptr<C2Param> value = _mFactory->getParamValue(p->index());
745         if (value) {
746             p->updateFrom(*value);
747         } else {
748             p->invalidate();
749         }
750     }
751 
752     return (paramCorrupted ? C2_CORRUPTED :
753             paramBlocking ? C2_BLOCKING :
754             paramTimedOut ? C2_TIMED_OUT :
755             paramNoMemory ? C2_NO_MEMORY :
756             (paramBadValue || paramWasInvalid) ? C2_BAD_VALUE :
757             paramNotFound ? C2_BAD_INDEX : C2_OK);
758 }
759 
getDependencyIndex_l(C2Param::Index ix) const760 size_t C2InterfaceHelper::getDependencyIndex_l(C2Param::Index ix) const {
761     return _mFactory->getDependencyIndex(ix);
762 }
763 
query(const std::vector<C2Param * > & stackParams,const std::vector<C2Param::Index> & heapParamIndices,c2_blocking_t mayBlock __unused,std::vector<std::unique_ptr<C2Param>> * const heapParams) const764 c2_status_t C2InterfaceHelper::query(
765         const std::vector<C2Param*> &stackParams,
766         const std::vector<C2Param::Index> &heapParamIndices,
767         c2_blocking_t mayBlock __unused /* TODO */,
768         std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
769     std::lock_guard<std::mutex> lock(mMutex);
770     bool paramWasInvalid = false;
771     bool paramNotFound = false;
772     bool paramDidNotFit = false;
773     bool paramNoMemory = false;
774 
775     for (C2Param* const p : stackParams) {
776         if (!*p) {
777             paramWasInvalid = true;
778             p->invalidate();
779         } else {
780             std::shared_ptr<C2Param> value = _mFactory->getParamValue(p->index());
781             if (!value) {
782                 paramNotFound = true;
783                 p->invalidate();
784             } else if (!p->updateFrom(*value)) {
785                 paramDidNotFit = true;
786                 p->invalidate();
787             }
788         }
789     }
790 
791     for (const C2Param::Index ix : heapParamIndices) {
792         std::shared_ptr<C2Param> value = _mFactory->getParamValue(ix);
793         if (value) {
794             std::unique_ptr<C2Param> p = C2Param::Copy(*value);
795             if (p != nullptr) {
796                 heapParams->push_back(std::move(p));
797             } else {
798                 heapParams->push_back(nullptr);
799                 paramNoMemory = true;
800             }
801         } else {
802             heapParams->push_back(nullptr);
803             paramNotFound = true;
804         }
805     }
806 
807     return paramNoMemory ? C2_NO_MEMORY :
808            paramNotFound ? C2_BAD_INDEX :
809            // the following errors are not marked in the return value
810            paramDidNotFit ? C2_OK :
811            paramWasInvalid ? C2_OK : C2_OK;
812 }
813 
querySupportedParams(std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const814 c2_status_t C2InterfaceHelper::querySupportedParams(
815         std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
816     std::lock_guard<std::mutex> lock(mMutex);
817     return _mFactory->querySupportedParams(params);
818 }
819 
820 
querySupportedValues(std::vector<C2FieldSupportedValuesQuery> & fields,c2_blocking_t mayBlock __unused) const821 c2_status_t C2InterfaceHelper::querySupportedValues(
822         std::vector<C2FieldSupportedValuesQuery> &fields, c2_blocking_t mayBlock __unused) const {
823     std::lock_guard<std::mutex> lock(mMutex);
824     for (C2FieldSupportedValuesQuery &query : fields) {
825         C2_LOG(VERBOSE) << "querying field " << query.field();
826         C2Param::Index ix = _C2ParamInspector::GetIndex(query.field());
827         std::shared_ptr<ParamHelper> param = _mFactory->getParam(ix);
828         if (!param) {
829             C2_LOG(VERBOSE) << "bad param";
830             query.status = C2_BAD_INDEX;
831             continue;
832         }
833         size_t offs = GetBaseOffset(
834                 mReflector, ix,
835                 _C2ParamInspector::GetOffset(query.field()) - sizeof(C2Param));
836         if (~offs == 0) {
837             C2_LOG(VERBOSE) << "field could not be found";
838             query.status = C2_NOT_FOUND;
839             continue;
840         }
841         offs += sizeof(C2Param);
842         C2_LOG(VERBOSE) << "field resolved to "
843                 << StringPrintf("@%02zx+%02x", offs, _C2ParamInspector::GetSize(query.field()));
844         std::shared_ptr<FieldHelper> field =
845             param->findField(offs, _C2ParamInspector::GetSize(query.field()));
846         if (!field) {
847             C2_LOG(VERBOSE) << "bad field";
848             query.status = C2_NOT_FOUND;
849             continue;
850         }
851 
852         const C2FieldSupportedValues *values = nullptr;
853         switch (query.type()) {
854         case C2FieldSupportedValuesQuery::CURRENT:
855             values = field->getSupportedValues();
856             break;
857         case C2FieldSupportedValuesQuery::POSSIBLE:
858             values = field->getPossibleValues();
859             break;
860         default:
861             C2_LOG(VERBOSE) << "bad query type: " << query.type();
862             query.status = C2_BAD_VALUE;
863         }
864         if (values) {
865             query.values = *values;
866             query.status = C2_OK;
867         } else {
868             C2_LOG(DEBUG) << "no values published by component";
869             query.status = C2_CORRUPTED;
870         }
871     }
872     return C2_OK;
873 }
874 
lock() const875 std::unique_lock<std::mutex> C2InterfaceHelper::lock() const {
876     return std::unique_lock<std::mutex>(mMutex);
877 }
878