/* This file is part of Ingen. Copyright 2007-2024 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 . */ #include "ingen/AtomForge.hpp" #include "lv2/atom/atom.h" #include "lv2/atom/forge.h" #include "lv2/atom/util.h" #include "lv2/urid/urid.h" #include "sord/sord.h" #include "sord/sordmm.hpp" #include #include #include namespace ingen { AtomForge::AtomForge(LV2_URID_Map& map) : LV2_Atom_Forge{} , _sratom{sratom_new(&map)} , _buf{static_cast(calloc(8, sizeof(LV2_Atom)))} { lv2_atom_forge_init(this, &map); lv2_atom_forge_set_sink(this, c_append, c_deref, this); } void AtomForge::read(Sord::World& world, SordModel* const model, const SordNode* const node) { sratom_read(_sratom.get(), this, world.c_obj(), model, node); } const LV2_Atom* AtomForge::atom() const { return _buf.get(); } void AtomForge::clear() { lv2_atom_forge_set_sink(this, c_append, c_deref, this); _size = 0; *_buf = {0U, 0U}; } Sratom& AtomForge::sratom() { return *_sratom; } intptr_t AtomForge::append(const void* const data, const uint32_t len) { // Record offset of the start of this write (+1 to avoid null) const auto ref = static_cast(_size + 1U); // Update size and reallocate if necessary if (lv2_atom_pad_size(_size + len) > _capacity) { const size_t new_size = lv2_atom_pad_size(_size + len); // Release buffer and try to realloc it auto* const old = _buf.release(); auto* const new_buf = static_cast(realloc(old, new_size)); if (!new_buf) { // Failure, reclaim old buffer and signal error to caller _buf = AtomPtr{old, FreeDeleter{}}; return 0; } // Adopt new buffer and update capacity to make room for the new data std::unique_ptr> ptr{new_buf}; _capacity = new_size; _buf = std::move(ptr); } // Append new data memcpy(reinterpret_cast(_buf.get()) + _size, data, len); _size += len; return ref; } LV2_Atom* AtomForge::deref(const intptr_t ref) { /* Make some assumptions and do unnecessary math to appease -Wcast-align. This is questionable at best, though the forge should only dereference references to aligned atoms. */ LV2_Atom* const ptr = _buf.get(); assert((ref - 1) % sizeof(LV2_Atom) == 0); return static_cast(ptr + (ref - 1) / sizeof(LV2_Atom)); // Alternatively: // return (LV2_Atom*)((uint8_t*)_buf.get() + ref - 1); } LV2_Atom_Forge_Ref AtomForge::c_append(void* const self, const void* const data, const uint32_t len) { return static_cast(self)->append(data, len); } LV2_Atom* AtomForge::c_deref(void* const self, const LV2_Atom_Forge_Ref ref) { return static_cast(self)->deref(ref); } } // namespace ingen