/* * Copyright (c) 2021, Ali Mohammad Pur * Copyright (c) 2022, kleines Filmröllchen * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace AK { template struct DisjointIterator { struct EndTag { }; using ReferenceType = Conditional>, Vector>&; DisjointIterator(ReferenceType chunks) : m_chunks(chunks) { while (m_chunk_index < m_chunks.size() && m_chunks[m_chunk_index].is_empty()) ++m_chunk_index; } DisjointIterator(ReferenceType chunks, EndTag) : m_chunk_index(chunks.size()) , m_index_in_chunk(0) , m_chunks(chunks) { } DisjointIterator& operator++() { if (m_chunk_index >= m_chunks.size()) return *this; auto& chunk = m_chunks[m_chunk_index]; if (m_index_in_chunk + 1 >= chunk.size()) { ++m_chunk_index; m_index_in_chunk = 0; } else { ++m_index_in_chunk; } if (m_chunk_index < m_chunks.size()) { while (m_chunks[m_chunk_index].is_empty()) ++m_chunk_index; } return *this; } bool operator==(DisjointIterator const& other) const { return &other.m_chunks == &m_chunks && other.m_index_in_chunk == m_index_in_chunk && other.m_chunk_index == m_chunk_index; } auto& operator*() requires(!IsConst) { return m_chunks[m_chunk_index][m_index_in_chunk]; } auto* operator->() requires(!IsConst) { return &m_chunks[m_chunk_index][m_index_in_chunk]; } auto const& operator*() const { return m_chunks[m_chunk_index][m_index_in_chunk]; } auto const* operator->() const { return &m_chunks[m_chunk_index][m_index_in_chunk]; } private: size_t m_chunk_index { 0 }; size_t m_index_in_chunk { 0 }; ReferenceType m_chunks; }; template>> class DisjointSpans { public: DisjointSpans() = default; ~DisjointSpans() = default; DisjointSpans(DisjointSpans const&) = default; DisjointSpans(DisjointSpans&&) = default; explicit DisjointSpans(SpanContainer spans) : m_spans(move(spans)) { } DisjointSpans& operator=(DisjointSpans&&) = default; DisjointSpans& operator=(DisjointSpans const&) = default; Span singular_span() const { VERIFY(m_spans.size() == 1); return m_spans[0]; } SpanContainer const& individual_spans() const { return m_spans; } bool operator==(DisjointSpans const& other) const { if (other.size() != size()) return false; auto it = begin(); auto other_it = other.begin(); for (; it != end(); ++it, ++other_it) { if (*it != *other_it) return false; } return true; } T& operator[](size_t index) { return at(index); } T const& operator[](size_t index) const { return at(index); } T const& at(size_t index) const { return const_cast(*this).at(index); } T& at(size_t index) { auto value = find(index); VERIFY(value != nullptr); return *value; } T* find(size_t index) { auto span_and_offset = span_around(index); if (span_and_offset.offset >= span_and_offset.span.size()) return nullptr; return &span_and_offset.span.at(span_and_offset.offset); } T const* find(size_t index) const { return const_cast(this)->find(index); } size_t size() const { size_t size = 0; for (auto& span : m_spans) size += span.size(); return size; } bool is_empty() const { return all_of(m_spans, [](auto& span) { return span.is_empty(); }); } DisjointSpans slice(size_t start, size_t length) const { DisjointSpans spans; for (auto& span : m_spans) { if (length == 0) break; if (start >= span.size()) { start -= span.size(); continue; } auto sliced_length = min(length, span.size() - start); spans.m_spans.append(span.slice(start, sliced_length)); start = 0; length -= sliced_length; } // Make sure that we weren't asked to make a slice larger than possible. VERIFY(length == 0); return spans; } DisjointSpans slice(size_t start) const { return slice(start, size() - start); } DisjointSpans slice_from_end(size_t length) const { return slice(size() - length, length); } DisjointIterator, false> begin() { return { m_spans }; } DisjointIterator, false> end() { return { m_spans, {} }; } DisjointIterator, true> begin() const { return { m_spans }; } DisjointIterator, true> end() const { return { m_spans, {} }; } private: struct SpanAndOffset { Span& span; size_t offset; }; SpanAndOffset span_around(size_t index) { size_t offset = 0; for (auto& span : m_spans) { if (span.is_empty()) continue; auto next_offset = span.size() + offset; if (next_offset <= index) { offset = next_offset; continue; } return { span, index - offset }; } return { m_spans.last(), index - (offset - m_spans.last().size()) }; } SpanContainer m_spans; }; namespace Detail { template ChunkType shatter_chunk(ChunkType& source_chunk, size_t start, size_t sliced_length) { auto wanted_slice = source_chunk.span().slice(start, sliced_length); ChunkType new_chunk; if constexpr (IsTriviallyConstructible) { new_chunk.resize(wanted_slice.size()); TypedTransfer::move(new_chunk.data(), wanted_slice.data(), wanted_slice.size()); } else { new_chunk.ensure_capacity(wanted_slice.size()); for (auto& entry : wanted_slice) new_chunk.unchecked_append(move(entry)); } source_chunk.remove(start, sliced_length); return new_chunk; } template FixedArray shatter_chunk(FixedArray& source_chunk, size_t start, size_t sliced_length) { auto wanted_slice = source_chunk.span().slice(start, sliced_length); FixedArray new_chunk = FixedArray::must_create_but_fixme_should_propagate_errors(wanted_slice.size()); if constexpr (IsTriviallyConstructible) { TypedTransfer::move(new_chunk.data(), wanted_slice.data(), wanted_slice.size()); } else { auto copied_chunk = FixedArray::create(wanted_slice).release_value_but_fixme_should_propagate_errors(); new_chunk.swap(copied_chunk); } auto rest_of_chunk = FixedArray::create(source_chunk.span().slice(start)).release_value_but_fixme_should_propagate_errors(); source_chunk.swap(rest_of_chunk); return new_chunk; } } template> class DisjointChunks { private: constexpr static auto InlineCapacity = IsCopyConstructible ? 1 : 0; public: DisjointChunks() = default; ~DisjointChunks() = default; DisjointChunks(DisjointChunks const&) = default; DisjointChunks(DisjointChunks&&) = default; DisjointChunks& operator=(DisjointChunks&&) = default; DisjointChunks& operator=(DisjointChunks const&) = default; void append(ChunkType&& chunk) { m_chunks.append(move(chunk)); } void extend(DisjointChunks&& chunks) { m_chunks.extend(move(chunks.m_chunks)); } void extend(DisjointChunks const& chunks) { m_chunks.extend(chunks.m_chunks); } ChunkType& first_chunk() { return m_chunks.first(); } ChunkType& last_chunk() { return m_chunks.last(); } ChunkType const& first_chunk() const { return m_chunks.first(); } ChunkType const& last_chunk() const { return m_chunks.last(); } void ensure_capacity(size_t needed_capacity) { m_chunks.ensure_capacity(needed_capacity); } void insert(size_t index, T value) { if (m_chunks.size() == 1) return m_chunks.first().insert(index, value); auto chunk_and_offset = chunk_around(index); if (!chunk_and_offset.chunk) { m_chunks.empend(); chunk_and_offset.chunk = &m_chunks.last(); } chunk_and_offset.chunk->insert(chunk_and_offset.offset, move(value)); } void clear() { m_chunks.clear(); } T& operator[](size_t index) { return at(index); } T const& operator[](size_t index) const { return at(index); } T const& at(size_t index) const { return const_cast(*this).at(index); } T& at(size_t index) { auto value = find(index); VERIFY(value != nullptr); return *value; } T* find(size_t index) { if (m_chunks.size() == 1) { if (m_chunks.first().size() > index) return &m_chunks.first().at(index); return nullptr; } auto chunk_and_offset = chunk_around(index); if (!chunk_and_offset.chunk || chunk_and_offset.offset >= chunk_and_offset.chunk->size()) return nullptr; return &chunk_and_offset.chunk->at(chunk_and_offset.offset); } T const* find(size_t index) const { return const_cast(this)->find(index); } size_t size() const { size_t sum = 0; for (auto& chunk : m_chunks) sum += chunk.size(); return sum; } bool is_empty() const { return all_of(m_chunks, [](auto& chunk) { return chunk.is_empty(); }); } template DisjointSpans, InlineSize>> spans() const& { Vector, InlineSize> spans; spans.ensure_capacity(m_chunks.size()); if (m_chunks.size() == 1) { spans.append(const_cast(m_chunks[0]).span()); return DisjointSpans, InlineSize>> { move(spans) }; } for (auto& chunk : m_chunks) spans.unchecked_append(const_cast(chunk).span()); return DisjointSpans, InlineSize>> { move(spans) }; } bool operator==(DisjointChunks const& other) const { if (other.size() != size()) return false; auto it = begin(); auto other_it = other.begin(); for (; it != end(); ++it, ++other_it) { if (*it != *other_it) return false; } return true; } DisjointChunks release_slice(size_t start, size_t length) & { return move(*this).slice(start, length); } DisjointChunks release_slice(size_t start) & { return move(*this).slice(start); } DisjointChunks slice(size_t start, size_t length) && { DisjointChunks result; for (auto& chunk : m_chunks) { if (length == 0) break; if (start >= chunk.size()) { start -= chunk.size(); continue; } auto sliced_length = min(length, chunk.size() - start); if (start == 0 && sliced_length == chunk.size()) { // Happy path! move the chunk itself. result.m_chunks.append(move(chunk)); } else { // Shatter the chunk, we were asked for only a part of it :( auto new_chunk = Detail::shatter_chunk(chunk, start, sliced_length); result.m_chunks.append(move(new_chunk)); } start = 0; length -= sliced_length; } m_chunks.remove_all_matching([](auto& chunk) { return chunk.is_empty(); }); // Make sure that we weren't asked to make a slice larger than possible. VERIFY(length == 0); return result; } DisjointChunks slice(size_t start) && { return move(*this).slice(start, size() - start); } DisjointChunks slice_from_end(size_t length) && { return move(*this).slice(size() - length, length); } void flatten() { if (m_chunks.is_empty()) return; auto size = this->size(); auto& first_chunk = m_chunks.first(); first_chunk.ensure_capacity(size); bool first = true; for (auto& chunk : m_chunks) { if (first) { first = false; continue; } first_chunk.extend(move(chunk)); } m_chunks.remove(1, m_chunks.size() - 1); } DisjointIterator begin() { return { m_chunks }; } DisjointIterator end() { return { m_chunks, {} }; } DisjointIterator begin() const { return { m_chunks }; } DisjointIterator end() const { return { m_chunks, {} }; } private: struct ChunkAndOffset { ChunkType* chunk; size_t offset; }; ChunkAndOffset chunk_around(size_t index) { if (m_chunks.is_empty()) return { nullptr, index }; size_t offset = 0; for (auto& chunk : m_chunks) { if (chunk.is_empty()) continue; auto next_offset = chunk.size() + offset; if (next_offset <= index) { offset = next_offset; continue; } return { &chunk, index - offset }; } return { &m_chunks.last(), index - (offset - m_chunks.last().size()) }; } Vector m_chunks; }; template struct Traits> : public DefaultTraits> { static unsigned hash(DisjointSpans const& span) { unsigned hash = 0; for (auto const& value : span) { auto value_hash = Traits::hash(value); hash = pair_int_hash(hash, value_hash); } return hash; } constexpr static bool is_trivial() { return false; } }; } #if USING_AK_GLOBALLY using AK::DisjointChunks; using AK::DisjointSpans; #endif