Move Codec2-related code from hardware/google/av

Test: None
Bug: 112362730
Change-Id: Ie2f8ff431d65c40333f267ab9877d47089adeea4
diff --git a/media/codec2/vndk/util/C2Debug.cpp b/media/codec2/vndk/util/C2Debug.cpp
new file mode 100644
index 0000000..b4aa719
--- /dev/null
+++ b/media/codec2/vndk/util/C2Debug.cpp
@@ -0,0 +1,279 @@
+/*
+ * 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 <C2ParamInternal.h>
+#include <util/C2Debug-interface.h>
+#include <util/C2Debug-param.h>
+#include <util/C2InterfaceUtils.h>
+
+#include <iostream>
+
+#include <android-base/stringprintf.h>
+
+using android::base::StringPrintf;
+
+/* -------------------------------- asString -------------------------------- */
+
+const char *asString(c2_status_t i, const char *def) {
+    switch (i) {
+        case C2_OK:        return "OK";
+        case C2_BAD_VALUE: return "BAD_VALUE";
+        case C2_BAD_INDEX: return "BAD_INDEX";
+        case C2_CANNOT_DO: return "CANNOT_DO";
+        case C2_DUPLICATE: return "DUPLICATE";
+        case C2_NOT_FOUND: return "NOT_FOUND";
+        case C2_BAD_STATE: return "BAD_STATE";
+        case C2_BLOCKING:  return "BLOCKING";
+        case C2_CANCELED:  return "CANCELED";
+        case C2_NO_MEMORY: return "NO_MEMORY";
+        case C2_REFUSED:   return "REFUSED";
+        case C2_TIMED_OUT: return "TIMED_OUT";
+        case C2_OMITTED:   return "OMITTED";
+        case C2_CORRUPTED: return "CORRUPTED";
+        case C2_NO_INIT:   return "NO_INIT";
+        default:           return def;
+    }
+}
+
+const char *asString(C2FieldDescriptor::type_t i, const char *def) {
+    switch (i) {
+        case C2FieldDescriptor::BLOB:   return "u8";
+        case C2FieldDescriptor::CNTR32: return "c32";
+        case C2FieldDescriptor::CNTR64: return "c64";
+        case C2FieldDescriptor::FLOAT:  return "fp";
+        case C2FieldDescriptor::INT32:  return "i32";
+        case C2FieldDescriptor::INT64:  return "i64";
+        case C2FieldDescriptor::STRING: return "chr";
+        case C2FieldDescriptor::UINT32: return "u32";
+        case C2FieldDescriptor::UINT64: return "u64";
+        default: return (i & C2FieldDescriptor::STRUCT_FLAG) ? "struct" : def;
+    }
+}
+
+/* ------------------------------ C2ParamField ------------------------------ */
+
+static std::string attribParamCoreIndex(const C2Param::CoreIndex &i) {
+    return StringPrintf("%c%c%03x",
+            i.isFlexible() ? 'F' : '-',
+            i.isVendor() ? 'V' : '-',
+            i.coreIndex());
+}
+
+static std::string attribParamIndex(
+        const C2Param::Type &i, bool addStream, unsigned streamId) {
+    std::string v = StringPrintf("%c%c",
+            i.forInput() ? 'I' : i.forOutput() ? 'O' : '-',
+            i.forStream() ? 'S' : i.forPort() ? 'P' : 'G');
+    if (addStream) {
+        if (i.forStream()) {
+            v += StringPrintf("%02d", streamId);
+        } else {
+            v += "--";
+        }
+    }
+
+    return v
+            + StringPrintf("%c ",
+                       i.kind() == C2Param::STRUCT  ? 'S' :
+                       i.kind() == C2Param::INFO    ? 'i' :
+                       i.kind() == C2Param::TUNING  ? 't' :
+                       i.kind() == C2Param::SETTING ? 's' :
+                       i.kind() == C2Param::NONE    ? '-' : '?')
+            + attribParamCoreIndex(i);
+}
+
+std::ostream& operator<<(std::ostream& os, const C2Param::CoreIndex &i) {
+    return os << "Param::CoreIndex(" << attribParamCoreIndex(i) << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const C2Param::Type &i) {
+    return os << StringPrintf("Param::Type(%08x: ", i.type())
+            << attribParamIndex(i, false, 0) << ")";
+}
+
+std::ostream& operator<<(std::ostream& os, const C2Param::Index &i) {
+    return os << StringPrintf("Param::Index(%08x: ", (uint32_t)i)
+            << attribParamIndex(i, true, i.stream()) << ")";
+}
+
+static std::string attribFieldId(const _C2FieldId &i) {
+    return StringPrintf("Field(@%02x+%02x)",
+            _C2ParamInspector::GetOffset(i),
+            _C2ParamInspector::GetSize(i));
+}
+
+
+std::ostream& operator<<(std::ostream& os, const _C2FieldId &i) {
+    return os << "<" << attribFieldId(i) << ">";
+}
+
+
+std::ostream& operator<<(std::ostream& os, const C2FieldDescriptor &i) {
+    os << attribFieldId(_C2ParamInspector::GetField(i)) << " ";
+    if (i.namedValues().size()) {
+        os << "enum ";
+    }
+    return os << asString(i.type()) << " " << i.name()
+            << StringPrintf("[%zu]", i.extent());
+}
+
+
+std::ostream& operator<<(std::ostream& os, const C2ParamField &i) {
+    os << "<" << C2Param::Index(_C2ParamInspector::GetIndex(i))
+            << StringPrintf("::Field(@%02x+%02x)>",
+                            _C2ParamInspector::GetOffset(i),
+                            _C2ParamInspector::GetSize(i));
+    return os;
+}
+
+
+/* -------------------------- _C2FieldValueHelper -------------------------- */
+
+std::ostream& _C2FieldValueHelper<char>::put(std::ostream &os, const C2Value::Primitive &p) {
+    if (isprint(p.i32)) {
+        return os << StringPrintf("'%c'", p.i32);
+    } else {
+        return os << StringPrintf("'\\x%02x'", (uint32_t)p.i32);
+    }
+}
+
+std::ostream& _C2FieldValueHelper<uint8_t>::put(std::ostream &os, const C2Value::Primitive &p) {
+    return os << StringPrintf("0x%02x", p.u32);
+}
+
+/* ---------------------- C2FieldSupportedValuesHelper ---------------------- */
+
+template<typename T>
+std::ostream& operator<<(std::ostream &os, const c2_cntr_t<T> &v) {
+    return os << "ctr(" << v.peeku() << ")";
+}
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2SupportedRange<T> &i) {
+    os << "Range(";
+    _C2FieldValueHelper<T>::put(os, i.min());
+    os << "..";
+    _C2FieldValueHelper<T>::put(os, i.max());
+    os << " *= " << i.num() << " /= " << i.denom() << " += " << i.step() << ")";
+    return os;
+}
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<char> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<uint8_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<int32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<uint32_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<c2_cntr32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<int64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<uint64_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<c2_cntr64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedRange<float> &i);
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<T> &i) {
+    os << "Flags[";
+    if (!i.isEmpty()) {
+        os << "min=";
+        _C2FieldValueHelper<T>::put(os, i.min());
+    }
+    bool comma = false;
+    for (const T &v : i.flags()) {
+        if (comma) {
+            os << ", ";
+        }
+        _C2FieldValueHelper<T>::put(os, v);
+        comma = true;
+    }
+    os << "]";
+    return os;
+}
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<char> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<uint8_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<int32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<uint32_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<c2_cntr32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<int64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<uint64_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<c2_cntr64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedFlags<float> &i);
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<T> &i) {
+    os << "Values[";
+    bool comma = false;
+    for (const T &v : i.values()) {
+        if (comma) {
+            os << ", ";
+        }
+        _C2FieldValueHelper<T>::put(os, v);
+        comma = true;
+    }
+    os << "]";
+    return os;
+}
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<char> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<uint8_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<int32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<uint32_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<c2_cntr32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<int64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<uint64_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<c2_cntr64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2SupportedValueSet<float> &i);
+
+template<typename T>
+struct C2FieldSupportedValuesHelper<T>::Impl {
+    Impl(const C2FieldSupportedValues &values);
+
+private:
+    typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
+    C2FieldSupportedValues::type_t _mType;
+    C2SupportedRange<ValueType> _mRange;
+    C2SupportedValueSet<ValueType> _mValues;
+    C2SupportedFlags<ValueType> _mFlags;
+
+public:
+//    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>
+std::ostream& C2FieldSupportedValuesHelper<T>::Impl::streamOut(std::ostream& os) const {
+    if (_mType == C2FieldSupportedValues::RANGE) {
+        os << _mRange;
+    } else if (_mType == C2FieldSupportedValues::VALUES) {
+        os << _mValues;
+    } else if (_mType == C2FieldSupportedValues::FLAGS) {
+        os << _mFlags;
+    } else {
+        os << "Unknown FSV type: " << (uint32_t)_mType;
+    }
+    return os;
+}
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<T> &i) {
+    return i._mImpl->streamOut(os);
+}
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<char> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<uint8_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<int32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<uint32_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<c2_cntr32_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<int64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<uint64_t> &i);
+//template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<c2_cntr64_t> &i);
+template std::ostream& operator<<(std::ostream& os, const C2FieldSupportedValuesHelper<float> &i);
+
diff --git a/media/codec2/vndk/util/C2InterfaceHelper.cpp b/media/codec2/vndk/util/C2InterfaceHelper.cpp
new file mode 100644
index 0000000..e447fbe
--- /dev/null
+++ b/media/codec2/vndk/util/C2InterfaceHelper.cpp
@@ -0,0 +1,841 @@
+/*
+ * 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 <C2Debug.h>
+#include <C2ParamInternal.h>
+#include <util/C2InterfaceHelper.h>
+
+#include <android-base/stringprintf.h>
+
+using ::android::base::StringPrintf;
+
+/* --------------------------------- ReflectorHelper --------------------------------- */
+
+void C2ReflectorHelper::addStructDescriptors(
+        std::vector<C2StructDescriptor> &structs, _Tuple<> *) {
+    std::lock_guard<std::mutex> lock(_mMutex);
+    for (C2StructDescriptor &strukt : structs) {
+        // TODO: check if structure descriptions conflict with existing ones
+        addStructDescriptor(std::move(strukt));
+    }
+}
+
+std::unique_ptr<C2StructDescriptor>
+C2ReflectorHelper::describe(C2Param::CoreIndex paramIndex) const {
+    std::lock_guard<std::mutex> lock(_mMutex);
+    auto it = _mStructs.find(paramIndex);
+    if (it == _mStructs.end()) {
+        return nullptr;
+    } else {
+        return std::make_unique<C2StructDescriptor>(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<C2ParamDescriptor>(
+                index(), (C2ParamDescriptor::attrib_t)mAttrib,
+                std::move(mName), std::move(mDependencies));
+    }
+
+    void createFieldsAndSupportedValues(const std::shared_ptr<C2ParamReflector> &reflector) {
+        for (const C2FieldUtils::Info &f :
+                C2FieldUtils::enumerateFields(*mDefaultValue, reflector)) {
+            if (!f.isArithmetic()) {
+                continue;
+            }
+            std::unique_ptr<C2FieldSupportedValues> fsvPointer;
+
+            // create a breakable structure
+            do {
+                C2FieldSupportedValues fsv;
+                switch (f.type()) {
+                    case C2FieldDescriptor::INT32:  fsv = C2SupportedRange<int32_t>::Any(); break;
+                    case C2FieldDescriptor::UINT32: fsv = C2SupportedRange<uint32_t>::Any(); break;
+                    case C2FieldDescriptor::INT64:  fsv = C2SupportedRange<int64_t>::Any(); break;
+                    case C2FieldDescriptor::UINT64: fsv = C2SupportedRange<uint64_t>::Any(); break;
+                    case C2FieldDescriptor::FLOAT:  fsv = C2SupportedRange<float>::Any(); break;
+                    case C2FieldDescriptor::BLOB:   fsv = C2SupportedRange<uint8_t>::Any(); break;
+                    case C2FieldDescriptor::STRING: fsv = C2SupportedRange<char>::Any(); break;
+                default:
+                    continue; // break out of do {} while
+                }
+                fsvPointer = std::make_unique<C2FieldSupportedValues>(fsv);
+            } while (false);
+
+            mFields.emplace_hint(
+                    mFields.end(),
+                    _C2FieldId(f.offset(), f.size()),
+                    std::make_shared<FieldHelper>(
+                            mParam, _C2FieldId(f.offset(), f.size()), std::move(fsvPointer)));
+        }
+    }
+
+    /**
+     * Finds a field descriptor.
+     */
+    std::shared_ptr<FieldHelper> 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<ParamRef> getDependenciesAsRefs() const {
+        return mDependenciesAsRefs;
+    }
+
+    std::shared_ptr<const C2ParamDescriptor> getDescriptor() const {
+        return mDescriptor;
+    }
+
+    const std::vector<C2Param::Index> 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<C2Param> default_) {
+        mDefaultValue = default_;
+    }
+
+    void setDependencies(std::vector<C2Param::Index> indices, std::vector<ParamRef> refs) {
+        mDependencies = indices;
+        mDependenciesAsRefs = refs;
+    }
+
+    void setFields(std::vector<C2ParamFieldValues> &&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<FieldHelper>(mParam,
+                                            _C2ParamInspector::GetField(pfv.paramOrField),
+                                            std::move(pfv.values)));
+        }
+    }
+
+    void setGetter(std::function<std::shared_ptr<C2Param>(bool)> getter) {
+        mGetter = getter;
+    }
+
+    void setSetter(std::function<C2R(const C2Param *, bool, bool *, Factory &)> setter) {
+        mSetter = setter;
+    }
+
+    c2_status_t trySet(
+            const C2Param *value, bool mayBlock, bool *changed, Factory &f,
+            std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+        C2R result = mSetter(value, mayBlock, changed, f);
+        return result.retrieveFailures(failures);
+    }
+
+    c2_status_t validate(const std::shared_ptr<C2ParamReflector> &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<C2Param> value() {
+        return mParam.get();
+    }
+
+    std::shared_ptr<const C2Param> value() const {
+        return mParam.get();
+    }
+
+private:
+    typedef _C2ParamInspector::attrib_t attrib_t;
+    ParamRef mParam;
+    C2String mName;
+    C2StructDescriptor _mStruct;
+    std::shared_ptr<C2Param> mDefaultValue;
+    attrib_t mAttrib;
+    std::function<C2R(const C2Param *, bool, bool *, Factory &)> mSetter;
+    std::function<std::shared_ptr<C2Param>(bool)> mGetter;
+    std::vector<C2Param::Index> mDependencies;
+    std::vector<ParamRef> mDependenciesAsRefs;
+    std::vector<C2Param::Index> mDownDependencies; // TODO: this does not work for stream dependencies
+    std::map<_C2FieldId, std::shared_ptr<FieldHelper>> mFields;
+    std::shared_ptr<C2ParamDescriptor> mDescriptor;
+};
+
+C2InterfaceHelper::ParamHelper::ParamHelper(
+        ParamRef param, C2StringLiteral name, C2StructDescriptor &&strukt)
+    : mImpl(std::make_unique<C2InterfaceHelper::ParamHelper::Impl>(
+            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> C2InterfaceHelper::ParamHelper::build() {
+    mImpl->build();
+    return std::make_shared<C2InterfaceHelper::ParamHelper>(std::move(*this));
+}
+
+std::shared_ptr<C2InterfaceHelper::FieldHelper>
+C2InterfaceHelper::ParamHelper::findField(size_t baseOffs, size_t baseSize) const {
+    return mImpl->findField(baseOffs, baseSize);
+}
+
+const std::vector<C2InterfaceHelper::ParamRef>
+C2InterfaceHelper::ParamHelper::getDependenciesAsRefs() const {
+    return mImpl->getDependenciesAsRefs();
+}
+
+std::shared_ptr<const C2ParamDescriptor>
+C2InterfaceHelper::ParamHelper::getDescriptor() const {
+    return mImpl->getDescriptor();
+}
+
+const std::vector<C2Param::Index> 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<C2Param> default_) {
+    mImpl->setDefaultValue(default_);
+}
+
+void C2InterfaceHelper::ParamHelper::setDependencies(
+        std::vector<C2Param::Index> indices, std::vector<ParamRef> refs) {
+    mImpl->setDependencies(indices, refs);
+}
+
+void C2InterfaceHelper::ParamHelper::setFields(std::vector<C2ParamFieldValues> &&fields) {
+    return mImpl->setFields(std::move(fields));
+}
+
+void C2InterfaceHelper::ParamHelper::setGetter(
+        std::function<std::shared_ptr<C2Param>(bool)> getter) {
+    mImpl->setGetter(getter);
+}
+
+void C2InterfaceHelper::ParamHelper::setSetter(
+        std::function<C2R(const C2Param *, bool, bool *, Factory &)> setter) {
+    mImpl->setSetter(setter);
+}
+
+c2_status_t C2InterfaceHelper::ParamHelper::trySet(
+        const C2Param *value, bool mayBlock, bool *changed, Factory &f,
+        std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+    return mImpl->trySet(value, mayBlock, changed, f, failures);
+}
+
+c2_status_t C2InterfaceHelper::ParamHelper::validate(
+        const std::shared_ptr<C2ParamReflector> &reflector) {
+    return mImpl->validate(reflector);
+}
+
+std::shared_ptr<C2Param> C2InterfaceHelper::ParamHelper::value() {
+    return mImpl->value();
+}
+
+std::shared_ptr<const C2Param> 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 &param, const _C2FieldId &field,
+            std::unique_ptr<C2FieldSupportedValues> &&values)
+    : mParam(param),
+      mFieldId(field),
+      mPossible(std::move(values)) {
+    C2_LOG(VERBOSE) << "Creating field helper " << field << " "
+            << C2FieldSupportedValuesHelper<uint32_t>(*mPossible);
+}
+
+void C2InterfaceHelper::FieldHelper::setSupportedValues(
+        std::unique_ptr<C2FieldSupportedValues> &&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<typename T>
+struct SupportedValuesBuilder : C2ParamFieldValuesBuilder<T> {
+    SupportedValuesBuilder(
+            C2ParamField &field, std::shared_ptr<C2InterfaceHelper::FieldHelper> helper)
+        : C2ParamFieldValuesBuilder<T>(field), _mHelper(helper), _mField(field) {
+    }
+
+    /**
+     * Save builder values on destruction.
+     */
+    virtual ~SupportedValuesBuilder() override {
+        _mHelper->setSupportedValues(std::move(C2ParamFieldValues(*this).values));
+    }
+
+private:
+    std::shared_ptr<C2InterfaceHelper::FieldHelper> _mHelper;
+    C2ParamField _mField;
+};
+
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> C2InterfaceHelper::Field<T>::shouldBe() const {
+    return C2ParamFieldValuesBuilder<T>(_mField);
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> C2InterfaceHelper::Field<T>::mustBe() {
+    return SupportedValuesBuilder<T>(_mField, _mHelper);
+}
+
+/*
+template<typename T> C2SettingResultsBuilder C2InterfaceHelper::Field<T>::validatePossible(T &value)
+const {
+    /// TODO
+    return C2SettingResultsBuilder::Ok();
+}
+*/
+
+template<typename T>
+C2InterfaceHelper::Field<T>::Field(std::shared_ptr<FieldHelper> helper, C2Param::Index index)
+    : _mHelper(helper), _mField(helper->makeParamField(index)) { }
+
+template struct C2InterfaceHelper::Field<uint8_t>;
+template struct C2InterfaceHelper::Field<char>;
+template struct C2InterfaceHelper::Field<int32_t>;
+template struct C2InterfaceHelper::Field<uint32_t>;
+//template struct C2InterfaceHelper::Field<c2_cntr32_t>;
+template struct C2InterfaceHelper::Field<int64_t>;
+template struct C2InterfaceHelper::Field<uint64_t>;
+//template struct C2InterfaceHelper::Field<c2_cntr64_t>;
+template struct C2InterfaceHelper::Field<float>;
+
+/* --------------------------------- Factory --------------------------------- */
+
+struct C2InterfaceHelper::FactoryImpl : public C2InterfaceHelper::Factory {
+    virtual std::shared_ptr<C2ParamReflector> getReflector() const override {
+        return _mReflector;
+    }
+
+    virtual std::shared_ptr<ParamHelper>
+    getParamHelper(const ParamRef &param) const override {
+        return _mParams.find(param)->second;
+    }
+
+public:
+    FactoryImpl(std::shared_ptr<C2ParamReflector> reflector)
+        : _mReflector(reflector) { }
+
+    virtual ~FactoryImpl() = default;
+
+    void addParam(std::shared_ptr<ParamHelper> 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<ParamHelper> 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<C2Param> getParamValue(C2Param::Index ix) const {
+        std::shared_ptr<ParamHelper> helper = getParam(ix);
+        return helper ? helper->value() : nullptr;
+    }
+
+    c2_status_t querySupportedParams(
+            std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
+        for (const auto &it : _mParams) {
+            // TODO: change querySupportedParams signature?
+            params->push_back(
+                    std::const_pointer_cast<C2ParamDescriptor>(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<ParamRef, std::shared_ptr<ParamHelper>> _mParams;
+    std::map<C2Param::Index, std::shared_ptr<ParamHelper>> _mIndexToHelper;
+    std::shared_ptr<C2ParamReflector> _mReflector;
+    std::map<C2Param::Index, size_t> _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<C2ReflectorHelper> reflector)
+    : mReflector(reflector),
+      _mFactory(std::make_shared<FactoryImpl>(reflector)) { }
+
+
+size_t C2InterfaceHelper::GetBaseOffset(const std::shared_ptr<C2ParamReflector> &reflector,
+        C2Param::CoreIndex index, size_t offset) {
+    std::unique_ptr<C2StructDescriptor> 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<ParamHelper> param) {
+    std::lock_guard<std::mutex> 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<std::unique_ptr<C2SettingResult>> failures;
+        (void)param->trySet(param->value().get(), C2_MAY_BLOCK, &changed, *_mFactory, &failures);
+    }
+}
+
+c2_status_t C2InterfaceHelper::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 /* TODO */) {
+    std::lock_guard<std::mutex> 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<size_t, std::pair<C2Param::Index, bool>> 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<ParamHelper> 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<C2Param> oldValue = param->value();
+            c2_status_t res = param->trySet(
+                    (!last && paramIx == ix) ? p : param->value().get(), mayBlock,
+                    &changed, *_mFactory, failures);
+            std::shared_ptr<C2Param> 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<C2Param*> &stackParams,
+        const std::vector<C2Param::Index> &heapParamIndices,
+        c2_blocking_t mayBlock __unused /* TODO */,
+        std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
+    std::lock_guard<std::mutex> 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<C2Param> 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<C2Param> value = _mFactory->getParamValue(ix);
+        if (value) {
+            std::unique_ptr<C2Param> 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<std::shared_ptr<C2ParamDescriptor>> *const params) const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return _mFactory->querySupportedParams(params);
+}
+
+
+c2_status_t C2InterfaceHelper::querySupportedValues(
+        std::vector<C2FieldSupportedValuesQuery> &fields, c2_blocking_t mayBlock __unused) const {
+    std::lock_guard<std::mutex> lock(mMutex);
+    for (C2FieldSupportedValuesQuery &query : fields) {
+        C2_LOG(VERBOSE) << "querying field " << query.field();
+        C2Param::Index ix = _C2ParamInspector::GetIndex(query.field());
+        std::shared_ptr<ParamHelper> 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<FieldHelper> 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<std::mutex> C2InterfaceHelper::lock() const {
+    return std::unique_lock<std::mutex>(mMutex);
+}
diff --git a/media/codec2/vndk/util/C2InterfaceUtils.cpp b/media/codec2/vndk/util/C2InterfaceUtils.cpp
new file mode 100644
index 0000000..61ec911
--- /dev/null
+++ b/media/codec2/vndk/util/C2InterfaceUtils.cpp
@@ -0,0 +1,1292 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wunused-variable"
+#pragma clang diagnostic ignored "-Wunused-value"
+
+#define C2_LOG_VERBOSE
+
+#include <C2Debug.h>
+#include <C2Param.h>
+#include <C2ParamDef.h>
+#include <C2ParamInternal.h>
+#include <util/C2InterfaceUtils.h>
+
+#include <cmath>
+#include <limits>
+#include <map>
+#include <type_traits>
+
+#include <android-base/stringprintf.h>
+
+std::ostream& operator<<(std::ostream& os, const _C2FieldId &i);
+
+std::ostream& operator<<(std::ostream& os, const C2ParamField &i);
+
+/* ---------------------------- C2SupportedRange ---------------------------- */
+
+/**
+ * Helper class for supported values range calculations.
+ */
+template<typename T, bool FP=std::is_floating_point<T>::value>
+struct _C2TypedSupportedRangeHelper {
+    /**
+     * type of range size: a - b if a >= b and a and b are of type T
+     */
+    typedef typename std::make_unsigned<T>::type DiffType;
+
+    /**
+     * calculate (high - low) mod step
+     */
+    static DiffType mod(T low, T high, T step) {
+        return DiffType(high - low) % DiffType(step);
+    }
+};
+
+template<typename T>
+struct _C2TypedSupportedRangeHelper<T, true> {
+    typedef T DiffType;
+
+    static DiffType mod(T low, T high, T step) {
+        return fmod(high - low, step);
+    }
+};
+
+template<typename T>
+C2SupportedRange<T>::C2SupportedRange(const C2FieldSupportedValues &values) {
+    if (values.type == C2FieldSupportedValues::RANGE) {
+        _mMin = values.range.min.ref<ValueType>();
+        _mMax = values.range.max.ref<ValueType>();
+        _mStep = values.range.step.ref<ValueType>();
+        _mNum = values.range.num.ref<ValueType>();
+        _mDenom = values.range.denom.ref<ValueType>();
+    } else {
+        _mMin = MAX_VALUE;
+        _mMax = MIN_VALUE;
+        _mStep = MIN_STEP;
+        _mNum = 0;
+        _mDenom = 0;
+    }
+}
+
+template<typename T>
+bool C2SupportedRange<T>::contains(T value) const {
+    // value must fall between min and max
+    if (value < _mMin || value > _mMax) {
+        return false;
+    }
+    // simple ranges contain all values between min and max
+    if (isSimpleRange()) {
+        return true;
+    }
+    // min is always part of the range
+    if (value == _mMin) {
+        return true;
+    }
+    // stepped ranges require (val - min) % step to be zero
+    if (isArithmeticSeries()) {
+        return _C2TypedSupportedRangeHelper<T>::mod(_mMin, value, _mStep) == 0;
+    }
+    // pure geometric series require (val / min) to be integer multiple of (num/denom)
+    if (isGeometricSeries()) {
+        if (value <= 0) {
+            return false;
+        }
+        double log2base = log2(_mNum / _mDenom);
+        double power = llround(log2(value / double(_mMin)) / log2base);
+        // TODO: validate that result falls within precision (other than round)
+        return value == T(_mMin * pow(_mNum / _mDenom, power) + MIN_STEP / 2);
+    }
+    // multiply-accumulate series require validating by walking through the series
+    if (isMacSeries()) {
+        double lastValue = _mMin;
+        double base = _mNum / _mDenom;
+        while (true) {
+            // this cast is safe as _mMin <= lastValue <= _mMax
+            if (T(lastValue + MIN_STEP / 2) == value) {
+                return true;
+            }
+            double nextValue = fma(lastValue, base, _mStep);
+            if (nextValue <= lastValue || nextValue > _mMax) {
+                return false; // series is no longer monotonic or within range
+            }
+            lastValue = nextValue;
+        };
+    }
+    // if we are here, this must be an invalid range
+    return false;
+}
+
+template<typename T>
+C2SupportedRange<T> C2SupportedRange<T>::limitedTo(const C2SupportedRange<T> &limit) const {
+    // TODO - this only works for simple ranges
+    return C2SupportedRange(std::max(_mMin, limit._mMin), std::min(_mMax, limit._mMax),
+                                 std::max(_mStep, limit._mStep));
+}
+
+template class C2SupportedRange<uint8_t>;
+template class C2SupportedRange<char>;
+template class C2SupportedRange<int32_t>;
+template class C2SupportedRange<uint32_t>;
+//template class C2SupportedRange<c2_cntr32_t>;
+template class C2SupportedRange<int64_t>;
+template class C2SupportedRange<uint64_t>;
+//template class C2SupportedRange<c2_cntr64_t>;
+template class C2SupportedRange<float>;
+
+/* -------------------------- C2SupportedFlags -------------------------- */
+
+/**
+ * Ordered supported flag set for a field of a given type.
+ */
+// float flags are not supported, but define a few methods to support generic supported values code
+template<>
+bool C2SupportedFlags<float>::contains(float value) const {
+    return false;
+}
+
+template<>
+const std::vector<float> C2SupportedFlags<float>::flags() const {
+    return std::vector<float>();
+}
+
+template<>
+C2SupportedFlags<float> C2SupportedFlags<float>::limitedTo(const C2SupportedFlags<float> &limit) const {
+    std::vector<C2Value::Primitive> values;
+    return C2SupportedFlags(std::move(values));
+}
+
+template<>
+float C2SupportedFlags<float>::min() const {
+    return 0;
+}
+
+template<typename T>
+bool C2SupportedFlags<T>::contains(T value) const {
+    // value must contain the minimal mask
+    T minMask = min();
+    if (~value & minMask) {
+        return false;
+    }
+    value &= ~minMask;
+    // otherwise, remove flags from value and see if we arrive at 0
+    for (const C2Value::Primitive &v : _mValues) {
+        if (value == 0) {
+            break;
+        }
+        if ((~value & v.ref<ValueType>()) == 0) {
+            value &= ~v.ref<ValueType>();
+        }
+    }
+    return value == 0;
+}
+
+template<typename T>
+const std::vector<T> C2SupportedFlags<T>::flags() const {
+    std::vector<T> vals(c2_max(_mValues.size(), 1u) - 1);
+    if (!_mValues.empty()) {
+        std::transform(_mValues.cbegin() + 1, _mValues.cend(), vals.begin(),
+                       [](const C2Value::Primitive &p)->T {
+            return p.ref<ValueType>();
+        });
+    }
+    return vals;
+}
+
+template<typename T>
+C2SupportedFlags<T> C2SupportedFlags<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
+    std::vector<C2Value::Primitive> values = _mValues; // make a copy
+    T minMask = min() | limit.min();
+    // minimum mask must be covered by both this and other
+    if (limit.contains(minMask) && contains(minMask)) {
+        values[0] = minMask;
+        // keep only flags that are covered by limit
+        std::remove_if(values.begin(), values.end(), [&limit, minMask](const C2Value::Primitive &v) -> bool {
+            T value = v.ref<ValueType>() | minMask;
+            return value == minMask || !limit.contains(value); });
+        // we also need to do it vice versa
+        for (const C2Value::Primitive &v : _mValues) {
+            T value = v.ref<ValueType>() | minMask;
+            if (value != minMask && contains(value)) {
+                values.emplace_back((ValueType)value);
+            }
+        }
+    }
+    return C2SupportedFlags(std::move(values));
+}
+
+template<typename T>
+T C2SupportedFlags<T>::min() const {
+    if (!_mValues.empty()) {
+        return _mValues.front().template ref<ValueType>();
+    } else {
+        return T(0);
+    }
+}
+
+template class C2SupportedFlags<uint8_t>;
+template class C2SupportedFlags<char>;
+template class C2SupportedFlags<int32_t>;
+template class C2SupportedFlags<uint32_t>;
+//template class C2SupportedFlags<c2_cntr32_t>;
+template class C2SupportedFlags<int64_t>;
+template class C2SupportedFlags<uint64_t>;
+//template class C2SupportedFlags<c2_cntr64_t>;
+
+/* -------------------------- C2SupportedValueSet -------------------------- */
+
+/**
+ * Ordered supported value set for a field of a given type.
+ */
+template<typename T>
+bool C2SupportedValueSet<T>::contains(T value) const {
+    return std::find_if(_mValues.cbegin(), _mValues.cend(),
+            [value](const C2Value::Primitive &p) -> bool {
+                return value == p.ref<ValueType>();
+            }) != _mValues.cend();
+}
+
+template<typename T>
+C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedValueSet<T> &limit) const {
+    std::vector<C2Value::Primitive> values = _mValues; // make a copy
+    std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
+        return !limit.contains(v.ref<ValueType>()); });
+    return C2SupportedValueSet(std::move(values));
+}
+
+template<typename T>
+C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedRange<T> &limit) const {
+    std::vector<C2Value::Primitive> values = _mValues; // make a copy
+    std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
+        return !limit.contains(v.ref<ValueType>()); });
+    return C2SupportedValueSet(std::move(values));
+}
+
+template<typename T>
+C2SupportedValueSet<T> C2SupportedValueSet<T>::limitedTo(const C2SupportedFlags<T> &limit) const {
+    std::vector<C2Value::Primitive> values = _mValues; // make a copy
+    std::remove_if(values.begin(), values.end(), [&limit](const C2Value::Primitive &v) -> bool {
+        return !limit.contains(v.ref<ValueType>()); });
+    return C2SupportedValueSet(std::move(values));
+}
+
+template<typename T>
+const std::vector<T> C2SupportedValueSet<T>::values() const {
+    std::vector<T> vals(_mValues.size());
+    std::transform(_mValues.cbegin(), _mValues.cend(), vals.begin(), [](const C2Value::Primitive &p) -> T {
+        return p.ref<ValueType>();
+    });
+    return vals;
+}
+
+template class C2SupportedValueSet<uint8_t>;
+template class C2SupportedValueSet<char>;
+template class C2SupportedValueSet<int32_t>;
+template class C2SupportedValueSet<uint32_t>;
+//template class C2SupportedValueSet<c2_cntr32_t>;
+template class C2SupportedValueSet<int64_t>;
+template class C2SupportedValueSet<uint64_t>;
+//template class C2SupportedValueSet<c2_cntr64_t>;
+template class C2SupportedValueSet<float>;
+
+/* ---------------------- C2FieldSupportedValuesHelper ---------------------- */
+
+template<typename T>
+struct C2FieldSupportedValuesHelper<T>::Impl {
+    Impl(const C2FieldSupportedValues &values)
+        : _mType(values.type),
+          _mRange(values),
+          _mValues(values),
+          _mFlags(values) { }
+
+    bool supports(T value) const;
+
+private:
+    typedef typename _C2FieldValueHelper<T>::ValueType ValueType;
+    C2FieldSupportedValues::type_t _mType;
+    C2SupportedRange<ValueType> _mRange;
+    C2SupportedValueSet<ValueType> _mValues;
+    C2SupportedValueSet<ValueType> _mFlags;
+
+//    friend std::ostream& operator<< <T>(std::ostream& os, const C2FieldSupportedValuesHelper<T>::Impl &i);
+//    friend std::ostream& operator<<(std::ostream& os, const Impl &i);
+    std::ostream& streamOut(std::ostream& os) const;
+};
+
+template<typename T>
+bool C2FieldSupportedValuesHelper<T>::Impl::supports(T value) const {
+    switch (_mType) {
+        case C2FieldSupportedValues::RANGE: return _mRange.contains(value);
+        case C2FieldSupportedValues::VALUES: return _mValues.contains(value);
+        case C2FieldSupportedValues::FLAGS: return _mFlags.contains(value);
+        default: return false;
+    }
+}
+
+template<typename T>
+C2FieldSupportedValuesHelper<T>::C2FieldSupportedValuesHelper(const C2FieldSupportedValues &values)
+    : _mImpl(std::make_unique<C2FieldSupportedValuesHelper<T>::Impl>(values)) { }
+
+template<typename T>
+C2FieldSupportedValuesHelper<T>::~C2FieldSupportedValuesHelper() = default;
+
+template<typename T>
+bool C2FieldSupportedValuesHelper<T>::supports(T value) const {
+    return _mImpl->supports(value);
+}
+
+template class C2FieldSupportedValuesHelper<uint8_t>;
+template class C2FieldSupportedValuesHelper<char>;
+template class C2FieldSupportedValuesHelper<int32_t>;
+template class C2FieldSupportedValuesHelper<uint32_t>;
+//template class C2FieldSupportedValuesHelper<c2_cntr32_t>;
+template class C2FieldSupportedValuesHelper<int64_t>;
+template class C2FieldSupportedValuesHelper<uint64_t>;
+//template class C2FieldSupportedValuesHelper<c2_cntr64_t>;
+template class C2FieldSupportedValuesHelper<float>;
+
+/* ----------------------- C2ParamFieldValuesBuilder ----------------------- */
+
+template<typename T>
+struct C2ParamFieldValuesBuilder<T>::Impl {
+    Impl(const C2ParamField &field)
+        : _mParamField(field),
+          _mType(type_t::RANGE),
+          _mDefined(false),
+          _mRange(C2SupportedRange<T>::Any()),
+          _mValues(C2SupportedValueSet<T>::None()),
+          _mFlags(C2SupportedFlags<T>::None()) { }
+
+    /**
+     * Get C2ParamFieldValues from this builder.
+     */
+    operator C2ParamFieldValues() const {
+        if (!_mDefined) {
+            return C2ParamFieldValues(_mParamField);
+        }
+        switch (_mType) {
+        case type_t::EMPTY:
+        case type_t::VALUES:
+            return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mValues);
+        case type_t::RANGE:
+            return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mRange);
+        case type_t::FLAGS:
+            return C2ParamFieldValues(_mParamField, (C2FieldSupportedValues)_mFlags);
+        default:
+            // TRESPASS
+            // should never get here
+            return C2ParamFieldValues(_mParamField);
+        }
+    }
+
+    /** Define the supported values as the currently supported values of this builder. */
+    void any() {
+        _mDefined = true;
+    }
+
+    /** Restrict (and thus define) the supported values to none. */
+    void none() {
+        _mDefined = true;
+        _mType = type_t::VALUES;
+        _mValues.clear();
+    }
+
+    /** Restrict (and thus define) the supported values to |value| alone. */
+    void equalTo(T value) {
+         return limitTo(C2SupportedValueSet<T>::OneOf({value}));
+    }
+
+    /** Restrict (and thus define) the supported values to a value set. */
+    void limitTo(const C2SupportedValueSet<T> &limit) {
+        if (!_mDefined) {
+            C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+            // shortcut for first limit applied
+            _mDefined = true;
+            _mValues = limit;
+            _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+        } else {
+            switch (_mType) {
+            case type_t::EMPTY:
+            case type_t::VALUES:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mValues = _mValues.limitedTo(limit);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                break;
+            case type_t::RANGE:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mValues = limit.limitedTo(_mRange);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                break;
+            case type_t::FLAGS:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mValues = limit.limitedTo(_mFlags);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                break;
+            default:
+                C2_LOG(FATAL); // should not be here
+            }
+            // TODO: support flags
+        }
+        C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
+    }
+
+    /** Restrict (and thus define) the supported values to a flag set. */
+    void limitTo(const C2SupportedFlags<T> &limit) {
+        if (!_mDefined) {
+            C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+            // shortcut for first limit applied
+            _mDefined = true;
+            _mFlags = limit;
+            _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS;
+        } else {
+            switch (_mType) {
+            case type_t::EMPTY:
+            case type_t::VALUES:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mValues = _mValues.limitedTo(limit);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
+                break;
+            case type_t::FLAGS:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mFlags) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+                _mFlags = _mFlags.limitedTo(limit);
+                _mType = _mFlags.isEmpty() ? type_t::EMPTY : type_t::FLAGS;
+                C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mFlags);
+                break;
+            case type_t::RANGE:
+                C2_LOG(FATAL) << "limiting ranges to flags is not supported";
+                _mType = type_t::EMPTY;
+                break;
+            default:
+                C2_LOG(FATAL); // should not be here
+            }
+        }
+    }
+
+    void limitTo(const C2SupportedRange<T> &limit) {
+        if (!_mDefined) {
+            C2_LOG(VERBOSE) << "NA.limitTo(" << C2FieldSupportedValuesHelper<T>(limit) << ")";
+
+            // shortcut for first limit applied
+            _mDefined = true;
+            _mRange = limit;
+            _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE;
+            C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange);
+        } else {
+            switch (_mType) {
+            case type_t::EMPTY:
+            case type_t::VALUES:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mValues) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+                _mValues = _mValues.limitedTo(limit);
+                _mType = _mValues.isEmpty() ? type_t::EMPTY : type_t::VALUES;
+                C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mValues);
+                break;
+            case type_t::FLAGS:
+                C2_LOG(FATAL) << "limiting flags to ranges is not supported";
+                _mType = type_t::EMPTY;
+                break;
+            case type_t::RANGE:
+                C2_LOG(VERBOSE) << "(" << C2FieldSupportedValuesHelper<T>(_mRange) << ").limitTo("
+                        << C2FieldSupportedValuesHelper<T>(limit) << ")";
+                _mRange = _mRange.limitedTo(limit);
+                C2_DCHECK(_mValues.isEmpty());
+                _mType = _mRange.isEmpty() ? type_t::EMPTY : type_t::RANGE;
+                C2_LOG(VERBOSE) << " = " << _mType << ":" << C2FieldSupportedValuesHelper<T>(_mRange);
+                break;
+            default:
+                C2_LOG(FATAL); // should not be here
+            }
+        }
+    }
+
+private:
+    void instantiate() __unused {
+        (void)_mValues.values(); // instantiate non-const values()
+    }
+
+    void instantiate() const __unused {
+        (void)_mValues.values(); // instantiate const values()
+    }
+
+    typedef C2FieldSupportedValues::type_t type_t;
+
+    C2ParamField _mParamField;
+    type_t _mType;
+    bool _mDefined;
+    C2SupportedRange<T> _mRange;
+    C2SupportedValueSet<T> _mValues;
+    C2SupportedFlags<T> _mFlags;
+
+};
+
+template<typename T>
+C2ParamFieldValuesBuilder<T>::operator C2ParamFieldValues() const {
+    return (C2ParamFieldValues)(*_mImpl.get());
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamField &field)
+    : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(field)) { }
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::any() {
+    _mImpl->any();
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::none() {
+    _mImpl->none();
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::equalTo(T value) {
+    _mImpl->equalTo(value);
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedValueSet<T> &limit) {
+    _mImpl->limitTo(limit);
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedFlags<T> &limit) {
+    _mImpl->limitTo(limit);
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::limitTo(const C2SupportedRange<T> &limit) {
+    _mImpl->limitTo(limit);
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T>::C2ParamFieldValuesBuilder(const C2ParamFieldValuesBuilder<T> &other)
+    : _mImpl(std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get())) { }
+
+template<typename T>
+C2ParamFieldValuesBuilder<T> &C2ParamFieldValuesBuilder<T>::operator=(
+        const C2ParamFieldValuesBuilder<T> &other) {
+    _mImpl = std::make_unique<C2ParamFieldValuesBuilder<T>::Impl>(*other._mImpl.get());
+    return *this;
+}
+
+template<typename T>
+C2ParamFieldValuesBuilder<T>::~C2ParamFieldValuesBuilder() = default;
+
+template class C2ParamFieldValuesBuilder<uint8_t>;
+template class C2ParamFieldValuesBuilder<char>;
+template class C2ParamFieldValuesBuilder<int32_t>;
+template class C2ParamFieldValuesBuilder<uint32_t>;
+//template class C2ParamFieldValuesBuilder<c2_cntr32_t>;
+template class C2ParamFieldValuesBuilder<int64_t>;
+template class C2ParamFieldValuesBuilder<uint64_t>;
+//template class C2ParamFieldValuesBuilder<c2_cntr64_t>;
+template class C2ParamFieldValuesBuilder<float>;
+
+/* ------------------------- C2SettingResultBuilder ------------------------- */
+
+C2SettingConflictsBuilder::C2SettingConflictsBuilder() : _mConflicts() { }
+
+C2SettingConflictsBuilder::C2SettingConflictsBuilder(C2ParamFieldValues &&conflict) {
+    _mConflicts.emplace_back(std::move(conflict));
+}
+
+C2SettingConflictsBuilder& C2SettingConflictsBuilder::with(C2ParamFieldValues &&conflict) {
+    _mConflicts.emplace_back(std::move(conflict));
+    return *this;
+}
+
+std::vector<C2ParamFieldValues> C2SettingConflictsBuilder::retrieveConflicts() {
+    return std::move(_mConflicts);
+}
+
+/* ------------------------- C2SettingResult/sBuilder ------------------------- */
+
+C2SettingResult C2SettingResultBuilder::ReadOnly(const C2ParamField &param) {
+    return C2SettingResult { C2SettingResult::READ_ONLY, { param }, { } };
+}
+
+C2SettingResult C2SettingResultBuilder::BadValue(const C2ParamField &paramField, bool isInfo) {
+    return { isInfo ? C2SettingResult::INFO_BAD_VALUE : C2SettingResult::BAD_VALUE,
+             { paramField }, { } };
+}
+
+C2SettingResult C2SettingResultBuilder::Conflict(
+        C2ParamFieldValues &&paramFieldValues, 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 &param, 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 &param, std::shared_ptr<C2ParamReflector> reflector)
+        : _mParam(&param), _mReflector(reflector) { }
+
+private:
+    const C2Param *_mParam;
+    std::shared_ptr<C2ParamReflector> _mReflector;
+};
+
+}
+
+C2FieldUtils::List C2FieldUtils::enumerateFields(
+        const C2Param &param, 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 &param, 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 &param, 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());
+}
+
diff --git a/media/codec2/vndk/util/C2ParamUtils.cpp b/media/codec2/vndk/util/C2ParamUtils.cpp
new file mode 100644
index 0000000..80573d8
--- /dev/null
+++ b/media/codec2/vndk/util/C2ParamUtils.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define __C2_GENERATE_GLOBAL_VARS__ // to be able to implement the methods defined
+#include <C2Enum.h>
+#include <util/C2Debug-log.h>
+#include <util/C2ParamUtils.h>
+
+#include <utility>
+#include <vector>
+
+/** \file
+ * Utilities for parameter handling to be used by Codec2 implementations.
+ */
+
+/// \cond INTERNAL
+
+/* ---------------------------- UTILITIES FOR ENUMERATION REFLECTION ---------------------------- */
+
+static size_t countLeadingUnderscores(C2StringLiteral a) {
+    size_t i = 0;
+    while (a[i] == '_') {
+        ++i;
+    }
+    return i;
+}
+
+static size_t countMatching(C2StringLiteral a, const C2String &b) {
+    for (size_t i = 0; i < b.size(); ++i) {
+        if (!a[i] || a[i] != b[i]) {
+            return i;
+        }
+    }
+    return b.size();
+}
+
+// ABCDef => abc-def
+// ABCD2ef => abcd2-ef // 0
+// ABCD2Ef => ancd2-ef // -1
+// AbcDef => abc-def // -1
+// Abc2Def => abc-2def
+// Abc2def => abc-2-def
+// _Yo => _yo
+// _yo => _yo
+// C2_yo => c2-yo
+// C2__yo => c2-yo
+
+//static
+C2String _C2EnumUtils::camelCaseToDashed(C2String name) {
+    enum {
+        kNone = '.',
+        kLower = 'a',
+        kUpper = 'A',
+        kDigit = '1',
+        kDash = '-',
+        kUnderscore = '_',
+    } type = kNone;
+    size_t word_start = 0;
+    for (size_t ix = 0; ix < name.size(); ++ix) {
+        C2_LOG(VERBOSE) << name.substr(0, word_start) << "|"
+                << name.substr(word_start, ix - word_start) << "["
+                << name.substr(ix, 1) << "]" << name.substr(ix + 1)
+                << ": " << (char)type;
+        if (isupper(name[ix])) {
+            if (type == kLower) {
+                name.insert(ix++, 1, '-');
+                word_start = ix;
+            }
+            name[ix] = tolower(name[ix]);
+            type = kUpper;
+        } else if (islower(name[ix])) {
+            if (type == kDigit && ix > 0) {
+                name.insert(ix++, 1, '-');
+                word_start = ix;
+            } else if (type == kUpper && ix > word_start + 1) {
+                name.insert(ix++ - 1, 1, '-');
+                word_start = ix - 1;
+            }
+            type = kLower;
+        } else if (isdigit(name[ix])) {
+            if (type == kLower) {
+                name.insert(ix++, 1, '-');
+                word_start = ix;
+            }
+            type = kDigit;
+        } else if (name[ix] == '_') {
+            if (type == kDash) {
+                name.erase(ix--, 1);
+            } else if (type != kNone && type != kUnderscore) {
+                name[ix] = '-';
+                type = kDash;
+                word_start = ix + 1;
+            } else {
+                type = kUnderscore;
+                word_start = ix + 1;
+            }
+        } else {
+            name.resize(ix);
+        }
+    }
+    C2_LOG(VERBOSE) << "=> " << name;
+    return name;
+}
+
+//static
+std::vector<C2String> _C2EnumUtils::sanitizeEnumValueNames(
+        const std::vector<C2StringLiteral> names,
+        C2StringLiteral _prefix) {
+    std::vector<C2String> sanitizedNames;
+    C2String prefix;
+    size_t extraUnderscores = 0;
+    bool first = true;
+    if (_prefix) {
+        extraUnderscores = countLeadingUnderscores(_prefix);
+        prefix = _prefix + extraUnderscores;
+        first = false;
+        C2_LOG(VERBOSE) << "prefix:" << prefix << ", underscores:" << extraUnderscores;
+    }
+
+    // calculate prefix and minimum leading underscores
+    for (C2StringLiteral s : names) {
+        C2_LOG(VERBOSE) << s;
+        size_t underscores = countLeadingUnderscores(s);
+        if (first) {
+            extraUnderscores = underscores;
+            prefix = s + underscores;
+            first = false;
+        } else {
+            size_t matching = countMatching(
+                s + underscores,
+                prefix);
+            prefix.resize(matching);
+            extraUnderscores = std::min(underscores, extraUnderscores);
+        }
+        C2_LOG(VERBOSE) << "prefix:" << prefix << ", underscores:" << extraUnderscores;
+        if (prefix.size() == 0 && extraUnderscores == 0) {
+            break;
+        }
+    }
+
+    // we swallow the first underscore after upper case prefixes
+    bool upperCasePrefix = true;
+    for (size_t i = 0; i < prefix.size(); ++i) {
+        if (islower(prefix[i])) {
+            upperCasePrefix = false;
+            break;
+        }
+    }
+
+    for (C2StringLiteral s : names) {
+        size_t underscores = countLeadingUnderscores(s);
+        C2String sanitized = C2String(s, underscores - extraUnderscores);
+        sanitized.append(s + prefix.size() + underscores +
+                    (upperCasePrefix && s[prefix.size() + underscores] == '_'));
+        sanitizedNames.push_back(camelCaseToDashed(sanitized));
+    }
+
+    for (C2String s : sanitizedNames) {
+        C2_LOG(VERBOSE) << s;
+    }
+
+    return sanitizedNames;
+}
+
+//static
+std::vector<C2String> _C2EnumUtils::parseEnumValuesFromString(C2StringLiteral value) {
+    std::vector<C2String> foundNames;
+    size_t pos = 0, len = strlen(value);
+    do {
+        size_t endPos = strcspn(value + pos, " ,=") + pos;
+        if (endPos > pos) {
+            foundNames.emplace_back(value + pos, endPos - pos);
+        }
+        if (value[endPos] && value[endPos] != ',') {
+            endPos += strcspn(value + endPos, ",");
+        }
+        pos = strspn(value + endPos, " ,") + endPos;
+    } while (pos < len);
+    return foundNames;
+}
+
+/// safe(r) parsing from parameter blob
+//static
+C2Param *C2ParamUtils::ParseFirst(const uint8_t *blob, size_t size) {
+    // _mSize must fit into size, but really C2Param must also to be a valid param
+    if (size < sizeof(C2Param)) {
+        return nullptr;
+    }
+    // _mSize must match length
+    C2Param *param = (C2Param*)blob;
+    if (param->size() > size) {
+        return nullptr;
+    }
+    return param;
+}
+