/src/serenity/Userland/Libraries/LibJS/Runtime/IndexedProperties.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/NonnullOwnPtr.h> |
10 | | #include <LibJS/Runtime/Shape.h> |
11 | | #include <LibJS/Runtime/Value.h> |
12 | | |
13 | | namespace JS { |
14 | | |
15 | | struct ValueAndAttributes { |
16 | | Value value; |
17 | | PropertyAttributes attributes { default_attributes }; |
18 | | |
19 | | Optional<u32> property_offset {}; |
20 | | }; |
21 | | |
22 | | class IndexedProperties; |
23 | | class IndexedPropertyIterator; |
24 | | class GenericIndexedPropertyStorage; |
25 | | |
26 | | class IndexedPropertyStorage { |
27 | | public: |
28 | 3 | virtual ~IndexedPropertyStorage() = default; |
29 | | |
30 | | enum class IsSimpleStorage { |
31 | | No, |
32 | | Yes, |
33 | | }; |
34 | | |
35 | | virtual bool has_index(u32 index) const = 0; |
36 | | virtual Optional<ValueAndAttributes> get(u32 index) const = 0; |
37 | | virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) = 0; |
38 | | virtual void remove(u32 index) = 0; |
39 | | |
40 | | virtual ValueAndAttributes take_first() = 0; |
41 | | virtual ValueAndAttributes take_last() = 0; |
42 | | |
43 | | virtual size_t size() const = 0; |
44 | | virtual size_t array_like_size() const = 0; |
45 | | virtual bool set_array_like_size(size_t new_size) = 0; |
46 | | |
47 | 400k | bool is_simple_storage() const { return m_is_simple_storage; } |
48 | | |
49 | | protected: |
50 | | explicit IndexedPropertyStorage(IsSimpleStorage is_simple_storage) |
51 | 3 | : m_is_simple_storage(is_simple_storage == IsSimpleStorage::Yes) |
52 | 3 | { |
53 | 3 | } |
54 | | |
55 | | private: |
56 | | bool m_is_simple_storage { false }; |
57 | | }; |
58 | | |
59 | | class SimpleIndexedPropertyStorage final : public IndexedPropertyStorage { |
60 | | public: |
61 | | SimpleIndexedPropertyStorage() |
62 | 3 | : IndexedPropertyStorage(IsSimpleStorage::Yes) |
63 | 3 | { |
64 | 3 | } |
65 | | explicit SimpleIndexedPropertyStorage(Vector<Value>&& initial_values); |
66 | | |
67 | | virtual bool has_index(u32 index) const override; |
68 | | virtual Optional<ValueAndAttributes> get(u32 index) const override; |
69 | | virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) override; |
70 | | virtual void remove(u32 index) override; |
71 | | |
72 | | virtual ValueAndAttributes take_first() override; |
73 | | virtual ValueAndAttributes take_last() override; |
74 | | |
75 | 0 | virtual size_t size() const override { return m_packed_elements.size(); } |
76 | 400k | virtual size_t array_like_size() const override { return m_array_size; } |
77 | | virtual bool set_array_like_size(size_t new_size) override; |
78 | | |
79 | 0 | Vector<Value> const& elements() const { return m_packed_elements; } |
80 | | |
81 | | [[nodiscard]] bool inline_has_index(u32 index) const |
82 | 0 | { |
83 | 0 | return index < m_array_size && !m_packed_elements.data()[index].is_empty(); |
84 | 0 | } |
85 | | |
86 | | [[nodiscard]] Optional<ValueAndAttributes> inline_get(u32 index) const |
87 | 0 | { |
88 | 0 | if (!inline_has_index(index)) |
89 | 0 | return {}; |
90 | 0 | return ValueAndAttributes { m_packed_elements.data()[index], default_attributes }; |
91 | 0 | } |
92 | | |
93 | | private: |
94 | | friend GenericIndexedPropertyStorage; |
95 | | |
96 | | void grow_storage_if_needed(); |
97 | | |
98 | | size_t m_array_size { 0 }; |
99 | | Vector<Value> m_packed_elements; |
100 | | }; |
101 | | |
102 | | class GenericIndexedPropertyStorage final : public IndexedPropertyStorage { |
103 | | public: |
104 | | explicit GenericIndexedPropertyStorage(SimpleIndexedPropertyStorage&&); |
105 | | explicit GenericIndexedPropertyStorage() |
106 | 0 | : IndexedPropertyStorage(IsSimpleStorage::No) |
107 | 0 | { |
108 | 0 | } |
109 | | |
110 | | virtual bool has_index(u32 index) const override; |
111 | | virtual Optional<ValueAndAttributes> get(u32 index) const override; |
112 | | virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) override; |
113 | | virtual void remove(u32 index) override; |
114 | | |
115 | | virtual ValueAndAttributes take_first() override; |
116 | | virtual ValueAndAttributes take_last() override; |
117 | | |
118 | 0 | virtual size_t size() const override { return m_sparse_elements.size(); } |
119 | 0 | virtual size_t array_like_size() const override { return m_array_size; } |
120 | | virtual bool set_array_like_size(size_t new_size) override; |
121 | | |
122 | 0 | HashMap<u32, ValueAndAttributes> const& sparse_elements() const { return m_sparse_elements; } |
123 | | |
124 | | private: |
125 | | size_t m_array_size { 0 }; |
126 | | HashMap<u32, ValueAndAttributes> m_sparse_elements; |
127 | | }; |
128 | | |
129 | | class IndexedPropertyIterator { |
130 | | public: |
131 | | IndexedPropertyIterator(IndexedProperties const&, u32 starting_index, bool skip_empty); |
132 | | |
133 | | IndexedPropertyIterator& operator++(); |
134 | | IndexedPropertyIterator& operator*(); |
135 | | bool operator!=(IndexedPropertyIterator const&) const; |
136 | | |
137 | 0 | u32 index() const { return m_index; } |
138 | | |
139 | | private: |
140 | | void skip_empty_indices(); |
141 | | |
142 | | IndexedProperties const& m_indexed_properties; |
143 | | Vector<u32> m_cached_indices; |
144 | | size_t m_next_cached_index { 0 }; |
145 | | u32 m_index { 0 }; |
146 | | bool m_skip_empty { false }; |
147 | | }; |
148 | | |
149 | | class IndexedProperties { |
150 | | public: |
151 | 12.9k | IndexedProperties() = default; |
152 | | |
153 | | explicit IndexedProperties(Vector<Value> values) |
154 | 0 | { |
155 | 0 | if (!values.is_empty()) |
156 | 0 | m_storage = make<SimpleIndexedPropertyStorage>(move(values)); |
157 | 0 | } |
158 | | |
159 | 0 | bool has_index(u32 index) const { return m_storage ? m_storage->has_index(index) : false; } |
160 | | Optional<ValueAndAttributes> get(u32 index) const; |
161 | | void put(u32 index, Value value, PropertyAttributes attributes = default_attributes); |
162 | | void remove(u32 index); |
163 | | |
164 | 0 | void append(Value value, PropertyAttributes attributes = default_attributes) { put(array_like_size(), value, attributes); } |
165 | | |
166 | 0 | IndexedPropertyIterator begin(bool skip_empty = true) const { return IndexedPropertyIterator(*this, 0, skip_empty); } |
167 | 0 | IndexedPropertyIterator end() const { return IndexedPropertyIterator(*this, array_like_size(), false); } |
168 | | |
169 | 0 | bool is_empty() const { return array_like_size() == 0; } |
170 | 400k | size_t array_like_size() const { return m_storage ? m_storage->array_like_size() : 0; } |
171 | | bool set_array_like_size(size_t); |
172 | | |
173 | 0 | IndexedPropertyStorage* storage() { return m_storage; } |
174 | 0 | IndexedPropertyStorage const* storage() const { return m_storage; } |
175 | | |
176 | | size_t real_size() const; |
177 | | |
178 | | Vector<u32> indices() const; |
179 | | |
180 | | template<typename Callback> |
181 | | void for_each_value(Callback callback) |
182 | 0 | { |
183 | 0 | if (!m_storage) |
184 | 0 | return; |
185 | 0 | if (m_storage->is_simple_storage()) { |
186 | 0 | for (auto& value : static_cast<SimpleIndexedPropertyStorage&>(*m_storage).elements()) |
187 | 0 | callback(value); |
188 | 0 | } else { |
189 | 0 | for (auto& element : static_cast<GenericIndexedPropertyStorage const&>(*m_storage).sparse_elements()) |
190 | 0 | callback(element.value.value); |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | | private: |
195 | | void switch_to_generic_storage(); |
196 | | void ensure_storage(); |
197 | | |
198 | | OwnPtr<IndexedPropertyStorage> m_storage; |
199 | | }; |
200 | | |
201 | | } |