/*
This file is part of Ingen.
Copyright 2007-2017 David Robillard
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or any later version.
Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Affero General Public License for details.
You should have received a copy of the GNU Affero General Public License
along with Ingen. If not, see .
*/
#ifndef INGEN_ATOM_HPP
#define INGEN_ATOM_HPP
#include
#include
#include
#include
#include
#include
#include "ingen/ingen.h"
#include "lv2/atom/Atom.hpp"
#include "lv2/urid/urid.h"
namespace ingen {
/**
A generic typed data container.
An Atom holds a value with some type and size, both specified by a uint32_t.
Values with size less than sizeof(void*) are stored inline: no dynamic
allocation occurs so Atoms may be created in hard real-time threads.
Otherwise, if the size is larger than sizeof(void*), the value will be
dynamically allocated in a separate chunk of memory.
In either case, the data is stored in a binary compatible format to LV2_Atom.
*/
class INGEN_API Atom {
public:
using InlineBody = uint64_t;
Atom() : _value{InlineAtom{{0, 0}, 0}} {}
template
Atom(const lv2::atom::Primitive& primitive)
{
InlineAtom atom{{primitive.atom.size, primitive.atom.type}, 0};
memcpy(&atom.body, &primitive.body, primitive.atom.size);
_value = atom;
}
Atom(const uint32_t size, const LV2_URID type, const void* const body)
{
if (size <= sizeof(InlineBody)) {
InlineAtom atom{{size, type}, 0};
memcpy(&atom.body, body, size);
_value = atom;
} else {
LV2_Atom* atom = (LV2_Atom*)malloc(sizeof(LV2_Atom) + size);
atom->size = size;
atom->type = type;
memcpy(atom + 1, body, size);
_value = AtomPtr(atom, free);
}
}
Atom(Atom&&) = default;
Atom& operator=(Atom&&) = default;
Atom(const Atom& other) {
if (other.is_inline()) {
_value = boost::get(other._value);
} else {
_value = AtomPtr(copy_new_atom(other.atom()), free);
}
}
const Atom& operator=(const Atom& other) {
if (other.is_inline()) {
_value = boost::get(other._value);
} else {
_value = AtomPtr(copy_new_atom(other.atom()), free);
}
return *this;
}
inline bool operator==(const Atom& rhs) const {
return lv2_atom_equals(atom(), rhs.atom());
}
inline bool operator!=(const Atom& rhs) const {
return !lv2_atom_equals(atom(), rhs.atom());
}
inline uint32_t size() const { return atom()->size; }
inline LV2_URID type() const { return atom()->type; }
inline const void* body() const { return atom() + 1; }
inline void* body() { return atom() + 1; }
inline bool is_valid() const { return type(); }
template const T& get() const;
template const T* ptr() const;
const LV2_Atom* atom() const {
if (auto* atom = boost::get(&_value)) {
return &atom->atom;
}
return boost::get(_value).get();
}
LV2_Atom* atom() {
if (auto* atom = boost::get(&_value)) {
return &atom->atom;
}
return boost::get(_value).get();
}
private:
friend class Forge;
struct InlineAtom {
LV2_Atom atom;
InlineBody body;
};
using AtomPtr = std::unique_ptr;
bool is_inline() const { return _value.which() == 0; }
LV2_Atom* copy_new_atom(const LV2_Atom* atom) {
LV2_Atom* copy = (LV2_Atom*)malloc(sizeof(LV2_Atom) + atom->size);
memcpy(copy, atom, sizeof(LV2_Atom) + atom->size);
return copy;
}
boost::variant _value;
};
template<>
inline const int32_t& Atom::get() const
{
assert(size() == sizeof(int32_t));
return *static_cast(body());
}
template<>
inline const float& Atom::get() const
{
assert(size() == sizeof(float));
return *static_cast(body());
}
template<>
inline const LV2_URID& Atom::get() const
{
assert(size() == sizeof(LV2_URID));
return *static_cast(body());
}
template<>
inline const char* Atom::ptr() const
{
return static_cast(body());
}
} // namespace ingen
#endif // INGEN_ATOM_HPP