/* ============================================================================== This file is part of the JUCE 7 technical preview. Copyright (c) 2022 - Raw Material Software Limited You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For the technical preview this file cannot be licensed commercially. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #if ! DOXYGEN namespace juce { template class FlagCache { using FlagType = uint32_t; public: FlagCache() = default; explicit FlagCache (size_t items) : flags (divCeil (items, groupsPerWord)) { std::fill (flags.begin(), flags.end(), 0); } void set (size_t index, FlagType bits) { const auto flagIndex = index / groupsPerWord; jassert (flagIndex < flags.size()); const auto groupIndex = index - (flagIndex * groupsPerWord); flags[flagIndex].fetch_or (moveToGroupPosition (bits, groupIndex), std::memory_order_acq_rel); } /* Calls the supplied callback for any entries with non-zero flags, and sets all flags to zero. */ template void ifSet (Callback&& callback) { for (size_t flagIndex = 0; flagIndex < flags.size(); ++flagIndex) { const auto prevFlags = flags[flagIndex].exchange (0, std::memory_order_acq_rel); for (size_t group = 0; group < groupsPerWord; ++group) { const auto masked = moveFromGroupPosition (prevFlags, group); if (masked != 0) callback ((flagIndex * groupsPerWord) + group, masked); } } } void clear() { std::fill (flags.begin(), flags.end(), 0); } private: /* Given the flags for a single item, and a group index, shifts the flags so that they are positioned at the appropriate location for that group index. e.g. If the flag type is a uint32_t, and there are 2 flags per item, then each uint32_t will hold flags for 16 items. The flags for item 0 are the least significant two bits; the flags for item 15 are the most significant two bits. */ static constexpr FlagType moveToGroupPosition (FlagType ungrouped, size_t groupIndex) { return (ungrouped & groupMask) << (groupIndex * bitsPerFlagGroup); } /* Given a set of grouped flags for multiple items, and a group index, extracts the flags set for an item at that group index. e.g. If the flag type is a uint32_t, and there are 2 flags per item, then each uint32_t will hold flags for 16 items. Asking for groupIndex 0 will return the least significant two bits; asking for groupIndex 15 will return the most significant two bits. */ static constexpr FlagType moveFromGroupPosition (FlagType grouped, size_t groupIndex) { return (grouped >> (groupIndex * bitsPerFlagGroup)) & groupMask; } static constexpr size_t findNextPowerOfTwoImpl (size_t n, size_t shift) { return shift == 32 ? n : findNextPowerOfTwoImpl (n | (n >> shift), shift * 2); } static constexpr size_t findNextPowerOfTwo (size_t value) { return findNextPowerOfTwoImpl (value - 1, 1) + 1; } static constexpr size_t divCeil (size_t a, size_t b) { return (a / b) + ((a % b) != 0); } static constexpr size_t bitsPerFlagGroup = findNextPowerOfTwo (requiredFlagBitsPerItem); static constexpr size_t groupsPerWord = (8 * sizeof (FlagType)) / bitsPerFlagGroup; static constexpr FlagType groupMask = ((FlagType) 1 << requiredFlagBitsPerItem) - 1; std::vector> flags; }; template class FlaggedFloatCache { public: FlaggedFloatCache() = default; explicit FlaggedFloatCache (size_t sizeIn) : values (sizeIn), flags (sizeIn) { std::fill (values.begin(), values.end(), 0.0f); } size_t size() const noexcept { return values.size(); } void setValue (size_t index, float value) { jassert (index < size()); values[index].store (value, std::memory_order_relaxed); } void setBits (size_t index, uint32_t bits) { flags.set (index, bits); } void setValueAndBits (size_t index, float value, uint32_t bits) { setValue (index, value); setBits (index, bits); } float get (size_t index) const noexcept { jassert (index < size()); return values[index].load (std::memory_order_relaxed); } /* Calls the supplied callback for any entries which have been modified since the last call to this function. */ template void ifSet (Callback&& callback) { flags.ifSet ([this, &callback] (size_t groupIndex, uint32_t bits) { callback (groupIndex, values[groupIndex].load (std::memory_order_relaxed), bits); }); } private: std::vector> values; FlagCache flags; }; } // namespace juce #endif