1 | /*
|
2 | * Souffle - A Datalog Compiler
|
3 | * Copyright (c) 2022, The Souffle Developers. All rights reserved
|
4 | * Licensed under the Universal Permissive License v 1.0 as shown at:
|
5 | * - https://opensource.org/licenses/UPL
|
6 | * - <souffle root>/licenses/SOUFFLE-UPL.txt
|
7 | */
|
8 |
|
9 | /**
|
10 | * @file RecordTableImpl.h
|
11 | *
|
12 | * RecordTable definition
|
13 | */
|
14 |
|
15 | #pragma once
|
16 |
|
17 | #include "souffle/RamTypes.h"
|
18 | #include "souffle/RecordTable.h"
|
19 | #include "souffle/datastructure/ConcurrentFlyweight.h"
|
20 | #include "souffle/utility/span.h"
|
21 |
|
22 | #include <cassert>
|
23 | #include <cstddef>
|
24 | #include <limits>
|
25 | #include <memory>
|
26 | #include <utility>
|
27 | #include <vector>
|
28 |
|
29 | namespace souffle {
|
30 |
|
31 | namespace details {
|
32 |
|
33 | // Helper to unroll for loop
|
34 | template <auto Start, auto End, auto Inc, class F>
|
35 | constexpr void constexpr_for(F&& f) {
|
36 | if constexpr (Start < End) {
|
37 | f(std::integral_constant<decltype(Start), Start>());
|
38 | constexpr_for<Start + Inc, End, Inc>(f);
|
39 | }
|
40 | }
|
41 |
|
42 | /// @brief The data-type of RamDomain records of any size.
|
43 | using GenericRecord = std::vector<RamDomain>;
|
44 |
|
45 | /// @brief The data-type of RamDomain records of specialized size.
|
46 | template <std::size_t Arity>
|
47 | using SpecializedRecord = std::array<RamDomain, Arity>;
|
48 |
|
49 | /// @brief A view in a sequence of RamDomain value.
|
50 | // TODO: use a `span`.
|
51 | struct GenericRecordView {
|
52 | explicit GenericRecordView(const RamDomain* Data, const std::size_t Arity) : Data(Data), Arity(Arity) {}
|
53 | GenericRecordView(const GenericRecordView& Other) : Data(Other.Data), Arity(Other.Arity) {}
|
54 | GenericRecordView(GenericRecordView&& Other) : Data(Other.Data), Arity(Other.Arity) {}
|
55 |
|
56 | const RamDomain* const Data;
|
57 | const std::size_t Arity;
|
58 |
|
59 | const RamDomain* data() const {
|
60 | return Data;
|
61 | }
|
62 |
|
63 | const RamDomain& operator[](int I) const {
|
64 | assert(I >= 0 && static_cast<std::size_t>(I) < Arity);
|
65 | return Data[I];
|
66 | }
|
67 | };
|
68 |
|
69 | template <std::size_t Arity>
|
70 | struct SpecializedRecordView {
|
71 | explicit SpecializedRecordView(const RamDomain* Data) : Data(Data) {}
|
72 | SpecializedRecordView(const SpecializedRecordView& Other) : Data(Other.Data) {}
|
73 | SpecializedRecordView(SpecializedRecordView&& Other) : Data(Other.Data) {}
|
74 |
|
75 | const RamDomain* const Data;
|
76 |
|
77 | const RamDomain* data() const {
|
78 | return Data;
|
79 | }
|
80 |
|
81 | const RamDomain& operator[](int I) const {
|
82 | assert(I >= 0 && static_cast<std::size_t>(I) < Arity);
|
83 | return Data[I];
|
84 | }
|
85 | };
|
86 |
|
87 | /// @brief Hash function object for a RamDomain record.
|
88 | struct GenericRecordHash {
|
89 | explicit GenericRecordHash(const std::size_t Arity) : Arity(Arity) {}
|
90 | GenericRecordHash(const GenericRecordHash& Other) : Arity(Other.Arity) {}
|
91 | GenericRecordHash(GenericRecordHash&& Other) : Arity(Other.Arity) {}
|
92 |
|
93 | const std::size_t Arity;
|
94 | std::hash<RamDomain> domainHash;
|
95 |
|
96 | template <typename T>
|
97 | std::size_t operator()(const T& Record) const {
|
98 | std::size_t Seed = 0;
|
99 | for (std::size_t I = 0; I < Arity; ++I) {
|
100 | Seed ^= domainHash(Record[(int)I]) + 0x9e3779b9U + (Seed << 6U) + (Seed >> 2U);
|
101 | }
|
102 | return Seed;
|
103 | }
|
104 | };
|
105 |
|
106 | template <std::size_t Arity>
|
107 | struct SpecializedRecordHash {
|
108 | explicit SpecializedRecordHash() {}
|
109 | SpecializedRecordHash(const SpecializedRecordHash& Other) : DomainHash(Other.DomainHash) {}
|
110 | SpecializedRecordHash(SpecializedRecordHash&& Other) : DomainHash(Other.DomainHash) {}
|
111 |
|
112 | std::hash<RamDomain> DomainHash;
|
113 |
|
114 | template <typename T>
|
115 | std::size_t operator()(const T& Record) const {
|
116 | std::size_t Seed = 0;
|
117 | constexpr_for<0, Arity, 1>([&](auto I) {
|
118 | Seed ^= DomainHash(Record[(int)I]) + 0x9e3779b9U + (Seed << 6U) + (Seed >> 2U);
|
119 | });
|
120 | return Seed;
|
121 | }
|
122 | };
|
123 |
|
124 | template <>
|
125 | struct SpecializedRecordHash<0> {
|
126 | explicit SpecializedRecordHash() {}
|
127 | SpecializedRecordHash(const SpecializedRecordHash&) {}
|
128 | SpecializedRecordHash(SpecializedRecordHash&&) {}
|
129 |
|
130 | template <typename T>
|
131 | std::size_t operator()(const T&) const {
|
132 | return 0;
|
133 | }
|
134 | };
|
135 |
|
136 | /// @brief Equality function object for RamDomain records.
|
137 | struct GenericRecordEqual {
|
138 | explicit GenericRecordEqual(const std::size_t Arity) : Arity(Arity) {}
|
139 | GenericRecordEqual(const GenericRecordEqual& Other) : Arity(Other.Arity) {}
|
140 | GenericRecordEqual(GenericRecordEqual&& Other) : Arity(Other.Arity) {}
|
141 |
|
142 | const std::size_t Arity;
|
143 |
|
144 | template <typename T, typename U>
|
145 | bool operator()(const T& A, const U& B) const {
|
146 | return (std::memcmp(A.data(), B.data(), Arity * sizeof(RamDomain)) == 0);
|
147 | }
|
148 | };
|
149 |
|
150 | template <std::size_t Arity>
|
151 | struct SpecializedRecordEqual {
|
152 | explicit SpecializedRecordEqual() {}
|
153 | SpecializedRecordEqual(const SpecializedRecordEqual&) {}
|
154 | SpecializedRecordEqual(SpecializedRecordEqual&&) {}
|
155 |
|
156 | template <typename T, typename U>
|
157 | bool operator()(const T& A, const U& B) const {
|
158 | constexpr std::size_t Len = Arity * sizeof(RamDomain);
|
159 | return (std::memcmp(A.data(), B.data(), Len) == 0);
|
160 | }
|
161 | };
|
162 |
|
163 | template <>
|
164 | struct SpecializedRecordEqual<0> {
|
165 | explicit SpecializedRecordEqual() {}
|
166 | SpecializedRecordEqual(const SpecializedRecordEqual&) {}
|
167 | SpecializedRecordEqual(SpecializedRecordEqual&&) {}
|
168 |
|
169 | template <typename T, typename U>
|
170 | bool operator()(const T&, const U&) const {
|
171 | return true;
|
172 | }
|
173 | };
|
174 |
|
175 | /// @brief Less function object for RamDomain records.
|
176 | struct GenericRecordLess {
|
177 | explicit GenericRecordLess(const std::size_t Arity) : Arity(Arity) {}
|
178 | GenericRecordLess(const GenericRecordLess& Other) : Arity(Other.Arity) {}
|
179 | GenericRecordLess(GenericRecordLess&& Other) : Arity(Other.Arity) {}
|
180 |
|
181 | const std::size_t Arity;
|
182 |
|
183 | template <typename T, typename U>
|
184 | bool operator()(const T& A, const U& B) const {
|
185 | return (std::memcmp(A.data(), B.data(), Arity * sizeof(RamDomain)) < 0);
|
186 | }
|
187 | };
|
188 |
|
189 | template <std::size_t Arity>
|
190 | struct SpecializedRecordLess {
|
191 | explicit SpecializedRecordLess() {}
|
192 | SpecializedRecordLess(const SpecializedRecordLess&) {}
|
193 | SpecializedRecordLess(SpecializedRecordLess&&) {}
|
194 |
|
195 | template <typename T, typename U>
|
196 | bool operator()(const T& A, const U& B) const {
|
197 | constexpr std::size_t Len = Arity * sizeof(RamDomain);
|
198 | return (std::memcmp(A.data(), B.data(), Len) < 0);
|
199 | }
|
200 | };
|
201 |
|
202 | template <>
|
203 | struct SpecializedRecordLess<0> {
|
204 | explicit SpecializedRecordLess() {}
|
205 | SpecializedRecordLess(const SpecializedRecordLess&) {}
|
206 | SpecializedRecordLess(SpecializedRecordLess&&) {}
|
207 |
|
208 | template <typename T, typename U>
|
209 | bool operator()(const T&, const U&) const {
|
210 | return false;
|
211 | }
|
212 | };
|
213 |
|
214 | /// @brief Compare function object for RamDomain records.
|
215 | struct GenericRecordCmp {
|
216 | explicit GenericRecordCmp(const std::size_t Arity) : Arity(Arity) {}
|
217 | GenericRecordCmp(const GenericRecordCmp& Other) : Arity(Other.Arity) {}
|
218 | GenericRecordCmp(GenericRecordCmp&& Other) : Arity(Other.Arity) {}
|
219 |
|
220 | const std::size_t Arity;
|
221 |
|
222 | template <typename T, typename U>
|
223 | int operator()(const T& A, const U& B) const {
|
224 | return std::memcmp(A.data(), B.data(), Arity * sizeof(RamDomain));
|
225 | }
|
226 | };
|
227 |
|
228 | template <std::size_t Arity>
|
229 | struct SpecializedRecordCmp {
|
230 | explicit SpecializedRecordCmp() {}
|
231 | SpecializedRecordCmp(const SpecializedRecordCmp&) {}
|
232 | SpecializedRecordCmp(SpecializedRecordCmp&&) {}
|
233 |
|
234 | template <typename T, typename U>
|
235 | bool operator()(const T& A, const U& B) const {
|
236 | constexpr std::size_t Len = Arity * sizeof(RamDomain);
|
237 | return std::memcmp(A.data(), B.data(), Len);
|
238 | }
|
239 | };
|
240 |
|
241 | template <>
|
242 | struct SpecializedRecordCmp<0> {
|
243 | explicit SpecializedRecordCmp() {}
|
244 | SpecializedRecordCmp(const SpecializedRecordCmp&) {}
|
245 | SpecializedRecordCmp(SpecializedRecordCmp&&) {}
|
246 |
|
247 | template <typename T, typename U>
|
248 | bool operator()(const T&, const U&) const {
|
249 | return 0;
|
250 | }
|
251 | };
|
252 |
|
253 | /// @brief Factory of RamDomain record.
|
254 | struct GenericRecordFactory {
|
255 | using value_type = GenericRecord;
|
256 | using pointer = GenericRecord*;
|
257 | using reference = GenericRecord&;
|
258 |
|
259 | explicit GenericRecordFactory(const std::size_t Arity) : Arity(Arity) {}
|
260 | GenericRecordFactory(const GenericRecordFactory& Other) : Arity(Other.Arity) {}
|
261 | GenericRecordFactory(GenericRecordFactory&& Other) : Arity(Other.Arity) {}
|
262 |
|
263 | const std::size_t Arity;
|
264 |
|
265 | reference replace(reference Place, const std::vector<RamDomain>& V) {
|
266 | assert(V.size() == Arity);
|
267 | Place = V;
|
268 | return Place;
|
269 | }
|
270 |
|
271 | reference replace(reference Place, const GenericRecordView& V) {
|
272 | Place.clear();
|
273 | Place.insert(Place.begin(), V.data(), V.data() + Arity);
|
274 | return Place;
|
275 | }
|
276 |
|
277 | reference replace(reference Place, const RamDomain* V) {
|
278 | Place.clear();
|
279 | Place.insert(Place.begin(), V, V + Arity);
|
280 | return Place;
|
281 | }
|
282 | };
|
283 |
|
284 | template <std::size_t Arity>
|
285 | struct SpecializedRecordFactory {
|
286 | using value_type = SpecializedRecord<Arity>;
|
287 | using pointer = SpecializedRecord<Arity>*;
|
288 | using reference = SpecializedRecord<Arity>&;
|
289 |
|
290 | explicit SpecializedRecordFactory() {}
|
291 | SpecializedRecordFactory(const SpecializedRecordFactory&) {}
|
292 | SpecializedRecordFactory(SpecializedRecordFactory&&) {}
|
293 |
|
294 | reference replace(reference Place, const SpecializedRecord<Arity>& V) {
|
295 | assert(V.size() == Arity);
|
296 | Place = V;
|
297 | return Place;
|
298 | }
|
299 |
|
300 | reference replace(reference Place, const SpecializedRecordView<Arity>& V) {
|
301 | constexpr std::size_t Len = Arity * sizeof(RamDomain);
|
302 | std::memcpy(Place.data(), V.data(), Len);
|
303 | return Place;
|
304 | }
|
305 |
|
306 | reference replace(reference Place, const RamDomain* V) {
|
307 | constexpr std::size_t Len = Arity * sizeof(RamDomain);
|
308 | std::memcpy(Place.data(), V, Len);
|
309 | return Place;
|
310 | }
|
311 | };
|
312 |
|
313 | template <>
|
314 | struct SpecializedRecordFactory<0> {
|
315 | using value_type = SpecializedRecord<0>;
|
316 | using pointer = SpecializedRecord<0>*;
|
317 | using reference = SpecializedRecord<0>&;
|
318 |
|
319 | explicit SpecializedRecordFactory() {}
|
320 | SpecializedRecordFactory(const SpecializedRecordFactory&) {}
|
321 | SpecializedRecordFactory(SpecializedRecordFactory&&) {}
|
322 |
|
323 | reference replace(reference Place, const SpecializedRecord<0>&) {
|
324 | return Place;
|
325 | }
|
326 |
|
327 | reference replace(reference Place, const SpecializedRecordView<0>&) {
|
328 | return Place;
|
329 | }
|
330 |
|
331 | reference replace(reference Place, const RamDomain*) {
|
332 | return Place;
|
333 | }
|
334 | };
|
335 |
|
336 | } // namespace details
|
337 |
|
338 | /** @brief Interface of bidirectional mappping between records and record references. */
|
339 | class RecordMap {
|
340 | public:
|
341 | virtual ~RecordMap() {}
|
342 | virtual void setNumLanes(const std::size_t NumLanes) = 0;
|
343 | virtual RamDomain pack(const std::vector<RamDomain>& Vector) = 0;
|
344 | virtual RamDomain pack(const RamDomain* Tuple) = 0;
|
345 | virtual RamDomain pack(const std::initializer_list<RamDomain>& List) = 0;
|
346 | virtual const RamDomain* unpack(RamDomain index) const = 0;
|
347 | };
|
348 |
|
349 | /** @brief Bidirectional mappping between records and record references, for any record arity. */
|
350 | class GenericRecordMap : public RecordMap,
|
351 | protected FlyweightImpl<details::GenericRecord, details::GenericRecordHash,
|
352 | details::GenericRecordEqual, details::GenericRecordFactory> {
|
353 | using Base = FlyweightImpl<details::GenericRecord, details::GenericRecordHash,
|
354 | details::GenericRecordEqual, details::GenericRecordFactory>;
|
355 |
|
356 | const std::size_t Arity;
|
357 |
|
358 | public:
|
359 | explicit GenericRecordMap(const std::size_t lane_count, const std::size_t arity)
|
360 | : Base(lane_count, 8, true, details::GenericRecordHash(arity), details::GenericRecordEqual(arity),
|
361 | details::GenericRecordFactory(arity)),
|
362 | Arity(arity) {}
|
363 |
|
364 | virtual ~GenericRecordMap() {}
|
365 |
|
366 | void setNumLanes(const std::size_t NumLanes) override {
|
367 | Base::setNumLanes(NumLanes);
|
368 | }
|
369 |
|
370 | /** @brief converts record to a record reference */
|
371 | RamDomain pack(const std::vector<RamDomain>& Vector) override {
|
372 | return findOrInsert(Vector).first;
|
373 | };
|
374 |
|
375 | /** @brief converts record to a record reference */
|
376 | RamDomain pack(const RamDomain* Tuple) override {
|
377 | details::GenericRecordView View{Tuple, Arity};
|
378 | return findOrInsert(View).first;
|
379 | }
|
380 |
|
381 | /** @brief converts record to a record reference */
|
382 | RamDomain pack(const std::initializer_list<RamDomain>& List) override {
|
383 | details::GenericRecordView View{std::data(List), Arity};
|
384 | return findOrInsert(View).first;
|
385 | }
|
386 |
|
387 | /** @brief convert record reference to a record pointer */
|
388 | const RamDomain* unpack(RamDomain Index) const override {
|
389 | return fetch(Index).data();
|
390 | }
|
391 | };
|
392 |
|
393 | /** @brief Bidirectional mappping between records and record references, specialized for a record arity. */
|
394 | template <std::size_t Arity>
|
395 | class SpecializedRecordMap
|
396 | : public RecordMap,
|
397 | protected FlyweightImpl<details::SpecializedRecord<Arity>, details::SpecializedRecordHash<Arity>,
|
398 | details::SpecializedRecordEqual<Arity>, details::SpecializedRecordFactory<Arity>> {
|
399 | using Record = details::SpecializedRecord<Arity>;
|
400 | using RecordView = details::SpecializedRecordView<Arity>;
|
401 | using RecordHash = details::SpecializedRecordHash<Arity>;
|
402 | using RecordEqual = details::SpecializedRecordEqual<Arity>;
|
403 | using RecordFactory = details::SpecializedRecordFactory<Arity>;
|
404 | using Base = FlyweightImpl<Record, RecordHash, RecordEqual, RecordFactory>;
|
405 |
|
406 | public:
|
407 | SpecializedRecordMap(const std::size_t LaneCount)
|
408 | : Base(LaneCount, 8, true, RecordHash(), RecordEqual(), RecordFactory()) {}
|
409 |
|
410 | virtual ~SpecializedRecordMap() {}
|
411 |
|
412 | void setNumLanes(const std::size_t NumLanes) override {
|
413 | Base::setNumLanes(NumLanes);
|
414 | }
|
415 |
|
416 | /** @brief converts record to a record reference */
|
417 | RamDomain pack(const std::vector<RamDomain>& Vector) override {
|
418 | assert(Vector.size() == Arity);
|
419 | RecordView View{Vector.data()};
|
420 | return Base::findOrInsert(View).first;
|
421 | };
|
422 |
|
423 | /** @brief converts record to a record reference */
|
424 | RamDomain pack(const RamDomain* Tuple) override {
|
425 | RecordView View{Tuple};
|
426 | return Base::findOrInsert(View).first;
|
427 | }
|
428 |
|
429 | /** @brief converts record to a record reference */
|
430 | RamDomain pack(const std::initializer_list<RamDomain>& List) override {
|
431 | assert(List.size() == Arity);
|
432 | RecordView View{std::data(List)};
|
433 | return Base::findOrInsert(View).first;
|
434 | }
|
435 |
|
436 | /** @brief convert record reference to a record pointer */
|
437 | const RamDomain* unpack(RamDomain Index) const override {
|
438 | return Base::fetch(Index).data();
|
439 | }
|
440 | };
|
441 |
|
442 | /** Record map specialized for arity 0 */
|
443 | template <>
|
444 | class SpecializedRecordMap<0> : public RecordMap {
|
445 | // The empty record always at index 1
|
446 | // The index 0 of each map is reserved.
|
447 | static constexpr RamDomain EmptyRecordIndex = 1;
|
448 |
|
449 | // To comply with previous behavior, the empty record
|
450 | // has no data:
|
451 | const RamDomain* EmptyRecordData = nullptr;
|
452 |
|
453 | public:
|
454 | SpecializedRecordMap(const std::size_t /* LaneCount */) {}
|
455 |
|
456 | virtual ~SpecializedRecordMap() {}
|
457 |
|
458 | void setNumLanes(const std::size_t) override {}
|
459 |
|
460 | /** @brief converts record to a record reference */
|
461 | RamDomain pack([[maybe_unused]] const std::vector<RamDomain>& Vector) override {
|
462 | assert(Vector.size() == 0);
|
463 | return EmptyRecordIndex;
|
464 | };
|
465 |
|
466 | /** @brief converts record to a record reference */
|
467 | RamDomain pack(const RamDomain*) override {
|
468 | return EmptyRecordIndex;
|
469 | }
|
470 |
|
471 | /** @brief converts record to a record reference */
|
472 | RamDomain pack([[maybe_unused]] const std::initializer_list<RamDomain>& List) override {
|
473 | assert(List.size() == 0);
|
474 | return EmptyRecordIndex;
|
475 | }
|
476 |
|
477 | /** @brief convert record reference to a record pointer */
|
478 | const RamDomain* unpack([[maybe_unused]] RamDomain Index) const override {
|
479 | assert(Index == EmptyRecordIndex);
|
480 | return EmptyRecordData;
|
481 | }
|
482 | };
|
483 |
|
484 | /** A concurrent Record Table with some specialized record maps. */
|
485 | template <std::size_t... SpecializedArities>
|
486 | class SpecializedRecordTable : public RecordTable {
|
487 | private:
|
488 | // The current size of the Maps vector.
|
489 | std::size_t Size;
|
490 |
|
491 | // The record maps, indexed by arity.
|
492 | std::vector<RecordMap*> Maps;
|
493 |
|
494 | // The concurrency manager.
|
495 | mutable ConcurrentLanes Lanes;
|
496 |
|
497 | template <std::size_t Arity, std::size_t... Arities>
|
498 | void CreateSpecializedMaps() {
|
499 | if (Arity >= Size) {
|
500 | Size = Arity + 1;
|
501 | Maps.reserve(Size);
|
502 | Maps.resize(Size);
|
503 | }
|
504 | Maps[Arity] = new SpecializedRecordMap<Arity>(Lanes.lanes());
|
505 | if constexpr (sizeof...(Arities) > 0) {
|
506 | CreateSpecializedMaps<Arities...>();
|
507 | }
|
508 | }
|
509 |
|
510 | public:
|
511 | /** @brief Construct a record table with the number of concurrent access lanes. */
|
512 | SpecializedRecordTable(const std::size_t LaneCount) : Size(0), Lanes(LaneCount) {
|
513 | CreateSpecializedMaps<SpecializedArities...>();
|
514 | }
|
515 |
|
516 | SpecializedRecordTable() : SpecializedRecordTable(1) {}
|
517 |
|
518 | virtual ~SpecializedRecordTable() {
|
519 | for (auto Map : Maps) {
|
520 | delete Map;
|
521 | }
|
522 | }
|
523 |
|
524 | /**
|
525 | * @brief set the number of concurrent access lanes.
|
526 | * Not thread-safe, use only when the datastructure is not being used.
|
527 | */
|
528 | virtual void setNumLanes(const std::size_t NumLanes) override {
|
529 | Lanes.setNumLanes(NumLanes);
|
530 | for (auto& Map : Maps) {
|
531 | if (Map) {
|
532 | Map->setNumLanes(NumLanes);
|
533 | }
|
534 | }
|
535 | }
|
536 |
|
537 | /** @brief convert tuple to record reference */
|
538 | virtual RamDomain pack(const RamDomain* Tuple, const std::size_t Arity) override {
|
539 | auto Guard = Lanes.guard();
|
540 | return lookupMap(Arity).pack(Tuple);
|
541 | }
|
542 |
|
543 | /** @brief convert tuple to record reference */
|
544 | virtual RamDomain pack(const std::initializer_list<RamDomain>& List) override {
|
545 | auto Guard = Lanes.guard();
|
546 | return lookupMap(List.size()).pack(std::data(List));
|
547 | }
|
548 |
|
549 | /** @brief convert record reference to a record */
|
550 | virtual const RamDomain* unpack(const RamDomain Ref, const std::size_t Arity) const override {
|
551 | auto Guard = Lanes.guard();
|
552 | return lookupMap(Arity).unpack(Ref);
|
553 | }
|
554 |
|
555 | private:
|
556 | /** @brief lookup RecordMap for a given arity; the map for that arity must exist. */
|
557 | RecordMap& lookupMap(const std::size_t Arity) const {
|
558 | assert(Arity < Size && "Lookup for an arity while there is no record for that arity.");
|
559 | auto* Map = Maps[Arity];
|
560 | assert(Map != nullptr && "Lookup for an arity while there is no record for that arity.");
|
561 | return *Map;
|
562 | }
|
563 |
|
564 | /** @brief lookup RecordMap for a given arity; if it does not exist, create new RecordMap */
|
565 | RecordMap& lookupMap(const std::size_t Arity) {
|
566 | if (Arity < Size) {
|
567 | auto* Map = Maps[Arity];
|
568 | if (Map) {
|
569 | return *Map;
|
570 | }
|
571 | }
|
572 |
|
573 | createMap(Arity);
|
574 | return *Maps[Arity];
|
575 | }
|
576 |
|
577 | /** @brief create the RecordMap for the given arity. */
|
578 | void createMap(const std::size_t Arity) {
|
579 | Lanes.beforeLockAllBut();
|
580 | if (Arity < Size && Maps[Arity] != nullptr) {
|
581 | // Map of required arity has been created concurrently
|
582 | Lanes.beforeUnlockAllBut();
|
583 | return;
|
584 | }
|
585 | Lanes.lockAllBut();
|
586 |
|
587 | if (Arity >= Size) {
|
588 | Size = Arity + 1;
|
589 | Maps.reserve(Size);
|
590 | Maps.resize(Size);
|
591 | }
|
592 | Maps[Arity] = new GenericRecordMap(Lanes.lanes(), Arity);
|
593 |
|
594 | Lanes.beforeUnlockAllBut();
|
595 | Lanes.unlockAllBut();
|
596 | }
|
597 | };
|
598 |
|
599 | } // namespace souffle
|