aboutsummaryrefslogtreecommitdiffstats
path: root/common/indexed_store.h
blob: 5579b039fbea9f88a0542fc097494aa222450e9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2021-22  gatecat <gatecat@ds0.me>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#ifndef INDEXED_STORE_H
#define INDEXED_STORE_H

#include <algorithm>
#include <limits>
#include <vector>

#include "nextpnr_assertions.h"
#include "nextpnr_namespaces.h"

NEXTPNR_NAMESPACE_BEGIN

template <typename T> struct store_index
{
    int32_t m_index = -1;
    store_index() = default;
    explicit store_index(int32_t index) : m_index(index){};
    int32_t idx() const { return m_index; }
    void set(int32_t index) { m_index = index; }
    bool empty() const { return m_index == -1; }
    bool operator==(const store_index<T> &other) const { return m_index == other.m_index; }
    bool operator!=(const store_index<T> &other) const { return m_index != other.m_index; }
    bool operator<(const store_index<T> &other) const { return m_index < other.m_index; }
    unsigned int hash() const { return m_index; }

    operator bool() const { return !empty(); }
    bool operator!() const { return empty(); }
};

// "Slotted" indexed object store
template <typename T> class indexed_store
{
  private:
    // This should move to using std::optional at some point
    class slot
    {
      private:
        alignas(T) unsigned char storage[sizeof(T)];
        int32_t next_free;
        bool active;
        inline T &obj() { return reinterpret_cast<T &>(storage); }
        inline const T &obj() const { return reinterpret_cast<const T &>(storage); }
        friend class indexed_store<T>;

      public:
        slot() : active(false), next_free(std::numeric_limits<int32_t>::max()){};
        slot(slot &&other) : active(other.active), next_free(other.next_free)
        {
            if (active)
                ::new (static_cast<void *>(&storage)) T(std::move(other.obj()));
        };

        template <class... Args> void create(Args &&...args)
        {
            NPNR_ASSERT(!active);
            active = true;
            ::new (static_cast<void *>(&storage)) T(std::forward<Args &&>(args)...);
        }
        bool empty() const { return !active; }
        T &get()
        {
            NPNR_ASSERT(active);
            return reinterpret_cast<T &>(storage);
        }
        const T &get() const
        {
            NPNR_ASSERT(active);
            return reinterpret_cast<const T &>(storage);
        }
        void free(int32_t first_free)
        {
            NPNR_ASSERT(active);
            obj().~T();
            active = false;
            next_free = first_free;
        }
        ~slot()
        {
            if (active)
                obj().~T();
        }
    };

    std::vector<slot> slots;
    int32_t first_free = 0;
    int32_t active_count = 0;

  public:
    // Create a new entry and return its index
    template <class... Args> store_index<T> add(Args &&...args)
    {
        ++active_count;
        if (first_free == int32_t(slots.size())) {
            slots.emplace_back();
            slots.back().create(std::forward<Args &&>(args)...);
            ++first_free;
            return store_index<T>(int32_t(slots.size()) - 1);
        } else {
            int32_t idx = first_free;
            auto &slot = slots.at(idx);
            first_free = slot.next_free;
            slot.create(std::forward<Args &&>(args)...);
            return store_index<T>(idx);
        }
    }

    // Remove an entry at an index
    void remove(store_index<T> idx)
    {
        --active_count;
        slots.at(idx.m_index).free(first_free);
        first_free = idx.m_index;
    }

    // Number of live entries
    int32_t entries() const { return active_count; }

    // Reserve a certain amount of space
    void reserve(int32_t size) { slots.reserve(size); }

    // Check if an index exists
    int32_t count(store_index<T> idx)
    {
        if (idx.m_index < 0 || idx.m_index >= int32_t(slots.size()))
            return 0;
        return slots.at(idx.m_index).empty() ? 0 : 1;
    }

    // Get an item by index
    T &at(store_index<T> idx) { return slots.at(idx.m_index).get(); }
    const T &at(store_index<T> idx) const { return slots.at(idx.m_index).get(); }
    T &operator[](store_index<T> idx) { return slots.at(idx.m_index).get(); }
    const T &operator[](store_index<T> idx) const { return slots.at(idx.m_index).get(); }

    // Total size of the container
    int32_t capacity() const { return int32_t(slots.size()); }

    // Iterate over items
    class iterator
    {
      private:
        indexed_store *base;
        int32_t index = 0;

      public:
        iterator(indexed_store *base, int32_t index) : base(base), index(index){};
        inline bool operator!=(const iterator &other) const { return other.index != index; }
        inline bool operator==(const iterator &other) const { return other.index == index; }
        inline iterator operator++()
        {
            // skip over unused slots
            do {
                index++;
            } while (index < int32_t(base->slots.size()) && !base->slots.at(index).active);
            return *this;
        }
        inline iterator operator++(int)
        {
            iterator prior(*this);
            do {
                index++;
            } while (index < int32_t(base->slots.size()) && !base->slots.at(index).active);
            return prior;
        }
        T &operator*() { return base->at(store_index<T>(index)); }
        template <typename It, typename S> friend class enumerated_iterator;
    };
    iterator begin() { return iterator{this, 0}; }
    iterator end() { return iterator{this, int32_t(slots.size())}; }

    class const_iterator
    {
      private:
        const indexed_store *base;
        int32_t index = 0;

      public:
        const_iterator(const indexed_store *base, int32_t index) : base(base), index(index){};
        inline bool operator!=(const const_iterator &other) const { return other.index != index; }
        inline bool operator==(const const_iterator &other) const { return other.index == index; }
        inline const_iterator operator++()
        {
            // skip over unused slots
            do {
                index++;
            } while (index < int32_t(base->slots.size()) && !base->slots.at(index).active);
            return *this;
        }
        inline const_iterator operator++(int)
        {
            iterator prior(*this);
            do {
                index++;
            } while (index < int32_t(base->slots.size()) && !base->slots.at(index).active);
            return prior;
        }
        const T &operator*() { return base->at(store_index<T>(index)); }
        template <typename It, typename S> friend class enumerated_iterator;
    };
    const_iterator begin() const { return const_iterator{this, 0}; }
    const_iterator end() const { return const_iterator{this, int32_t(slots.size())}; }

    template <typename S> struct enumerated_item
    {
        enumerated_item(int32_t index, T &value) : index(index), value(value){};
        store_index<std::remove_const<S>> index;
        S &value;
    };

    template <typename It, typename S> class enumerated_iterator
    {
      private:
        It base;

      public:
        enumerated_iterator(const It &base) : base(base){};
        inline bool operator!=(const enumerated_iterator<It, S> &other) const { return other.base != base; }
        inline bool operator==(const enumerated_iterator<It, S> &other) const { return other.base == base; }
        inline enumerated_iterator<It, S> operator++()
        {
            ++base;
            return *this;
        }
        inline enumerated_iterator<It, S> operator++(int)
        {
            iterator prior(*this);
            ++base;
            return prior;
        }
        enumerated_item<S> operator*() { return enumerated_item<S>{base.index, *base}; }
    };

    template <typename It, typename S> struct enumerated_range
    {
        enumerated_range(const It &begin, const It &end) : m_begin(begin), m_end(end){};
        enumerated_iterator<It, S> m_begin, m_end;
        enumerated_iterator<It, S> begin() { return m_begin; }
        enumerated_iterator<It, S> end() { return m_end; }
    };

    enumerated_range<iterator, T> enumerate() { return enumerated_range<iterator, T>{begin(), end()}; }
    enumerated_range<const_iterator, const T> enumerate() const { return enumerated_range<iterator, T>{begin(), end()}; }
};

NEXTPNR_NAMESPACE_END

#endif