diff options
Diffstat (limited to 'lv2')
-rw-r--r-- | lv2/atom/Atom.hpp | 208 | ||||
-rw-r--r-- | lv2/atom/Forge.hpp | 547 | ||||
-rw-r--r-- | lv2/atom/atom-test.cpp | 351 |
3 files changed, 1106 insertions, 0 deletions
diff --git a/lv2/atom/Atom.hpp b/lv2/atom/Atom.hpp new file mode 100644 index 0000000..ab6d703 --- /dev/null +++ b/lv2/atom/Atom.hpp @@ -0,0 +1,208 @@ +/* + Copyright 2008-2017 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @defgroup atompp Atom C++ bindings. + + See <http://lv2plug.in/ns/ext/atom> for details. + + @{ +*/ + +#ifndef LV2_ATOM_HPP +#define LV2_ATOM_HPP + +#include <cstring> +#include <string> + +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" +#include "lv2/lv2plug.in/ns/ext/atom/util.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" + +namespace lv2 { +namespace atom { + +struct Atom : LV2_Atom {}; + +template<typename AtomType> +struct AtomWithBody : AtomType +{ + using Body = decltype(AtomType::body); + + AtomWithBody(AtomType atom) : AtomType(atom) {} + + operator const LV2_Atom&() const { return this->atom; } + explicit operator Body() const { return AtomType::body; } + + bool operator==(const Body& body) const { return AtomType::body == body; } + bool operator!=(const Body& body) const { return AtomType::body != body; } + + bool operator!() const { return !AtomType::body; } +}; + +template<typename AtomType> +struct Primitive : AtomWithBody<AtomType> +{ + Primitive(AtomType atom) : AtomWithBody<AtomType>(atom) {} + + bool operator<(const Primitive& rhs) const { + return AtomType::body < rhs.body; + } +}; + +using Int = Primitive<LV2_Atom_Int>; +using Long = Primitive<LV2_Atom_Long>; +using Float = Primitive<LV2_Atom_Float>; +using Double = Primitive<LV2_Atom_Double>; +using URID = Primitive<LV2_Atom_URID>; + +struct Bool : Primitive<LV2_Atom_Bool> +{ + Bool(LV2_Atom_Bool atom) : Primitive<LV2_Atom_Bool>(atom) {} + + operator bool() const { return LV2_Atom_Bool::body; } + bool operator!() const { return !LV2_Atom_Bool::body; } +}; + +struct String : LV2_Atom_String +{ + operator const LV2_Atom&() const { return this->atom; } + operator const Atom&() const { return (const Atom&)*this; } + + const char* c_str() const { return (const char*)(this + 1); } + char* c_str() { return (char*)(this + 1); } + + bool operator==(const char* s) const { return !strcmp(c_str(), s); } + bool operator!=(const char* s) const { return strcmp(c_str(), s); } + bool operator==(const std::string& s) const { return s == c_str(); } + bool operator!=(const std::string& s) const { return s != c_str(); } +}; + +struct Literal : AtomWithBody<LV2_Atom_Literal> +{ + const char* c_str() const { return (const char*)(this + 1); } + char* c_str() { return (char*)(this + 1); } +}; + +template<typename CType, typename CppType, CType* Next(const CType*)> +struct Iterator +{ + using value_type = CppType; + + explicit Iterator(const CType* p) + : ptr(static_cast<const value_type*>(p)) + {} + + Iterator& operator++() { + ptr = static_cast<value_type*>(Next(ptr)); + return *this; + } + + const value_type& operator*() const { return *ptr; } + const value_type* operator->() const { return ptr; } + + bool operator==(const Iterator& i) const { return ptr == i.ptr; } + bool operator!=(const Iterator& i) const { return ptr != i.ptr; } + +private: + const value_type* ptr; +}; + +struct Tuple : LV2_Atom_Tuple +{ + operator const LV2_Atom&() const { return this->atom; } + + using const_iterator = Iterator<LV2_Atom, Atom, lv2_atom_tuple_next>; + + const_iterator begin() const { + return const_iterator(lv2_atom_tuple_begin(this)); + } + + const_iterator end() const { + return const_iterator( + (const LV2_Atom*)( + (const char*)(this + 1) + lv2_atom_pad_size(atom.size))); + } +}; + +template<typename T> +struct Vector : AtomWithBody<LV2_Atom_Vector> +{ + operator const LV2_Atom&() const { return this->atom; } + + const T* data() const { return (const T*)(&body + 1); } + T* data() { return (T*)(&body + 1); } + + const T* begin() const { return data(); } + const T* end() const { return (const T*)(const char*)&body + atom.size; } + T* begin() { return data(); } + T* end() { return (T*)(char*)&body + atom.size; } +}; + +struct Property : AtomWithBody<LV2_Atom_Property> {}; + +struct Object : AtomWithBody<LV2_Atom_Object> +{ + using const_iterator = Iterator<LV2_Atom_Property_Body, + Property::Body, + lv2_atom_object_next>; + + const_iterator begin() const { + return const_iterator(lv2_atom_object_begin(&body)); + } + + const_iterator end() const { + return const_iterator( + (const LV2_Atom_Property_Body*)( + (const char*)&body + lv2_atom_pad_size(atom.size))); + } +}; + +struct Event : LV2_Atom_Event {}; + +struct Sequence : AtomWithBody<LV2_Atom_Sequence> +{ + using const_iterator = Iterator<LV2_Atom_Event, Event, lv2_atom_sequence_next>; + + const_iterator begin() const { + return const_iterator(lv2_atom_sequence_begin(&body)); + } + + const_iterator end() const { + return const_iterator(lv2_atom_sequence_end(&body, atom.size)); + } +}; + +inline bool +operator==(const LV2_Atom& lhs, const LV2_Atom& rhs) +{ + return lv2_atom_equals(&lhs, &rhs); +} + +inline bool +operator!=(const LV2_Atom& lhs, const LV2_Atom& rhs) +{ + return !lv2_atom_equals(&lhs, &rhs); +} + +} // namespace atom +} // namespace lv2 + +/** + @} +*/ + +#endif /* LV2_ATOM_HPP */ diff --git a/lv2/atom/Forge.hpp b/lv2/atom/Forge.hpp new file mode 100644 index 0000000..127d635 --- /dev/null +++ b/lv2/atom/Forge.hpp @@ -0,0 +1,547 @@ +/* + Copyright 2017 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file Forge.hpp A C++ wrapper for LV2_Atom_Forge. + + This file provides a wrapper for the atom forge that makes it simpler and + safer to write atoms in C++. It uses scoped handled for nested frames to + enforce correct atom structure in a safer and more readable way than the C + API. + + A "make" interface is also provided for making primitive atoms that are + value types without requiring an output buffer. +*/ + +/** + @defgroup forgepp C++ Forge + @ingroup atompp + @{ +*/ + +#ifndef LV2_ATOM_FORGE_HPP +#define LV2_ATOM_FORGE_HPP + +#include <string> + +#include "lv2/atom/Atom.hpp" +#include "lv2/atom/forge.h" + +namespace lv2 { +namespace atom { + +namespace detail { + +template<typename Sink> +static LV2_Atom_Forge_Ref +write(void* const sink, const void* const buf, const uint32_t size) +{ + return ((Sink*)sink)->write(buf, size); +} + +template<typename Sink> +static LV2_Atom* +deref(void* const sink, const LV2_Atom_Forge_Ref ref) +{ + return ((Sink*)sink)->deref(ref); +} + +}; + +/** A "forge" for creating atoms by appending to a buffer. */ +class Forge : public LV2_Atom_Forge +{ +public: + using Frame = LV2_Atom_Forge_Frame; + + /** Type safe wrapper for LV2_Atom_Forge_Ref. */ + template<typename T> + struct Ref + { + Ref(Forge& forge, const LV2_Atom_Forge_Ref ref) + : forge(forge) + , ref(ref) + {} + + const T& operator*() const { + return *(const T*)lv2_atom_forge_deref(&forge, ref); + } + + const T* operator->() const { + return (const T*)lv2_atom_forge_deref(&forge, ref); + } + + T& operator*() { return *(T*)lv2_atom_forge_deref(&forge, ref); } + T* operator->() { return (T*)lv2_atom_forge_deref(&forge, ref); } + + operator const T&() const { return **this; } + operator T&() { return **this; } + + Forge& forge; + LV2_Atom_Forge_Ref ref; + }; + + /** + @name Initialisation + @{ + */ + + /** Construct a forge with uninitialized output. + * + * Either set_buffer() or set_sink() must be called before writing. + */ + Forge(LV2_URID_Map* const map) + { + lv2_atom_forge_init(this, map); + } + + /** Construct a forge for writing to a buffer. */ + Forge(LV2_URID_Map* const map, uint8_t* const buf, const size_t size) + { + lv2_atom_forge_init(this, map); + set_buffer(buf, size); + } + + /** Construct a forge for writing to a sink function. */ + template<typename Handle> + Forge(LV2_URID_Map* const map, + LV2_Atom_Forge_Ref (*sink)(Handle, const void*, uint32_t), + LV2_Atom* (*deref)(Handle, LV2_Atom_Forge_Ref), + Handle handle) + { + lv2_atom_forge_init(this, map); + set_sink(sink, deref, handle); + } + + /** Construct a forge for writing to a sink object. + * + * The sink must have write(const void*, uint32_t) and + * deref(LV2_Atom_Forge_Ref) methods. + */ + template<typename Sink> + Forge(LV2_URID_Map* const map, Sink& sink) + { + lv2_atom_forge_init(this, map); + set_sink(sink); + } + + /** Set the output buffer to write to. */ + void set_buffer(uint8_t* const buf, const size_t size) + { + lv2_atom_forge_set_buffer(this, buf, size); + } + + /** Set a sink function to write to. */ + template<typename Handle> + void set_sink(LV2_Atom_Forge_Ref (*sink)(Handle, const void*, uint32_t), + LV2_Atom* (*deref)(Handle, LV2_Atom_Forge_Ref), + Handle handle) + { + lv2_atom_forge_set_sink(this, sink, deref, handle); + } + + /** Set a sink object to write to. */ + template<typename Sink> + void set_sink(Sink& sink) + { + lv2_atom_forge_set_sink( + this, detail::write<Sink>, detail::deref<Sink>, &sink); + } + + /** + @} + @name Primitive Value Construction + @{ + */ + + atom::Int make(const int32_t v) { return LV2_Atom_Int{sizeof(v), Int, v}; } + atom::Long make(const int64_t v) { return LV2_Atom_Long{sizeof(v), Long, v}; } + atom::Float make(const float v) { return LV2_Atom_Float{sizeof(v), Float, v}; } + atom::Double make(const double v) { return LV2_Atom_Double{sizeof(v), Double, v}; } + atom::Bool make(const bool v) { return LV2_Atom_Bool{sizeof(int32_t), Bool, v}; } + + /** + @} + @name Low Level Output + @{ + */ + + /** Write raw output. */ + LV2_Atom_Forge_Ref raw(const void* const data, const uint32_t size) { + return lv2_atom_forge_raw(this, data, size); + } + + /** Pad output accordingly so next write is 64-bit aligned. */ + void pad(const uint32_t written) { + lv2_atom_forge_pad(this, written); + } + + /** Write raw output, padding to 64-bits as necessary. */ + LV2_Atom_Forge_Ref write(const void* const data, const uint32_t size) + { + return lv2_atom_forge_write(this, data, size); + } + + /** Write a null-terminated string body. */ + LV2_Atom_Forge_Ref string_body(const char* const str, const uint32_t len) + { + return lv2_atom_forge_string_body(this, str, len); + } + + /** + @} + @name Atoms + @{ + */ + + /** Write an Atom header. */ + Ref<atom::Atom> atom(uint32_t size, uint32_t type) + { + return ref<atom::Atom>(lv2_atom_forge_atom(this, size, type)); + } + + /** Write an Atom. */ + Ref<atom::Atom> atom(uint32_t size, uint32_t type, const void* body) + { + Ref<atom::Atom> ref(atom(size, type)); + lv2_atom_forge_write(this, body, size); + return ref; + } + + /** Write a primitive (fixed-size) atom. */ + Ref<atom::Atom> primitive(const LV2_Atom* atom) + { + return ref<atom::Atom>(lv2_atom_forge_primitive(this, atom)); + } + + /** + @} + @name Primitives + @{ + */ + + /** Write an atom:Int. */ + Ref<atom::Int> write(const int32_t val) + { + return ref<atom::Int>(lv2_atom_forge_int(this, val)); + } + + /** Write an atom:Long. */ + Ref<atom::Long> write(const int64_t val) + { + return ref<atom::Long>(lv2_atom_forge_long(this, val)); + } + + /** Write an atom:Float. */ + Ref<atom::Float> write(const float val) + { + return ref<atom::Float>(lv2_atom_forge_float(this, val)); + } + + /** Write an atom:Double. */ + Ref<atom::Double> write(const double val) + { + return ref<atom::Double>(lv2_atom_forge_double(this, val)); + } + + /** Write an atom:Bool. */ + Ref<atom::Bool> write(const bool val) + { + return ref<atom::Bool>(lv2_atom_forge_bool(this, val)); + } + + /** Write an atom:URID. */ + Ref<atom::URID> urid(const LV2_URID id) + { + return ref<atom::URID>(lv2_atom_forge_urid(this, id)); + } + + /** + @} + @name Strings + @{ + */ + + /** Write an atom:String. Note that `str` need not be NULL terminated. */ + Ref<atom::String> string(const char* const str, const uint32_t len) + { + return ref<atom::String>(lv2_atom_forge_string(this, str, len)); + } + + /** Write an atom:String. */ + Ref<atom::String> string(const char* const str) + { + return string(str, strlen(str)); + } + + /** Write an atom:String. */ + Ref<atom::String> write(const std::string& str) + { + return ref<atom::String>( + lv2_atom_forge_string(this, str.c_str(), str.length())); + } + + /** Write a URI string. Note that `str` need not be NULL terminated. */ + Ref<atom::String> uri(const char* const uri, const uint32_t len) + { + return ref<atom::String>(lv2_atom_forge_uri(this, uri, len)); + } + + /** Write a URI string. */ + Ref<atom::String> uri(const char* const str) + { + return uri(str, strlen(str)); + } + + /** Write a URI string. */ + Ref<atom::String> uri(const std::string& str) + { + return ref<atom::String>( + lv2_atom_forge_uri(this, str.c_str(), str.length())); + } + + /** Write an atom:Path. Note that `str` need not be NULL terminated. */ + Ref<atom::String> path(const char* const str, const uint32_t len) + { + return ref<atom::String>(lv2_atom_forge_path(this, str, len)); + } + + /** Write an atom:Path. */ + Ref<atom::String> path(const char* const str) + { + return path(str, strlen(str)); + } + + /** Write an atom:Path. */ + Ref<atom::String> path(const std::string& path) + { + return ref<atom::String>( + lv2_atom_forge_path(this, path.c_str(), path.length())); + } + + /** Write an atom:Literal. Note that `str` need not be NULL terminated. */ + Ref<atom::Literal> literal(const char* const str, + const uint32_t len, + const uint32_t datatype, + const uint32_t lang) + { + return ref<atom::Literal>( + lv2_atom_forge_literal(this, str, len, datatype, lang)); + } + + /** Write an atom:Literal. */ + Ref<atom::Literal> literal(const char* const str, + const uint32_t datatype, + const uint32_t lang) + { + return literal(str, strlen(str), datatype, lang); + } + + /** Write an atom:Literal. */ + Ref<atom::Literal> literal(const std::string& str, + const uint32_t datatype, + const uint32_t lang) + { + return literal(str.c_str(), str.length(), datatype, lang); + } + + /** + @} + @name Collections + @{ + */ + + /** Base for all scoped collection handles. */ + template<typename T> + struct ScopedFrame + { + public: + ScopedFrame(Forge& forge) : forge(forge) {} + ~ScopedFrame() { lv2_atom_forge_pop(&forge, &frame); } + + ScopedFrame(const ScopedFrame&) = delete; + ScopedFrame& operator=(const ScopedFrame&) = delete; + + ScopedFrame(ScopedFrame&&) = default; + ScopedFrame& operator=(ScopedFrame&&) = default; + + T& operator*() { + return *(T*)lv2_atom_forge_deref(&forge, frame.ref); + } + + const T& operator*() const { + return *(const T*)lv2_atom_forge_deref(&forge, frame.ref); + } + + T* operator->() { + return (T*)lv2_atom_forge_deref(&forge, frame.ref); + } + + const T* operator->() const { + return (const T*)lv2_atom_forge_deref(&forge, frame.ref); + } + + Forge& forge; + Frame frame; + }; + + /** A scoped handle for writing a Vector. + + For example, to write the vector [1, 2, 3]: + + @code + { + ScopedVector vector(forge, sizeof(int32_t), forge.Int); + forge.write(1); + forge.write(2); + forge.write(3); + } // Vector finished + @endcode + */ + template<typename T> + struct ScopedVector : ScopedFrame<atom::Vector<T>> + { + ScopedVector(Forge& forge, const uint32_t child_type) + : ScopedFrame<atom::Vector<T>>(forge) + { + lv2_atom_forge_vector_head( + &forge, &this->frame, sizeof(T), child_type); + } + }; + + /** Write a complete atom:Vector. */ + template<typename T> + Ref<atom::Vector<T>> vector(const uint32_t elem_type, + const uint32_t n_elems, + const T* const elems) + { + return ref<atom::Vector<T>>( + lv2_atom_forge_vector(this, sizeof(T), elem_type, n_elems, elems)); + } + + /** A scoped handle for writing a Tuple. + + For example, to write the tuple (1, 2.0): + + @code + { + ScopedTuple tuple(forge); + forge.write(1); + forge.write(2.0f); + } // Tuple finished + @endcode + */ + struct ScopedTuple : ScopedFrame<atom::Tuple> + { + ScopedTuple(Forge& forge) : ScopedFrame<atom::Tuple>(forge) + { + lv2_atom_forge_tuple(&forge, &frame); + } + }; + + /** A scoped handle for writing an Object. + + The caller is responsible for correctly writing a sequence of + properties like (key, value, key, value, ...). For example, to write + the object [ a eg:Cat; eg:name "Hobbes" ]: + + @code + LV2_URID eg_Cat = map("http://example.org/Cat"); + LV2_URID eg_name = map("http://example.org/name"); + { + ScopedObject object(forge, 0, eg_Cat); + object.key(eg_name); + forge.string("Hobbes", strlen("Hobbes")); + } // Object finished + @endcode + */ + struct ScopedObject : ScopedFrame<atom::Object> + { + ScopedObject(Forge& forge, LV2_URID id, LV2_URID otype) + : ScopedFrame<atom::Object>(forge) + { + lv2_atom_forge_object(&forge, &frame, id, otype); + } + + /** Write a property key, to be followed by the value. */ + Ref<atom::Property::Body> key(const LV2_URID key, const LV2_URID ctx = 0) + { + return forge.ref<atom::Property::Body>( + lv2_atom_forge_property_head(&forge, key, ctx)); + } + }; + + /** A scoped handle for writing a Sequence. + + The caller is responsible for correctly writing a sequence of events + like (time, value, time, value, ...). For example: + + @code + { + ScopedSequence sequence(forge, 0, eg_Cat); + + sequence.frame_time(0); + forge.string("Bang", strlen("Bang")); + + sequence.frame_time(8); + { + ScopedTuple tuple(forge); + forge.write(1); + forge.write(2.0f); + } // Tuple event body finished + } // Sequence finished + @endcode + */ + struct ScopedSequence : ScopedFrame<atom::Sequence> + { + ScopedSequence(Forge& forge, const uint32_t unit) + : ScopedFrame<atom::Sequence>(forge) + { + lv2_atom_forge_sequence_head(&forge, &frame, unit); + } + + /** Write a time stamp in frames, to be followed by the event body. */ + Ref<int64_t> frame_time(const int64_t frames) + { + return forge.ref<int64_t>( + lv2_atom_forge_frame_time(&forge, frames)); + } + + /** Write a time stamp in beats, to be followed by the event body. */ + Ref<double> beat_time(const double beats) + { + return forge.ref<double>( + lv2_atom_forge_beat_time(&forge, beats)); + } + }; + + /** + @} + */ + +private: + template<typename T> + Ref<T> ref(const LV2_Atom_Forge_Ref ref) { return Ref<T>(*this, ref); } +}; + +} // namespace atom +} // namespace lv2 + +/** + @} +*/ + +#endif /* LV2_ATOM_FORGE_HPP */ diff --git a/lv2/atom/atom-test.cpp b/lv2/atom/atom-test.cpp new file mode 100644 index 0000000..518780e --- /dev/null +++ b/lv2/atom/atom-test.cpp @@ -0,0 +1,351 @@ +/* + Copyright 2012-2019 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "lv2/atom/Atom.hpp" +#include "lv2/atom/Forge.hpp" +#include "lv2/atom/atom-test-utils.c" +#include "lv2/atom/util.h" + +#include <cstdint> +#include <vector> + +using namespace lv2::atom; + +static int +test_primitives() +{ + LV2_URID_Map map{NULL, urid_map}; + Forge forge{&map}; + + if (forge.make(int32_t(2)) < forge.make(int32_t(1)) || + forge.make(int64_t(4)) < forge.make(int64_t(3)) || + forge.make(6.0f) < forge.make(4.0f) || + forge.make(8.0) < forge.make(7.0) || + forge.make(true) < forge.make(false)) { + return test_fail("Primitive comparison failed\n"); + } + + return 0; +} + +int +main(void) +{ + if (test_primitives()) { + return 1; + } + + LV2_URID_Map map = { NULL, urid_map }; + + LV2_URID eg_Object = urid_map(NULL, "http://example.org/Object"); + LV2_URID eg_one = urid_map(NULL, "http://example.org/one"); + LV2_URID eg_two = urid_map(NULL, "http://example.org/two"); + LV2_URID eg_three = urid_map(NULL, "http://example.org/three"); + LV2_URID eg_four = urid_map(NULL, "http://example.org/four"); + LV2_URID eg_true = urid_map(NULL, "http://example.org/true"); + LV2_URID eg_false = urid_map(NULL, "http://example.org/false"); + LV2_URID eg_path = urid_map(NULL, "http://example.org/path"); + LV2_URID eg_uri = urid_map(NULL, "http://example.org/uri"); + LV2_URID eg_urid = urid_map(NULL, "http://example.org/urid"); + LV2_URID eg_string = urid_map(NULL, "http://example.org/string"); + LV2_URID eg_literal = urid_map(NULL, "http://example.org/literal"); + LV2_URID eg_tuple = urid_map(NULL, "http://example.org/tuple"); + LV2_URID eg_vector = urid_map(NULL, "http://example.org/vector"); + LV2_URID eg_vector2 = urid_map(NULL, "http://example.org/vector2"); + LV2_URID eg_seq = urid_map(NULL, "http://example.org/seq"); + +#define BUF_SIZE 1024 +#define NUM_PROPS 15 + + uint8_t buf[BUF_SIZE]; + Forge forge(&map, buf, BUF_SIZE); + + Forge::ScopedObject obj(forge, 0, eg_Object); + + // eg_one = (Int)1 + obj.key(eg_one); + const Int& one = forge.write(1); + if (one != 1) { + return test_fail("%d != 1\n", one); + } + + // eg_two = (Long)2 + obj.key(eg_two); + const Long& two = forge.write(2L); + if (two != 2L) { + return test_fail("%ld != 2\n", two); + } + + // eg_three = (Float)3.0 + obj.key(eg_three); + const Float& three = forge.write(3.0f); + if (three != 3.0f) { + return test_fail("%f != 3.0f\n", three); + } + + // eg_four = (Double)4.0 + obj.key(eg_four); + const Double& four = forge.write(4.0); + if (four != 4) { + return test_fail("%ld != 4.0\n", four); + } + + // eg_true = (Bool)1 + obj.key(eg_true); + const Bool& t = forge.write(true); + if (!t) { + return test_fail("%ld != 1 (true)\n", t); + } + + // eg_false = (Bool)0 + obj.key(eg_false); + const Bool& f = forge.write(false); + if (f) { + return test_fail("%ld != 0 (false)\n", f); + } + + // eg_path = (Path)"/foo/bar" + obj.key(eg_path); + const String& path = forge.path("/foo/bar"); + if (path != "/foo/bar") { + return test_fail("%s != \"/foo/bar\"\n", path.c_str()); + } + + // eg_uri = (URI)"http://example.org/value" + obj.key(eg_uri); + const String& uri = forge.uri("http://lv2plug.in/"); + if (uri != "http://lv2plug.in/") { + return test_fail("%s != \"http://lv2plug.in/\"\n", uri.c_str()); + } + + // eg_urid = (URID)"http://example.org/value" + LV2_URID eg_value = urid_map(NULL, "http://example.org/value"); + obj.key(eg_urid); + LV2_Atom_URID* urid = &*forge.urid(eg_value); + if (urid->body != eg_value) { + return test_fail("%u != %u\n", urid->body, eg_value); + } + + // eg_string = (String)"hello" + obj.key(eg_string); + const String& string = forge.string("hello", strlen("hello")); + if (string != "hello") { + return test_fail("%s != \"hello\"\n", string.c_str()); + } + + // eg_literal = (Literal)"hello"@fr + obj.key(eg_literal); + const Literal& literal = forge.literal( + "bonjour", 0, urid_map(NULL, "http://lexvo.org/id/term/fr")); + if (strcmp(literal.c_str(), "bonjour")) { + return test_fail("%s != \"bonjour\"\n", literal.c_str()); + } + + // eg_tuple = "foo",true + obj.key(eg_tuple); + const Tuple* tuple = NULL; + const String* tup0 = NULL; + const Bool* tup1 = NULL; + { + Forge::ScopedTuple scoped_tuple(forge); + tup0 = &*forge.string("foo", strlen("foo")); + tup1 = &*forge.write(true); + tuple = &*scoped_tuple; + } + std::vector<const Atom*> elements; + for (const Atom& atom : *tuple) { + elements.push_back(&atom); + } + if (elements.size() != 2) { + return test_fail("Short tuple iteration\n"); + } else if (*elements[0] != *tup0) { + return test_fail("Incorrect first tuple element\n"); + } else if (*elements[1] != *tup1) { + return test_fail("Incorrect second tuple element\n"); + } + + // eg_vector = (Vector<Int>)1,2,3,4 + obj.key(eg_vector); + const int32_t elems[] = { 1, 2, 3, 4 }; + const Vector<int32_t>& vector = forge.vector(forge.Int, 4, elems); + if (memcmp(elems, vector.data(), sizeof(elems))) { + return test_fail("Corrupt vector\n"); + } + int32_t sum = 0; + for (const int32_t i : vector) { + sum += i; + } + if (sum != 1 + 2 + 3 + 4) { + return test_fail("Corrupt vector sum\n"); + } + + // eg_vector2 = (Vector<Int>)1,2,3,4 + obj.key(eg_vector2); + const Vector<int32_t>* vector2 = NULL; + { + Forge::ScopedVector<int32_t> scoped_vector(forge, forge.Int); + for (unsigned e = 0; e < sizeof(elems) / sizeof(int32_t); ++e) { + forge.write(elems[e]); + } + vector2 = &*scoped_vector; + } + if (vector != *vector2) { + return test_fail("Vector != Vector2\n"); + } + + // eg_seq = (Sequence)1, 2 + obj.key(eg_seq); + const Sequence* seq = NULL; + { + Forge::ScopedSequence scoped_sequence(forge, 0); + scoped_sequence.frame_time(0); + forge.write(1); + scoped_sequence.frame_time(1); + forge.write(2); + seq = &*scoped_sequence; + } + + // Test equality + if (one == two) { + return test_fail("1 == 2.0\n"); + } else if (one != one) { + return test_fail("1 != 1\n"); + } + + unsigned n_events = 0; + for (const Event& ev : *seq) { + if (ev.time.frames != n_events) { + return test_fail("Corrupt event %u has bad time\n", n_events); + } else if (ev.body.type != forge.Int) { + return test_fail("Corrupt event %u has bad type\n", n_events); + } else if (((LV2_Atom_Int*)&ev.body)->body != (int)n_events + 1) { + return test_fail("Event %u != %d\n", n_events, n_events + 1); + } + ++n_events; + } + + unsigned n_props = 0; + for (const Property::Body& prop : *obj) { + if (!prop.key) { + return test_fail("Corrupt property %u has no key\n", n_props); + } else if (prop.context) { + return test_fail("Corrupt property %u has context\n", n_props); + } + ++n_props; + } + + if (n_props != NUM_PROPS) { + return test_fail("Corrupt object has %u properties != %u\n", + n_props, NUM_PROPS); + } + + struct { + const LV2_Atom* one; + const LV2_Atom* two; + const LV2_Atom* three; + const LV2_Atom* four; + const LV2_Atom* affirmative; + const LV2_Atom* negative; + const LV2_Atom* path; + const LV2_Atom* uri; + const LV2_Atom* urid; + const LV2_Atom* string; + const LV2_Atom* literal; + const LV2_Atom* tuple; + const LV2_Atom* vector; + const LV2_Atom* vector2; + const LV2_Atom* seq; + } matches; + + memset(&matches, 0, sizeof(matches)); + + LV2_Atom_Object_Query q[] = { + { eg_one, &matches.one }, + { eg_two, &matches.two }, + { eg_three, &matches.three }, + { eg_four, &matches.four }, + { eg_true, &matches.affirmative }, + { eg_false, &matches.negative }, + { eg_path, &matches.path }, + { eg_uri, &matches.uri }, + { eg_urid, &matches.urid }, + { eg_string, &matches.string }, + { eg_literal, &matches.literal }, + { eg_tuple, &matches.tuple }, + { eg_vector, &matches.vector }, + { eg_vector2, &matches.vector2 }, + { eg_seq, &matches.seq }, + LV2_ATOM_OBJECT_QUERY_END + }; + + unsigned n_matches = lv2_atom_object_query(&*obj, q); + for (int n = 0; n < 2; ++n) { + if (n_matches != n_props) { + return test_fail("Query failed, %u matches != %u\n", + n_matches, n_props); + } else if (one != *matches.one) { + return test_fail("Bad match one\n"); + } else if (two != *matches.two) { + return test_fail("Bad match two\n"); + } else if (three != *matches.three) { + return test_fail("Bad match three\n"); + } else if (four != *matches.four) { + return test_fail("Bad match four\n"); + } else if (t != *matches.affirmative) { + return test_fail("Bad match true\n"); + } else if (f != *matches.negative) { + return test_fail("Bad match false\n"); + } else if (path != *matches.path) { + return test_fail("Bad match path\n"); + } else if (uri != *matches.uri) { + return test_fail("Bad match URI\n"); + } else if (string != *matches.string) { + return test_fail("Bad match string\n"); + } else if (literal != *matches.literal) { + return test_fail("Bad match literal\n"); + } else if (*tuple != *matches.tuple) { + return test_fail("Bad match tuple\n"); + } else if (vector != *matches.vector) { + return test_fail("Bad match vector\n"); + } else if (vector != *matches.vector2) { + return test_fail("Bad match vector2\n"); + } else if (*seq != *matches.seq) { + return test_fail("Bad match sequence\n"); + } + memset(&matches, 0, sizeof(matches)); + n_matches = lv2_atom_object_get((LV2_Atom_Object*)&*obj, + eg_one, &matches.one, + eg_two, &matches.two, + eg_three, &matches.three, + eg_four, &matches.four, + eg_true, &matches.affirmative, + eg_false, &matches.negative, + eg_path, &matches.path, + eg_uri, &matches.uri, + eg_urid, &matches.urid, + eg_string, &matches.string, + eg_literal, &matches.literal, + eg_tuple, &matches.tuple, + eg_vector, &matches.vector, + eg_vector2, &matches.vector2, + eg_seq, &matches.seq, + 0); + } + + free_urid_map(); + + return 0; +} |