diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp
index 53bf9eac3..5656d33e3 100644
--- a/libraries/libvapours/include/vapours/util.hpp
+++ b/libraries/libvapours/include/vapours/util.hpp
@@ -21,6 +21,7 @@
#include "util/util_size.hpp"
#include "util/util_fourcc.hpp"
#include "util/util_bitpack.hpp"
+#include "util/util_bitset.hpp"
#include "util/util_scope_guard.hpp"
#include "util/util_typed_storage.hpp"
#include "util/util_intrusive_list.hpp"
diff --git a/libraries/libvapours/include/vapours/util/util_bitset.hpp b/libraries/libvapours/include/vapours/util/util_bitset.hpp
new file mode 100644
index 000000000..e3f9ae732
--- /dev/null
+++ b/libraries/libvapours/include/vapours/util/util_bitset.hpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include "../defines.hpp"
+
+namespace ams::util {
+
+ namespace impl {
+
+ template
+ class BitSet {
+ private:
+ static_assert(std::is_integral::value);
+ static_assert(std::is_unsigned::value);
+ static_assert(sizeof(Storage) <= sizeof(u64));
+
+ static constexpr size_t FlagsPerWord = BITSIZEOF(Storage);
+ static constexpr size_t NumWords = util::AlignUp(N, FlagsPerWord) / FlagsPerWord;
+
+ static constexpr ALWAYS_INLINE auto CountLeadingZeroImpl(Storage word) {
+ return __builtin_clzll(static_cast(word)) - (BITSIZEOF(unsigned long long) - FlagsPerWord);
+ }
+
+ static constexpr ALWAYS_INLINE Storage GetBitMask(size_t bit) {
+ return Storage(1) << (FlagsPerWord - 1 - bit);
+ }
+ private:
+ Storage words[NumWords];
+ public:
+ constexpr ALWAYS_INLINE BitSet() : words() { /* ... */ }
+
+ constexpr ALWAYS_INLINE void SetBit(size_t i) {
+ this->words[i / FlagsPerWord] |= GetBitMask(i % FlagsPerWord);
+ }
+
+ constexpr ALWAYS_INLINE void ClearBit(size_t i) {
+ this->words[i / FlagsPerWord] &= ~GetBitMask(i % FlagsPerWord);
+ }
+
+ constexpr ALWAYS_INLINE size_t CountLeadingZero() const {
+ for (size_t i = 0; i < NumWords; i++) {
+ if (this->words[i]) {
+ return FlagsPerWord * i + CountLeadingZeroImpl(this->words[i]);
+ }
+ }
+ return FlagsPerWord * NumWords;
+ }
+
+ constexpr ALWAYS_INLINE size_t GetNextSet(size_t n) const {
+ for (size_t i = (n + 1) / FlagsPerWord; i < NumWords; i++) {
+ Storage word = this->words[i];
+ if (!util::IsAligned(n + 1, FlagsPerWord)) {
+ word &= GetBitMask(n % FlagsPerWord) - 1;
+ }
+ if (word) {
+ return FlagsPerWord * i + CountLeadingZeroImpl(word);
+ }
+ }
+ return FlagsPerWord * NumWords;
+ }
+ };
+
+ }
+
+ template
+ using BitSet8 = impl::BitSet;
+
+ template
+ using BitSet16 = impl::BitSet;
+
+ template
+ using BitSet32 = impl::BitSet;
+
+ template
+ using BitSet64 = impl::BitSet;
+
+}