1/*****************************************************************************
2
3  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4  more contributor license agreements.  See the NOTICE file distributed
5  with this work for additional information regarding copyright ownership.
6  Accellera licenses this file to you under the Apache License, Version 2.0
7  (the "License"); you may not use this file except in compliance with the
8  License.  You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15  implied.  See the License for the specific language governing
16  permissions and limitations under the License.
17
18 *****************************************************************************/
19
20#include <tlm_utils/instance_specific_extensions_int.h>
21
22#include <iostream>
23#include <map>
24#include <typeindex>
25
26namespace tlm
27{
28
29template class tlm_array<tlm_utils::ispex_base *>;
30
31} // namespace tlm
32
33namespace tlm_utils
34{
35
36namespace
37{
38
39class ispex_registry // Copied from tlm_gp.cpp.
40{
41    typedef unsigned int key_type;
42    typedef std::map<std::type_index, key_type> type_map;
43
44  public:
45    static ispex_registry &
46    instance()
47    {
48        if (!instance_) {
49            // Don't cleanup registry.
50            instance_ = new ispex_registry();
51        }
52        return *instance_;
53    }
54
55    unsigned int
56    register_extension(std::type_index type)
57    {
58        type_map::const_iterator it = ids_.find(type);
59
60        if (it == ids_.end()) {
61            // New extension - generate/store ID.
62            type_map::value_type v(type, static_cast<key_type>(ids_.size()));
63            ids_.insert(v);
64            return v.second;
65        }
66        return it->second;
67    }
68
69    static unsigned int
70    max_num_extensions()
71    {
72        return (instance_) ? instance().ids_.size() : 0;
73    }
74
75  private:
76    static ispex_registry *instance_;
77    type_map ids_;
78    ispex_registry() {}
79};
80
81ispex_registry *ispex_registry::instance_ = nullptr;
82
83} //  anonymous namespace
84
85unsigned int
86ispex_base::register_private_extension(const std::type_info &type)
87{
88    return ispex_registry::instance().register_extension(type);
89}
90
91// Helper to do the numbering of private extension accessors.
92static unsigned int
93max_num_ispex_accessors(bool increment=false)
94{
95    static unsigned int max_num = 0;
96    if (increment)
97        ++max_num;
98    return max_num;
99}
100
101// ----------------------------------------------------------------------------
102
103// The pool for the container, plain as can be.
104class instance_specific_extension_container_pool
105{
106    instance_specific_extension_container_pool() : unused(nullptr) {}
107    ~instance_specific_extension_container_pool();
108
109  public:
110    static instance_specific_extension_container_pool &
111    instance()
112    {
113        static instance_specific_extension_container_pool inst;
114        return inst;
115    }
116
117    instance_specific_extension_container *create();
118    void free(instance_specific_extension_container *);
119
120  private:
121    instance_specific_extension_container *unused;
122};
123
124instance_specific_extension_container *
125instance_specific_extension_container_pool::create()
126{
127    if (!unused) {
128        unused = new instance_specific_extension_container();
129    }
130    instance_specific_extension_container *tmp = unused;
131    unused = unused->next;
132    return tmp;
133}
134
135void
136instance_specific_extension_container_pool::free(
137        instance_specific_extension_container *cont)
138{
139    cont->next = unused;
140    unused = cont;
141}
142
143instance_specific_extension_container_pool::
144    ~instance_specific_extension_container_pool()
145{
146    while (unused) {
147        instance_specific_extension_container *tmp = unused;
148        unused = unused->next;
149        delete tmp;
150    }
151}
152
153// ----------------------------------------------------------------------------
154
155instance_specific_extension_container *
156instance_specific_extension_container::create()
157{
158    return instance_specific_extension_container_pool::instance().create();
159}
160
161instance_specific_extension_container::
162    instance_specific_extension_container() :
163    use_count(0), m_txn(NULL), m_release_fn(NULL), m_carrier(NULL), next(NULL)
164{
165    resize();
166}
167
168void
169instance_specific_extension_container::
170    attach_carrier(instance_specific_extension_carrier *carrier,
171            void *txn, release_fn *rel_fn)
172{
173    m_txn = txn;
174    m_release_fn = rel_fn;
175    m_carrier = carrier;
176}
177
178void
179instance_specific_extension_container::resize()
180{
181    m_ispex_per_accessor.resize(max_num_ispex_accessors());
182
183    for (unsigned int i = 0; i < m_ispex_per_accessor.size(); ++i) {
184        m_ispex_per_accessor[i] =
185            new instance_specific_extensions_per_accessor(this);
186        m_ispex_per_accessor[i]->resize_extensions();
187    }
188}
189
190instance_specific_extension_container::
191    ~instance_specific_extension_container()
192{
193    for (unsigned int i = 0; i < m_ispex_per_accessor.size(); ++i)
194        delete m_ispex_per_accessor[i];
195}
196
197void
198instance_specific_extension_container::inc_use_count()
199{
200    use_count++;
201}
202
203void
204instance_specific_extension_container::dec_use_count()
205{
206    if ((--use_count) == 0) {
207        // If this container isn't used any more we release the carrier
208        // extension.
209        m_release_fn(m_carrier, m_txn);
210        // We send it back to our pool.
211        instance_specific_extension_container_pool::instance().free(this);
212    }
213}
214
215instance_specific_extensions_per_accessor *
216instance_specific_extension_container::get_accessor(unsigned int idx)
217{
218    return m_ispex_per_accessor[idx];
219}
220
221// ----------------------------------------------------------------------------
222
223// non-templatized version with manual index:
224ispex_base *
225instance_specific_extensions_per_accessor::set_extension(
226        unsigned int index, ispex_base *ext)
227{
228    resize_extensions();
229    ispex_base *tmp = m_extensions[index];
230    m_extensions[index] = ext;
231    if (!tmp && ext)
232        m_container->inc_use_count();
233    return tmp;
234}
235
236ispex_base *
237instance_specific_extensions_per_accessor::get_extension(
238        unsigned int index) const
239{
240    return (index < m_extensions.size()) ? m_extensions[index] : nullptr;
241}
242
243void
244instance_specific_extensions_per_accessor::clear_extension(unsigned int index)
245{
246    if (index < m_extensions.size()) {
247        if (m_extensions[index])
248            m_container->dec_use_count();
249        m_extensions[index] = static_cast<ispex_base *>(nullptr);
250    }
251}
252
253void
254instance_specific_extensions_per_accessor::resize_extensions()
255{
256    m_extensions.expand(ispex_registry::max_num_extensions());
257}
258
259// ----------------------------------------------------------------------------
260
261instance_specific_extension_accessor::instance_specific_extension_accessor() :
262    m_index(max_num_ispex_accessors(true) - 1)
263{}
264
265} // namespace tlm_utils
266