1 // Copyright(C) 1999-2020 National Technology & Engineering Solutions
2 // of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
3 // NTESS, the U.S. Government retains certain rights in this software.
4 //
5 // See packages/seacas/LICENSE for details
6 
7 #include <Ioss_CompositeVariableType.h>
8 #include <Ioss_ConstructedVariableType.h>
9 #include <Ioss_NamedSuffixVariableType.h>
10 #include <Ioss_Utils.h>
11 #include <Ioss_VariableType.h>
12 #include <algorithm>
13 #include <cassert>
14 #include <cmath>
15 #include <cstddef>
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cstring>
19 #include <fmt/ostream.h>
20 #include <map>
21 #include <sstream>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 namespace Ioss {
insert(const VTM_ValuePair & value,bool delete_me)27   void Registry::insert(const VTM_ValuePair &value, bool delete_me)
28   {
29     m_registry.insert(value);
30     if (delete_me) {
31       m_deleteThese.push_back(value.second);
32     }
33   }
34 
~Registry()35   Registry::~Registry()
36   {
37     for (auto &entry : m_deleteThese) {
38       delete entry;
39     }
40   }
41 
42   VariableType::~VariableType() = default;
43 
VariableType(const std::string & type,int comp_count,bool delete_me)44   VariableType::VariableType(const std::string &type, int comp_count, bool delete_me)
45       : name_(type), componentCount(comp_count)
46   {
47     std::string low_type = Utils::lowercase(type);
48     registry().insert(VTM_ValuePair(low_type, this), delete_me);
49 
50     // Register uppercase version also
51     std::string up_type = Utils::uppercase(type);
52     registry().insert(VTM_ValuePair(up_type, this), false);
53   }
54 
alias(const std::string & base,const std::string & syn)55   void VariableType::alias(const std::string &base, const std::string &syn)
56   {
57     registry().insert(
58         VTM_ValuePair(Utils::lowercase(syn), const_cast<VariableType *>(factory(base))), false);
59     // Register uppercase version also
60     std::string up_type = Utils::uppercase(syn);
61     registry().insert(VTM_ValuePair(up_type, const_cast<VariableType *>(factory(base))), false);
62   }
63 
registry()64   Registry &VariableType::registry()
65   {
66     static Registry registry_;
67     return registry_;
68   }
69 
70   /** \brief Get the names of variable types known to IOSS.
71    *
72    *  \param[out] names The list of known variable type names.
73    *  \returns The number of known variable types.
74    */
describe(NameList * names)75   int VariableType::describe(NameList *names)
76   {
77     int count = 0;
78     for (auto &entry : registry()) {
79       names->push_back(entry.first);
80       count++;
81     }
82     return count;
83   }
84 
add_field_type_mapping(const std::string & raw_field,const std::string & raw_type)85   bool VariableType::add_field_type_mapping(const std::string &raw_field,
86                                             const std::string &raw_type)
87   {
88     // See if storage type 'type' exists...
89     std::string field = Utils::lowercase(raw_field);
90     std::string type  = Utils::lowercase(raw_type);
91     if (registry().find(type) == registry().end()) {
92       return false;
93     }
94 
95     // Add mapping.
96     return registry().customFieldTypes.insert(std::make_pair(field, type)).second;
97   }
98 
create_named_suffix_field_type(const std::string & type_name,const std::vector<std::string> & suffices)99   bool VariableType::create_named_suffix_field_type(const std::string &             type_name,
100                                                     const std::vector<std::string> &suffices)
101   {
102     size_t count = suffices.size();
103     if (count < 1) {
104       return false;
105     }
106 
107     std::string low_name = Utils::lowercase(type_name);
108     // See if the variable already exists...
109     if (registry().find(low_name) != registry().end()) {
110       return false;
111     }
112 
113     // Create the variable.  Note that the 'true' argument means Ioss will delete
114     // the pointer.
115     auto var_type = new NamedSuffixVariableType(low_name, count, true);
116 
117     for (size_t i = 0; i < count; i++) {
118       var_type->add_suffix(i + 1, suffices[i]);
119     }
120     return true;
121   }
122 
get_field_type_mapping(const std::string & field,std::string * type)123   bool VariableType::get_field_type_mapping(const std::string &field, std::string *type)
124   {
125     // Returns true if a mapping exists, 'type' contains the mapped type.
126     // Returns false if no custom mapping exists for this field.
127     std::string low_field = Utils::lowercase(field);
128 
129     if (registry().customFieldTypes.find(low_field) == registry().customFieldTypes.end()) {
130       return false;
131     }
132 
133     *type = registry().customFieldTypes.find(low_field)->second;
134     return true;
135   }
136 
factory(const std::string & raw_name,int copies)137   const VariableType *VariableType::factory(const std::string &raw_name, int copies)
138   {
139     VariableType *inst = nullptr;
140     std::string   name = Utils::lowercase(raw_name);
141     auto          iter = registry().find(name);
142     if (iter == registry().end()) {
143       bool can_construct = build_variable_type(name);
144       if (can_construct) {
145         iter = registry().find(name);
146         assert(iter != registry().end());
147         inst = (*iter).second;
148       }
149       else {
150         std::ostringstream errmsg;
151         fmt::print(errmsg, "ERROR: The variable type '{}' is not supported.\n", raw_name);
152         IOSS_ERROR(errmsg);
153       }
154     }
155     else {
156       inst = (*iter).second;
157     }
158 
159     if (copies != 1) {
160       inst = CompositeVariableType::composite_variable_type(inst, copies);
161     }
162     assert(inst != nullptr);
163     return inst;
164   }
165 
factory(const std::vector<Suffix> & suffices)166   const VariableType *VariableType::factory(const std::vector<Suffix> &suffices)
167   {
168     size_t size = suffices.size();
169     // Maximum suffix size is currently 5.
170     assert(size < 100000);
171     const VariableType *ivt = nullptr;
172     if (size <= 1) {
173       return nullptr; // All storage types must have at least 2 components.
174     }
175 
176     bool match = false;
177     for (const auto &vtype : registry()) {
178       ivt = vtype.second;
179       if (ivt->suffix_count() == static_cast<int>(size)) {
180         if (ivt->match(suffices)) {
181           match = true;
182           break;
183         }
184       }
185     }
186 
187     if (!match) {
188       match = true;
189       // Check if the suffices form a sequence (1,2,3,...,N)
190       // This indicates a "component" variable type that is
191       // constructed "on-the-fly" for use in Sierra
192       //
193       size_t width = Ioss::Utils::number_width(size);
194       for (size_t i = 0; i < size; i++) {
195         std::string digits = fmt::format("{:0{}}", i + 1, width);
196         if (!Ioss::Utils::str_equal(&suffices[i].m_data[0], digits)) {
197           match = false;
198           break;
199         }
200       }
201       if (match) {
202         // Create a new type.  For now, assume that the base type is
203         // "Real" (Sierra type).  The name of the new type is
204         // "Real[component_count]"
205         // Note that this type has not yet been constructed since
206         // it would have been found above.
207         ivt = new ConstructedVariableType(size, true);
208       }
209       else {
210         ivt = nullptr;
211       }
212     }
213     return ivt;
214   }
215 
match(const std::vector<Suffix> & suffices)216   bool VariableType::match(const std::vector<Suffix> &suffices) const
217   {
218     bool result = true;
219     if (static_cast<int>(suffices.size()) == suffix_count()) {
220       for (int i = 0; i < suffix_count(); i++) {
221         if (suffices[i] != label(i + 1)) {
222           result = false;
223           break;
224         }
225       }
226     }
227     else {
228       result = false;
229     }
230     return result;
231   }
232 
label_name(const std::string & base,int which,const char suffix_sep)233   std::string VariableType::label_name(const std::string &base, int which,
234                                        const char suffix_sep) const
235   {
236     std::string my_name = base;
237     std::string suffix  = label(which, suffix_sep);
238     if (!suffix.empty()) {
239       if (suffix_sep != 0) {
240         my_name += suffix_sep;
241       }
242       my_name += suffix;
243     }
244     return my_name;
245   }
246 
build_variable_type(const std::string & raw_type)247   bool VariableType::build_variable_type(const std::string &raw_type)
248   {
249     // See if this is a multi-component instance of a base type.
250     // An example would be REAL[2] which is a basic real type with
251     // two components.  The suffices would be .0 and .1
252 
253     std::string type = Utils::lowercase(raw_type);
254 
255     // Step 0:
256     // See if the type contains '[' and ']'
257     char const *typestr = type.c_str();
258     char const *lbrace  = std::strchr(typestr, '[');
259     char const *rbrace  = std::strrchr(typestr, ']');
260 
261     if (lbrace == nullptr || rbrace == nullptr) {
262       return false;
263     }
264 
265     // Step 1:
266     // First, we split off the basename (REAL/INTEGER) from the component count
267     // ([2])
268     // and see if the basename is a valid variable type and the count is a
269     // valid integer.
270     size_t len      = type.length() + 1;
271     auto   typecopy = new char[len];
272     Utils::copy_string(typecopy, typestr, len);
273 
274     char *base = std::strtok(typecopy, "[]");
275     assert(base != nullptr);
276     auto iter = VariableType::registry().find(base);
277     if (iter == registry().end()) {
278       delete[] typecopy;
279       return false;
280     }
281 
282     char *countstr = std::strtok(nullptr, "[]");
283     assert(countstr != nullptr);
284     int count = std::atoi(countstr);
285     if (count <= 0) {
286       delete[] typecopy;
287       return false;
288     }
289 
290     // We now know we have a valid base type and an integer
291     // specifying the number of 'components' in our new type.
292     // Create the new type and register it in the registry...
293     new ConstructedVariableType(type, count, true);
294     delete[] typecopy;
295     return true;
296   }
297 
numeric_label(int which,int ncomp,const std::string & name)298   std::string VariableType::numeric_label(int which, int ncomp, const std::string &name)
299   {
300     if (ncomp >= 100000) {
301       std::ostringstream errmsg;
302       fmt::print(errmsg,
303                  "ERROR: Variable '{}' has {:L} components which is larger than the current maximum"
304                  " of 100,000. Please contact developer.\n",
305                  name, ncomp);
306       IOSS_ERROR(errmsg);
307     }
308 
309     size_t      width  = Ioss::Utils::number_width(ncomp);
310     std::string digits = fmt::format("{:0{}}", which, width);
311     return digits;
312   }
313 } // namespace Ioss
314