HAL  v4.5.0-232-g8053ac4b3
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
parameter.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <cctype>
5 #include <unordered_set>
6 #include <utility>
7 
8 namespace hal
9 {
10  template<>
11  std::map<Parameter::Type, std::string> EnumStrings<Parameter::Type>::data = {
12  {Parameter::Type::Boolean, "boolean"},
13  {Parameter::Type::BitVector, "bit_vector"},
14  {Parameter::Type::LogicVector, "logic_vector"},
15  {Parameter::Type::Integer, "integer"},
16  {Parameter::Type::String, "string"},
17  {Parameter::Type::Float, "float"},
18  {Parameter::Type::Time, "time"},
19  {Parameter::Type::Enum, "enum"},
20  };
21 
22  namespace
23  {
24  std::string normalize_value(Parameter::Type type, const std::string& value)
25  {
27  return value;
28  if (value.size() < 2 || value[0] != '0')
29  return value;
30  const char pfx = static_cast<char>(std::tolower(static_cast<unsigned char>(value[1])));
31  if (pfx != 'b' && pfx != 'o' && pfx != 'x')
32  return value;
33  std::string result;
34  result.reserve(value.size());
35  result += '0';
36  result += pfx;
37  for (std::size_t i = 2; i < value.size(); ++i)
38  result += static_cast<char>(std::toupper(static_cast<unsigned char>(value[i])));
39  return result;
40  }
41 
42  // Number of bits needed to encode ``count`` distinct values, i.e. ``ceil(log2(count))``.
43  // Returns 0 for count <= 1, which the enum constructor rejects as invalid.
44  u16 enum_bit_width(size_t count)
45  {
46  if (count <= 1)
47  {
48  return 0;
49  }
50  const unsigned long long max = static_cast<unsigned long long>(count - 1);
51  return static_cast<u16>(sizeof(unsigned long long) * 8 - __builtin_clzll(max));
52  }
53 
54  // Parse a bit-vector value string (0b..., 0o..., 0x...) into a u64.
55  // Returns an error for empty input or unparseable digits.
56  Result<u64> parse_bit_vector(const std::string& value)
57  {
58  if (value.empty())
59  {
60  return ERR("empty bit-vector value");
61  }
62  int base;
63  if (value.size() >= 2 && value[0] == '0')
64  {
65  const char c = value[1];
66  if (c == 'b')
67  {
68  base = 2;
69  }
70  else if (c == 'o')
71  {
72  base = 8;
73  }
74  else if (c == 'x')
75  {
76  base = 16;
77  }
78  else
79  {
80  return ERR("invalid base");
81  }
82  }
83  else
84  {
85  return ERR("bit-vector value '" + value + "' has no base prefix (expected 0b, 0o, or 0x)");
86  }
87  const std::string digits = value.substr(2);
88  if (digits.empty())
89  {
90  return ERR("bit-vector value '" + value + "' has no digits after the base prefix");
91  }
92  try
93  {
94  size_t consumed = 0;
95  const u64 v = std::stoull(digits, &consumed, base);
96  if (consumed != digits.size())
97  {
98  return ERR("bit-vector value '" + value + "' contains non-digit characters");
99  }
100  return OK(v);
101  }
102  catch (const std::invalid_argument&)
103  {
104  return ERR("bit-vector value '" + value + "' is not a valid number");
105  }
106  catch (const std::out_of_range&)
107  {
108  return ERR("bit-vector value '" + value + "' exceeds the 64-bit range");
109  }
110  }
111 
112  // Returns true if `c` is one of the 9 VHDL std_logic state characters.
113  // Expects canonical uppercase form: X, Z, U, L, H, W; also accepts 0, 1, -.
114  bool is_logic_state_char(char c)
115  {
116  return c == '0' || c == '1' || c == 'X' || c == 'Z' || c == 'U' || c == 'L' || c == 'H' || c == 'W' || c == '-';
117  }
118 
119  // Parse a 9-state logic-vector literal "0b<bits>", "0o<digits>", or "0x<digits>".
120  // Expects canonical form: lowercase prefix, uppercase digits and state characters.
121  // Each digit is expanded to its bits (MSB first). A state character (X, Z, U, L,
122  // H, W, -) expands to N copies of itself where N is 1, 3, or 4 depending on the base.
123  // Returns the equivalent per-bit string (characters in {`0`, `1`, `X`, `Z`, `U`,
124  // `L`, `H`, `W`, `-`}).
125  Result<std::string> parse_logic_vector(const std::string& value)
126  {
127  if (value.empty())
128  {
129  return ERR("empty logic-vector value");
130  }
131  if (value.size() < 2 || value[0] != '0')
132  {
133  return ERR("logic-vector value '" + value + "' has no base prefix");
134  }
135  const char base_char = value[1];
136  int bits_per_digit;
137  std::string numeric_set;
138  switch (base_char)
139  {
140  case 'b':
141  bits_per_digit = 1;
142  numeric_set = "01";
143  break;
144  case 'o':
145  bits_per_digit = 3;
146  numeric_set = "01234567";
147  break;
148  case 'x':
149  bits_per_digit = 4;
150  numeric_set = "0123456789ABCDEF";
151  break;
152  default:
153  return ERR("logic-vector value '" + value + "' has invalid base");
154  }
155  const std::string digits = value.substr(2);
156  if (digits.empty())
157  {
158  return ERR("logic-vector value '" + value + "' has no digits after the base prefix");
159  }
160  std::string out;
161  out.reserve(digits.size() * bits_per_digit);
162  for (const char c : digits)
163  {
164  const bool is_numeric = numeric_set.find(c) != std::string::npos;
165  const bool is_state = is_logic_state_char(c) && c != '0' && c != '1';
166  if (!is_numeric && !is_state)
167  {
168  return ERR("logic-vector value '" + value + "' contains invalid digit '" + std::string(1, c) + "'");
169  }
170  if (is_state)
171  {
172  out.append(static_cast<size_t>(bits_per_digit), c);
173  }
174  else
175  {
176  const int n = (c >= 'A') ? (c - 'A' + 10) : (c - '0');
177  for (int i = bits_per_digit - 1; i >= 0; --i)
178  {
179  out += ((n >> i) & 1) ? '1' : '0';
180  }
181  }
182  }
183  return OK(out);
184  }
185 
186  // Parse a base-10 signed integer (optional leading +/-).
187  Result<i64> parse_integer(const std::string& value)
188  {
189  if (value.empty())
190  {
191  return ERR("empty integer value");
192  }
193  try
194  {
195  size_t consumed = 0;
196  const i64 v = std::stoll(value, &consumed, 10);
197  if (consumed != value.size())
198  {
199  return ERR("integer value '" + value + "' contains trailing characters");
200  }
201  return OK(v);
202  }
203  catch (const std::invalid_argument&)
204  {
205  return ERR("integer value '" + value + "' is not a valid number");
206  }
207  catch (const std::out_of_range&)
208  {
209  return ERR("integer value '" + value + "' exceeds the 64-bit signed range");
210  }
211  }
212 
213  // Parse a floating-point value (decimal, with optional sign and exponent).
214  Result<double> parse_float(const std::string& value)
215  {
216  if (value.empty())
217  {
218  return ERR("empty float value");
219  }
220  try
221  {
222  size_t consumed = 0;
223  const double v = std::stod(value, &consumed);
224  if (consumed != value.size())
225  {
226  return ERR("float value '" + value + "' contains trailing characters");
227  }
228  return OK(v);
229  }
230  catch (const std::invalid_argument&)
231  {
232  return ERR("float value '" + value + "' is not a valid number");
233  }
234  catch (const std::out_of_range&)
235  {
236  return ERR("float value '" + value + "' exceeds the double range");
237  }
238  }
239 
240  // Parse a time value of the form ``<number><unit>`` with the unit in
241  // {fs, ps, ns, us, ms, s, min, h}. The number is a non-negative decimal
242  // (no scientific notation; whitespace between the number and unit is rejected).
243  Result<std::monostate> parse_time(const std::string& value)
244  {
245  if (value.empty())
246  {
247  return ERR("empty time value");
248  }
249  size_t i = 0;
250  while (i < value.size() && (std::isdigit(static_cast<unsigned char>(value[i])) || value[i] == '.'))
251  {
252  ++i;
253  }
254  if (i == 0)
255  {
256  return ERR("time value '" + value + "' has no numeric prefix");
257  }
258  if (i == value.size())
259  {
260  return ERR("time value '" + value + "' is missing a unit suffix");
261  }
262  const std::string num_str = value.substr(0, i);
263  const std::string unit = value.substr(i);
264  static const std::vector<std::string> valid_units = {"fs", "ps", "ns", "us", "ms", "s", "min", "h"};
265  if (std::find(valid_units.begin(), valid_units.end(), unit) == valid_units.end())
266  {
267  return ERR("time value '" + value + "' has invalid unit '" + unit + "'");
268  }
269  try
270  {
271  size_t consumed = 0;
272  (void)std::stod(num_str, &consumed);
273  if (consumed != num_str.size())
274  {
275  return ERR("time value '" + value + "' has non-numeric prefix '" + num_str + "'");
276  }
277  }
278  catch (const std::invalid_argument&)
279  {
280  return ERR("time value '" + value + "' has invalid numeric prefix '" + num_str + "'");
281  }
282  catch (const std::out_of_range&)
283  {
284  return ERR("time value '" + value + "' has numeric prefix '" + num_str + "' out of range");
285  }
286  return OK({});
287  }
288  } // namespace
289 
291  {
292  }
293 
294  const std::string& Parameter::get_name() const
295  {
296  return name;
297  }
298 
300  {
301  return type;
302  }
303 
305  {
306  return size;
307  }
308 
309  const std::string& Parameter::get_default_value() const
310  {
311  return default_value;
312  }
313 
314  const std::vector<std::string>& Parameter::get_enum_values() const
315  {
316  return enum_values;
317  }
318 
319  Result<Parameter> Parameter::Boolean(const std::string& name, const std::string& default_value)
320  {
321  if (name.empty())
322  {
323  return ERR("could not create parameter of type 'Boolean': parameter name is empty");
324  }
325 
326  auto param = Parameter();
327  param.type = Parameter::Type::Boolean;
328  param.name = name;
329  param.size = 1;
330  param.default_value = default_value;
331 
332  if (!default_value.empty() && !param.validate(default_value))
333  {
334  return ERR("could not create parameter with name '" + name + "' of type 'Boolean': default value '" + default_value + "' is invalid");
335  }
336 
337  return OK(param);
338  }
339 
340  Result<Parameter> Parameter::BitVector(const std::string& name, u16 size, const std::string& default_value)
341  {
342  if (name.empty())
343  {
344  return ERR("could not create parameter of type 'BitVector': parameter name is empty");
345  }
346  if (size == 0)
347  {
348  return ERR("could not create parameter with name '" + name + "' of type 'BitVector': size must be at least 1");
349  }
350 
351  auto param = Parameter();
352  param.type = Parameter::Type::BitVector;
353  param.name = name;
354  param.size = size;
355  param.default_value = normalize_value(Type::BitVector, default_value);
356 
357  if (!default_value.empty() && !param.validate(param.default_value))
358  {
359  return ERR("could not create parameter with name '" + name + "' of type 'BitVector': default value '" + param.default_value + "' is invalid");
360  }
361 
362  return OK(param);
363  }
364 
365  Result<Parameter> Parameter::LogicVector(const std::string& name, u16 size, const std::string& default_value)
366  {
367  if (name.empty())
368  {
369  return ERR("could not create parameter of type 'LogicVector': parameter name is empty");
370  }
371  if (size == 0)
372  {
373  return ERR("could not create parameter with name '" + name + "' of type 'LogicVector': size must be at least 1");
374  }
375 
376  auto param = Parameter();
377  param.type = Parameter::Type::LogicVector;
378  param.name = name;
379  param.size = size;
380  param.default_value = normalize_value(Type::LogicVector, default_value);
381 
382  if (!default_value.empty() && !param.validate(param.default_value))
383  {
384  return ERR("could not create parameter with name '" + name + "' of type 'LogicVector': default value '" + param.default_value + "' is invalid");
385  }
386 
387  return OK(param);
388  }
389 
390  Result<Parameter> Parameter::Integer(const std::string& name, const std::string& default_value)
391  {
392  if (name.empty())
393  {
394  return ERR("could not create parameter of type 'Integer': parameter name is empty");
395  }
396 
397  auto param = Parameter();
398  param.type = Parameter::Type::Integer;
399  param.name = name;
400  param.size = 64;
401  param.default_value = default_value;
402 
403  if (!default_value.empty() && !param.validate(default_value))
404  {
405  return ERR("could not create parameter with name '" + name + "' of type 'Integer': default value '" + default_value + "' is invalid");
406  }
407 
408  return OK(param);
409  }
410 
411  Result<Parameter> Parameter::String(const std::string& name, const std::string& default_value)
412  {
413  if (name.empty())
414  {
415  return ERR("could not create parameter of type 'String': parameter name is empty");
416  }
417 
418  auto param = Parameter();
419  param.type = Parameter::Type::String;
420  param.name = name;
421  param.size = 0;
422  param.default_value = default_value;
423 
424  return OK(param);
425  }
426 
427  Result<Parameter> Parameter::Float(const std::string& name, const std::string& default_value)
428  {
429  if (name.empty())
430  {
431  return ERR("could not create parameter of type 'Float': parameter name is empty");
432  }
433 
434  auto param = Parameter();
435  param.type = Parameter::Type::Float;
436  param.name = name;
437  param.size = 64;
438  param.default_value = default_value;
439 
440  if (!default_value.empty() && !param.validate(default_value))
441  {
442  return ERR("could not create parameter with name '" + name + "' of type 'Float': default value '" + default_value + "' is invalid");
443  }
444 
445  return OK(param);
446  }
447 
448  Result<Parameter> Parameter::Time(const std::string& name, const std::string& default_value)
449  {
450  if (name.empty())
451  {
452  return ERR("could not create parameter of type 'Time': parameter name is empty");
453  }
454 
455  auto param = Parameter();
456  param.type = Parameter::Type::Time;
457  param.name = name;
458  param.size = 0;
459  param.default_value = default_value;
460 
461  if (!default_value.empty() && !param.validate(default_value))
462  {
463  return ERR("could not create parameter with name '" + name + "' of type 'Time': default value '" + default_value + "' is invalid");
464  }
465 
466  return OK(param);
467  }
468 
469  Result<Parameter> Parameter::Enum(const std::string& name, const std::vector<std::string>& values, const std::string& default_value)
470  {
471  if (name.empty())
472  {
473  return ERR("could not create parameter of type 'Enum': parameter name is empty");
474  }
475  if (values.size() < 2)
476  {
477  return ERR("could not create parameter with name '" + name + "' of type 'Enum': at least two enum values are required");
478  }
479  {
480  std::unordered_set<std::string> seen;
481  for (const auto& v : values)
482  {
483  if (!seen.insert(v).second)
484  {
485  return ERR("could not create parameter with name '" + name + "' of type 'Enum': duplicate enum value '" + v + "'");
486  }
487  }
488  }
489 
490  auto param = Parameter();
491  param.type = Parameter::Type::Enum;
492  param.name = name;
493  param.size = enum_bit_width(values.size());
494  param.enum_values = values;
495  param.default_value = default_value;
496 
497  if (!default_value.empty() && !param.validate(default_value))
498  {
499  return ERR("could not create parameter with name '" + name + "' of type 'Enum': default value '" + param.default_value + "' is invalid");
500  }
501 
502  return OK(param);
503  }
504 
505  bool Parameter::validate(const std::string& value) const
506  {
507  const std::string v = normalize_value(type, value);
508  switch (type)
509  {
510  case Type::Boolean: {
511  return v == "true" || v == "false";
512  }
513  case Type::BitVector: {
514  if (size <= 64)
515  {
516  auto parsed = parse_bit_vector(v);
517  if (parsed.is_error())
518  {
519  return false;
520  }
521  const u64 n = parsed.get();
522  return size == 64 || (n >> size) == 0;
523  }
524  // size > 64: validate structurally — count significant bits without
525  // converting to u64 (which would overflow for wide values).
526  if (v.size() < 3 || v[0] != '0')
527  {
528  return false;
529  }
530  const char bc = v[1]; // lowercase after normalize
531  int bpd;
532  std::string valid_chars;
533  switch (bc)
534  {
535  case 'b': bpd = 1; valid_chars = "01"; break;
536  case 'o': bpd = 3; valid_chars = "01234567"; break;
537  case 'x': bpd = 4; valid_chars = "0123456789ABCDEF"; break;
538  default: return false;
539  }
540  const std::string digits = v.substr(2);
541  if (digits.empty())
542  {
543  return false;
544  }
545  for (char c : digits)
546  {
547  if (valid_chars.find(c) == std::string::npos)
548  {
549  return false;
550  }
551  }
552  // skip leading zero digits to find the most-significant non-zero digit
553  size_t i = 0;
554  while (i < digits.size() && digits[i] == '0')
555  {
556  ++i;
557  }
558  if (i == digits.size())
559  {
560  return true; // value is zero — fits in any size
561  }
562  const char lead = digits[i]; // uppercase after normalize
563  const int lead_val = (lead >= 'A') ? (lead - 'A' + 10) : (lead - '0');
564  int lead_bits = 0;
565  while ((1 << lead_bits) <= lead_val)
566  {
567  ++lead_bits;
568  }
569  const u64 sig_bits = static_cast<u64>(lead_bits) + static_cast<u64>(digits.size() - i - 1) * static_cast<u64>(bpd);
570  return sig_bits <= size;
571  }
572  case Type::LogicVector: {
573  auto parsed = parse_logic_vector(v);
574  if (parsed.is_error())
575  {
576  return false;
577  }
578  return parsed.get().size() <= size;
579  }
580  case Type::Integer: {
581  return parse_integer(v).is_ok();
582  }
583  case Type::String: {
584  return true;
585  }
586  case Type::Float: {
587  return parse_float(v).is_ok();
588  }
589  case Type::Time: {
590  return parse_time(v).is_ok();
591  }
592  case Type::Enum: {
593  if (std::find(enum_values.begin(), enum_values.end(), v) == enum_values.end())
594  {
595  return false;
596  }
597  return true;
598  }
599  }
600  return false;
601  }
602 
603  Result<u64> Parameter::encode_as_int(const std::string& value) const
604  {
605  const std::string v = normalize_value(type, value);
606  switch (type)
607  {
608  case Type::Boolean: {
609  if (v == "false")
610  {
611  return OK(0u);
612  }
613  if (v == "true")
614  {
615  return OK(1u);
616  }
617  return ERR("value '" + value + "' is not a valid boolean");
618  }
619  case Type::BitVector: {
620  auto parsed = parse_bit_vector(v);
621  if (parsed.is_error())
622  {
623  return ERR_APPEND(parsed.get_error(), "value '" + value + "' is not a valid bit-vector");
624  }
625  const u64 n = parsed.get();
626  if (size < 64 && (n >> size) != 0)
627  {
628  return ERR("value '" + value + "' does not fit in " + std::to_string(size) + " bits");
629  }
630  return OK(n);
631  }
632  case Type::Integer: {
633  auto parsed = parse_integer(v);
634  if (parsed.is_error())
635  {
636  return ERR_APPEND(parsed.get_error(), "value '" + value + "' is not a valid integer");
637  }
638  return OK(static_cast<u64>(parsed.get()));
639  }
640  case Type::LogicVector:
641  case Type::String:
642  case Type::Float:
643  case Type::Time: {
644  return ERR("parameter of type '" + enum_to_string<Parameter::Type>(type) + "' has no canonical integer encoding");
645  }
646  case Type::Enum: {
647  for (size_t i = 0; i < enum_values.size(); ++i)
648  {
649  if (enum_values[i] == value)
650  {
651  return OK(static_cast<u64>(i));
652  }
653  }
654  return ERR("value '" + value + "' is not a declared enum value");
655  }
656  }
657  return ERR("unknown parameter type");
658  }
659 
660  bool Parameter::operator==(const Parameter& other) const
661  {
662  return name == other.name && type == other.type && size == other.size && default_value == other.default_value && enum_values == other.enum_values;
663  }
664 
665  bool Parameter::operator!=(const Parameter& other) const
666  {
667  return !(*this == other);
668  }
669 } // namespace hal
u32 size
uint64_t u64
Definition: defines.h:42
uint16_t u16
Definition: defines.h:40
int64_t i64
Definition: defines.h:37
#define ERR(message)
Definition: result.h:60
#define OK(...)
Definition: result.h:56
#define ERR_APPEND(prev_error, message)
Definition: result.h:64
Definition: defines.h:45
PinType type
std::string name
static Result< Parameter > Boolean(const std::string &name, const std::string &default_value)
Definition: parameter.cpp:319
bool operator!=(const Parameter &other) const
Definition: parameter.cpp:665
static Result< Parameter > BitVector(const std::string &name, u16 size, const std::string &default_value)
Definition: parameter.cpp:340
static Result< Parameter > Integer(const std::string &name, const std::string &default_value)
Definition: parameter.cpp:390
bool operator==(const Parameter &other) const
Definition: parameter.cpp:660
static Result< Parameter > String(const std::string &name, const std::string &default_value)
Definition: parameter.cpp:411
u16 get_size() const
Definition: parameter.cpp:304
static Result< Parameter > LogicVector(const std::string &name, u16 size, const std::string &default_value)
Definition: parameter.cpp:365
Type get_type() const
Definition: parameter.cpp:299
const std::string & get_name() const
Definition: parameter.cpp:294
const std::vector< std::string > & get_enum_values() const
Definition: parameter.cpp:314
static Result< Parameter > Time(const std::string &name, const std::string &default_value)
Definition: parameter.cpp:448
static Result< Parameter > Enum(const std::string &name, const std::vector< std::string > &values, const std::string &default_value)
Definition: parameter.cpp:469
bool validate(const std::string &value) const
Definition: parameter.cpp:505
static Result< Parameter > Float(const std::string &name, const std::string &default_value)
Definition: parameter.cpp:427
Result< u64 > encode_as_int(const std::string &value) const
Definition: parameter.cpp:603
const std::string & get_default_value() const
Definition: parameter.cpp:309