mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-19 01:17:06 +03:00
574 lines
17 KiB
C++
574 lines
17 KiB
C++
/*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2015-2023 Advanced Micro Devices, Inc. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <migraphx/cloneable.hpp>
|
|
#include <migraphx/errors.hpp>
|
|
#include <migraphx/stringutils.hpp>
|
|
#include <migraphx/value.hpp>
|
|
#include <migraphx/optional.hpp>
|
|
#include <migraphx/hash.hpp>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
namespace migraphx {
|
|
inline namespace MIGRAPHX_INLINE_NS {
|
|
|
|
struct value_base_impl : cloneable<value_base_impl>
|
|
{
|
|
virtual value::type_t get_type() { return value::null_type; }
|
|
#define MIGRAPHX_VALUE_GENERATE_BASE_FUNCTIONS(vt, cpp_type) \
|
|
virtual const cpp_type* if_##vt() const { return nullptr; }
|
|
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_BASE_FUNCTIONS)
|
|
virtual std::vector<value>* if_array() { return nullptr; }
|
|
virtual std::unordered_map<std::string, std::size_t>* if_object() { return nullptr; }
|
|
virtual value_base_impl* if_value() const { return nullptr; }
|
|
value_base_impl() = default;
|
|
value_base_impl(const value_base_impl&) = default;
|
|
value_base_impl& operator=(const value_base_impl&) = default;
|
|
virtual ~value_base_impl() override {}
|
|
};
|
|
|
|
#define MIGRAPHX_VALUE_GENERATE_BASE_TYPE(vt, cpp_type) \
|
|
struct vt##_value_holder : value_base_impl::share \
|
|
{ \
|
|
vt##_value_holder(cpp_type d) : data(std::move(d)) {} \
|
|
virtual value::type_t get_type() override { return value::vt##_type; } \
|
|
virtual const cpp_type* if_##vt() const override { return &data; } \
|
|
cpp_type data; \
|
|
};
|
|
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_BASE_TYPE)
|
|
|
|
struct array_value_holder : value_base_impl::derive<array_value_holder>
|
|
{
|
|
array_value_holder() {}
|
|
array_value_holder(std::vector<value> d) : data(std::move(d)) {}
|
|
virtual value::type_t get_type() override { return value::array_type; }
|
|
virtual std::vector<value>* if_array() override { return &data; }
|
|
std::vector<value> data;
|
|
};
|
|
|
|
struct object_value_holder : value_base_impl::derive<object_value_holder>
|
|
{
|
|
object_value_holder() {}
|
|
object_value_holder(std::vector<value> d, std::unordered_map<std::string, std::size_t> l)
|
|
: data(std::move(d)), lookup(std::move(l))
|
|
{
|
|
}
|
|
virtual value::type_t get_type() override { return value::object_type; }
|
|
virtual std::vector<value>* if_array() override { return &data; }
|
|
virtual std::unordered_map<std::string, std::size_t>* if_object() override { return &lookup; }
|
|
std::vector<value> data;
|
|
std::unordered_map<std::string, std::size_t> lookup;
|
|
};
|
|
|
|
value::value(const value& rhs) : x(rhs.x ? rhs.x->clone() : nullptr), key(rhs.key) {}
|
|
value& value::operator=(value rhs)
|
|
{
|
|
std::swap(rhs.x, x);
|
|
if(not rhs.key.empty())
|
|
std::swap(rhs.key, key);
|
|
return *this;
|
|
}
|
|
|
|
void set_vector(std::shared_ptr<value_base_impl>& x,
|
|
const std::vector<value>& v,
|
|
bool array_on_empty = true)
|
|
{
|
|
if(v.empty())
|
|
{
|
|
if(array_on_empty)
|
|
x = std::make_shared<array_value_holder>();
|
|
else
|
|
x = std::make_shared<object_value_holder>();
|
|
return;
|
|
}
|
|
if(v.front().get_key().empty())
|
|
{
|
|
x = std::make_shared<array_value_holder>(v);
|
|
}
|
|
else
|
|
{
|
|
std::unordered_map<std::string, std::size_t> lookup;
|
|
std::size_t i = 0;
|
|
for(auto&& e : v)
|
|
{
|
|
lookup[e.get_key()] = i;
|
|
i++;
|
|
}
|
|
x = std::make_shared<object_value_holder>(v, lookup);
|
|
}
|
|
}
|
|
|
|
value::value(const std::initializer_list<value>& i) : x(nullptr)
|
|
{
|
|
if(i.size() == 2 and i.begin()->is_string() and i.begin()->get_key().empty())
|
|
{
|
|
key = i.begin()->get_string();
|
|
auto r = (i.begin() + 1)->x;
|
|
x = r ? r->clone() : nullptr;
|
|
return;
|
|
}
|
|
set_vector(x, std::vector<value>(i.begin(), i.end()));
|
|
}
|
|
|
|
value::value(const std::vector<value>& v, bool array_on_empty) : x(nullptr)
|
|
{
|
|
set_vector(x, v, array_on_empty);
|
|
}
|
|
|
|
value::value(const std::unordered_map<std::string, value>& m)
|
|
: value(std::vector<value>(m.begin(), m.end()), false)
|
|
{
|
|
}
|
|
|
|
value::value(const std::string& pkey, const std::vector<value>& v, bool array_on_empty)
|
|
: x(nullptr), key(pkey)
|
|
{
|
|
set_vector(x, v, array_on_empty);
|
|
}
|
|
|
|
value::value(const std::string& pkey, const std::unordered_map<std::string, value>& m)
|
|
: value(pkey, std::vector<value>(m.begin(), m.end()), false)
|
|
{
|
|
}
|
|
|
|
value::value(const std::string& pkey, std::nullptr_t) : x(nullptr), key(pkey) {}
|
|
|
|
value::value(std::nullptr_t) : x(nullptr) {}
|
|
|
|
value::value(const std::string& pkey, const value& rhs)
|
|
: x(rhs.x ? rhs.x->clone() : nullptr), key(pkey)
|
|
{
|
|
}
|
|
|
|
value::value(const std::string& pkey, const char* i) : value(pkey, std::string(i)) {}
|
|
value::value(const char* i) : value(std::string(i)) {}
|
|
|
|
#define MIGRAPHX_VALUE_GENERATE_DEFINE_METHODS(vt, cpp_type) \
|
|
value::value(cpp_type i) : x(std::make_shared<vt##_value_holder>(std::move(i))) {} \
|
|
value::value(const std::string& pkey, cpp_type i) \
|
|
: x(std::make_shared<vt##_value_holder>(std::move(i))), key(pkey) \
|
|
{ \
|
|
} \
|
|
value& value::operator=(cpp_type rhs) \
|
|
{ \
|
|
x = std::make_shared<vt##_value_holder>(std::move(rhs)); \
|
|
return *this; \
|
|
} \
|
|
bool value::is_##vt() const { return x ? x->get_type() == vt##_type : false; } \
|
|
const cpp_type& value::get_##vt() const \
|
|
{ \
|
|
auto* r = this->if_##vt(); \
|
|
assert(r); \
|
|
return *r; \
|
|
} \
|
|
const cpp_type* value::if_##vt() const { return x ? x->if_##vt() : nullptr; }
|
|
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_DEFINE_METHODS)
|
|
|
|
value& value::operator=(const char* c)
|
|
{
|
|
*this = std::string{c};
|
|
return *this;
|
|
}
|
|
|
|
value& value::operator=(std::nullptr_t)
|
|
{
|
|
x = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
value& value::operator=(const std::initializer_list<value>& i)
|
|
{
|
|
value rhs = i;
|
|
std::swap(rhs.x, x);
|
|
return *this;
|
|
}
|
|
|
|
bool value::is_array() const { return x ? x->get_type() == array_type : false; }
|
|
const std::vector<value>& value::value::get_array() const
|
|
{
|
|
const auto* r = this->if_array();
|
|
assert(r);
|
|
return *r;
|
|
}
|
|
const std::vector<value>* value::if_array() const { return x ? x->if_array() : nullptr; }
|
|
|
|
bool value::is_object() const { return x ? x->get_type() == object_type : false; }
|
|
const std::vector<value>& value::get_object() const
|
|
{
|
|
const auto* r = this->if_object();
|
|
assert(r);
|
|
return *r;
|
|
}
|
|
const std::vector<value>* value::if_object() const
|
|
{
|
|
auto* r = x ? x->if_array() : nullptr;
|
|
assert(r == nullptr or
|
|
std::none_of(r->begin(), r->end(), [](auto&& v) { return v.get_key().empty(); }));
|
|
return r;
|
|
}
|
|
|
|
bool value::is_null() const { return x == nullptr; }
|
|
|
|
const std::string& value::get_key() const { return key; }
|
|
|
|
std::vector<value>* if_array_impl(const std::shared_ptr<value_base_impl>& x)
|
|
{
|
|
if(x == nullptr)
|
|
return nullptr;
|
|
return x->if_array();
|
|
}
|
|
|
|
std::vector<value>& get_array_impl(const std::shared_ptr<value_base_impl>& x)
|
|
{
|
|
auto* a = if_array_impl(x);
|
|
assert(a);
|
|
return *a;
|
|
}
|
|
|
|
std::vector<value>& get_array_throw(const std::shared_ptr<value_base_impl>& x)
|
|
{
|
|
auto* a = if_array_impl(x);
|
|
if(a == nullptr)
|
|
MIGRAPHX_THROW("Expected an array or object");
|
|
return *a;
|
|
}
|
|
|
|
template <class T>
|
|
T* find_impl(const std::shared_ptr<value_base_impl>& x, const std::string& key, T* end)
|
|
{
|
|
auto* a = if_array_impl(x);
|
|
if(a == nullptr)
|
|
return end;
|
|
auto* lookup = x->if_object();
|
|
if(lookup == nullptr)
|
|
return end;
|
|
auto it = lookup->find(key);
|
|
if(it == lookup->end())
|
|
return end;
|
|
return std::addressof((*a)[it->second]);
|
|
}
|
|
|
|
value* value::find(const std::string& pkey) { return find_impl(x, pkey, this->end()); }
|
|
|
|
const value* value::find(const std::string& pkey) const { return find_impl(x, pkey, this->end()); }
|
|
bool value::contains(const std::string& pkey) const
|
|
{
|
|
const auto* it = find(pkey);
|
|
if(it == nullptr)
|
|
return false;
|
|
if(it == end())
|
|
return false;
|
|
return true;
|
|
}
|
|
std::size_t value::size() const
|
|
{
|
|
const auto* a = if_array_impl(x);
|
|
if(a == nullptr)
|
|
return 0;
|
|
return a->size();
|
|
}
|
|
bool value::empty() const { return size() == 0; }
|
|
const value* value::data() const
|
|
{
|
|
auto* a = if_array_impl(x);
|
|
if(a == nullptr)
|
|
return nullptr;
|
|
return a->data();
|
|
}
|
|
value* value::data()
|
|
{
|
|
auto* a = if_array_impl(x);
|
|
if(a == nullptr)
|
|
return nullptr;
|
|
return a->data();
|
|
}
|
|
value* value::begin()
|
|
{
|
|
// cppcheck-suppress assertWithSideEffect
|
|
assert(data() or empty());
|
|
return data();
|
|
}
|
|
const value* value::begin() const
|
|
{
|
|
assert(data() or empty());
|
|
return data();
|
|
}
|
|
value* value::end() { return begin() + size(); }
|
|
const value* value::end() const { return begin() + size(); }
|
|
|
|
value& value::front()
|
|
{
|
|
assert(this->size() > 0);
|
|
return *begin();
|
|
}
|
|
const value& value::front() const
|
|
{
|
|
assert(this->size() > 0);
|
|
return *begin();
|
|
}
|
|
value& value::back()
|
|
{
|
|
assert(this->size() > 0);
|
|
return *std::prev(end());
|
|
}
|
|
const value& value::back() const
|
|
{
|
|
assert(this->size() > 0);
|
|
return *std::prev(end());
|
|
}
|
|
value& value::at(std::size_t i)
|
|
{
|
|
auto* a = if_array_impl(x);
|
|
if(a == nullptr)
|
|
MIGRAPHX_THROW("Not an array");
|
|
return a->at(i);
|
|
}
|
|
const value& value::at(std::size_t i) const
|
|
{
|
|
auto* a = if_array_impl(x);
|
|
if(a == nullptr)
|
|
MIGRAPHX_THROW("Not an array");
|
|
return a->at(i);
|
|
}
|
|
value& value::at(const std::string& pkey)
|
|
{
|
|
auto* r = find(pkey);
|
|
if(r == nullptr)
|
|
MIGRAPHX_THROW("Not an object");
|
|
if(r == end())
|
|
MIGRAPHX_THROW("Key not found: " + pkey);
|
|
return *r;
|
|
}
|
|
const value& value::at(const std::string& pkey) const
|
|
{
|
|
const auto* r = find(pkey);
|
|
if(r == nullptr)
|
|
MIGRAPHX_THROW("Not an object for field: " + pkey);
|
|
if(r == end())
|
|
MIGRAPHX_THROW("Key not found: " + pkey);
|
|
return *r;
|
|
}
|
|
value& value::operator[](std::size_t i)
|
|
{
|
|
assert(i < this->size());
|
|
return *(begin() + i);
|
|
}
|
|
const value& value::operator[](std::size_t i) const
|
|
{
|
|
assert(i < this->size());
|
|
return *(begin() + i);
|
|
}
|
|
value& value::operator[](const std::string& pkey) { return *emplace(pkey, nullptr).first; }
|
|
|
|
void value::clear() { get_array_throw(x).clear(); }
|
|
void value::resize(std::size_t n)
|
|
{
|
|
if(not is_array())
|
|
MIGRAPHX_THROW("Expected an array.");
|
|
get_array_impl(x).resize(n);
|
|
}
|
|
void value::resize(std::size_t n, const value& v)
|
|
{
|
|
if(not is_array())
|
|
MIGRAPHX_THROW("Expected an array.");
|
|
get_array_impl(x).resize(n, v);
|
|
}
|
|
|
|
std::pair<value*, bool> value::insert(const value& v)
|
|
{
|
|
if(v.key.empty())
|
|
{
|
|
if(not x)
|
|
x = std::make_shared<array_value_holder>();
|
|
get_array_impl(x).push_back(v);
|
|
assert(this->if_array());
|
|
return std::make_pair(&back(), true);
|
|
}
|
|
else
|
|
{
|
|
if(not x)
|
|
x = std::make_shared<object_value_holder>();
|
|
auto p = x->if_object()->emplace(v.key, get_array_impl(x).size());
|
|
if(p.second)
|
|
get_array_impl(x).push_back(v);
|
|
assert(this->if_object());
|
|
return std::make_pair(&get_array_impl(x)[p.first->second], p.second);
|
|
}
|
|
}
|
|
value* value::insert(const value* pos, const value& v)
|
|
{
|
|
assert(v.key.empty());
|
|
if(not x)
|
|
x = std::make_shared<array_value_holder>();
|
|
auto&& a = get_array_impl(x);
|
|
auto it = a.insert(a.begin() + (pos - begin()), v);
|
|
return std::addressof(*it);
|
|
}
|
|
|
|
value value::without_key() const
|
|
{
|
|
value result = *this;
|
|
result.key = "";
|
|
return result;
|
|
}
|
|
|
|
value value::with_key(const std::string& pkey) const
|
|
{
|
|
value result = *this;
|
|
result.key = pkey;
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
const T& compare_decay(const T& x)
|
|
{
|
|
return x;
|
|
}
|
|
int compare_decay(std::nullptr_t) { return 0; }
|
|
|
|
template <class F>
|
|
bool compare(const value& x, const value& y, F f)
|
|
{
|
|
bool result = false;
|
|
x.visit_value([&](auto&& a) {
|
|
y.visit_value([&](auto&& b) {
|
|
if constexpr(std::is_same<decltype(a), decltype(b)>{})
|
|
result = f(std::forward_as_tuple(x.get_key(), compare_decay(a)),
|
|
std::forward_as_tuple(y.get_key(), compare_decay(b)));
|
|
else
|
|
assert(false); // NOLINT
|
|
});
|
|
});
|
|
return result;
|
|
}
|
|
|
|
value::type_t value::get_type() const
|
|
{
|
|
if(not x)
|
|
return null_type;
|
|
return x->get_type();
|
|
}
|
|
|
|
bool operator==(const value& x, const value& y)
|
|
{
|
|
if(x.get_type() != y.get_type())
|
|
return false;
|
|
return compare(x, y, std::equal_to<>{});
|
|
}
|
|
bool operator!=(const value& x, const value& y) { return not(x == y); }
|
|
bool operator<(const value& x, const value& y)
|
|
{
|
|
if(x.get_type() != y.get_type())
|
|
return x.get_type() < y.get_type();
|
|
return compare(x, y, std::less<>{});
|
|
}
|
|
bool operator<=(const value& x, const value& y) { return not(x > y); }
|
|
bool operator>(const value& x, const value& y) { return y < x; }
|
|
bool operator>=(const value& x, const value& y) { return not(x < y); }
|
|
|
|
void print_value(std::ostream& os, std::nullptr_t) { os << "null"; }
|
|
|
|
template <class T>
|
|
void print_value(std::ostream& os, const T& x)
|
|
{
|
|
os << x;
|
|
}
|
|
|
|
template <class T, class U>
|
|
void print_value(std::ostream& os, const std::pair<T, U>& x)
|
|
{
|
|
os << x.first;
|
|
os << ": ";
|
|
print_value(os, x.second);
|
|
}
|
|
|
|
void print_value(std::ostream& os, const std::vector<value>& x)
|
|
{
|
|
os << "{";
|
|
os << to_string_range(x);
|
|
os << "}";
|
|
}
|
|
|
|
void print_value(std::ostream& os, const value::binary& x) { os << x; }
|
|
|
|
std::ostream& operator<<(std::ostream& os, const value& d)
|
|
{
|
|
d.visit([&](auto&& y) { print_value(os, y); });
|
|
return os;
|
|
}
|
|
|
|
template <class T>
|
|
std::size_t value_hash(const std::string& key, const T& x)
|
|
{
|
|
std::size_t h = hash_value(key);
|
|
hash_combine(h, x);
|
|
return h;
|
|
}
|
|
|
|
std::size_t value_hash(const std::string& key, std::nullptr_t) { return hash_value(key); }
|
|
|
|
std::size_t value_hash(const std::string& key, const std::vector<value>& x)
|
|
{
|
|
std::size_t h = hash_value(key);
|
|
for(const auto& v : x)
|
|
hash_combine(h, v);
|
|
return h;
|
|
}
|
|
std::size_t value_hash(const std::string& key, const value::binary& x)
|
|
{
|
|
std::size_t h = hash_value(key);
|
|
for(const auto& v : x)
|
|
hash_combine(h, v);
|
|
return h;
|
|
}
|
|
|
|
std::size_t value::hash() const
|
|
{
|
|
std::size_t h = 0;
|
|
this->visit_value([&](const auto& a) { h = value_hash(this->get_key(), a); });
|
|
return h;
|
|
}
|
|
|
|
void value::debug_print(bool show_type) const
|
|
{
|
|
if(show_type)
|
|
{
|
|
switch(get_type())
|
|
{
|
|
#define MIGRAPHX_VALUE_GENERATE_TYPE_STRING_CASE(vt, cpp_type) \
|
|
case vt##_type: std::cout << #vt << ": "; break;
|
|
MIGRAPHX_VISIT_VALUE_TYPES(MIGRAPHX_VALUE_GENERATE_TYPE_STRING_CASE)
|
|
MIGRAPHX_VALUE_GENERATE_TYPE_STRING_CASE(null, )
|
|
MIGRAPHX_VALUE_GENERATE_TYPE_STRING_CASE(array, )
|
|
MIGRAPHX_VALUE_GENERATE_TYPE_STRING_CASE(object, )
|
|
}
|
|
}
|
|
std::cout << *this << std::endl;
|
|
}
|
|
|
|
} // namespace MIGRAPHX_INLINE_NS
|
|
} // namespace migraphx
|