| 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
 |