diff options
| author | David Robillard <d@drobilla.net> | 2017-12-18 01:36:54 +0100 | 
|---|---|---|
| committer | David Robillard <d@drobilla.net> | 2019-03-31 21:15:45 +0200 | 
| commit | 6b9eade6bc75f879cbbe8b21ea7765907e65a349 (patch) | |
| tree | 33e1cd4ad6eebe3a54d023da5b9134a421a9f9d8 /lv2/atom | |
| parent | c0a3fc67642385626e1b6093844272cee0a3ea77 (diff) | |
| download | lv2-6b9eade6bc75f879cbbe8b21ea7765907e65a349.tar.xz | |
Add C++ bindings for atoms and forgeatom-cpp
Diffstat (limited to 'lv2/atom')
| -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; +}  |