OpenFPGA/libs/EXTERNAL/libsdcparse/src/sdc_common.cpp

687 lines
25 KiB
C++

#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <cassert>
#include <cmath>
#include <memory>
#include "sdc_common.hpp"
#include "sdc_error.hpp"
namespace sdcparse {
/*
* Functions for create_clock
*/
void sdc_create_clock_set_period(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock, double period) {
if(!std::isnan(sdc_create_clock.period)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only define a single clock period.\n");
} else {
sdc_create_clock.period = period;
}
}
void sdc_create_clock_set_name(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock, const std::string& name) {
if(!sdc_create_clock.name.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only define a single clock name.\n");
} else {
sdc_create_clock.name = name;
}
}
void sdc_create_clock_set_waveform(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock, double rise_edge, double fall_edge) {
if(!std::isnan(sdc_create_clock.rise_edge) || !std::isnan(sdc_create_clock.fall_edge)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only define a single waveform.\n");
} else {
sdc_create_clock.rise_edge = rise_edge;
sdc_create_clock.fall_edge = fall_edge;
}
}
void sdc_create_clock_add_targets(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock, StringGroup target_group) {
assert(target_group.type == StringGroupType::STRING);
if(!sdc_create_clock.targets.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only define a single set of targets for clock creation. "
"If you want to define multiple targets specify them as a list (e.g. \"{target1 target2}\").\n");
}
sdc_create_clock.targets = target_group;
}
void add_sdc_create_clock(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock) {
/*
* Error Checking
*/
//Must have a clock period
if(std::isnan(sdc_create_clock.period)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must define clock period.\n");
}
//Must have either a target (if a netlist clock), or a name (if a virtual clock)
if(sdc_create_clock.targets.strings.size() == 0 && sdc_create_clock.name.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must define either a target (for netlist clock) or a name (for virtual clock).\n");
}
//Currently we do not support defining clock names that differ from the netlist target name
if(sdc_create_clock.targets.strings.size() != 0 && !sdc_create_clock.name.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Currently custom names for netlist clocks are unsupported, remove '-name' option or create a virtual clock.\n");
}
/*
* Set defaults
*/
//Determine default rise/fall time for waveform
if(std::isnan(sdc_create_clock.rise_edge) && std::isnan(sdc_create_clock.fall_edge)) {
sdc_create_clock.rise_edge = 0.0;
sdc_create_clock.fall_edge = sdc_create_clock.period / 2;
}
assert(!std::isnan(sdc_create_clock.rise_edge));
assert(!std::isnan(sdc_create_clock.fall_edge));
//Determine if clock is virtual or not
if(sdc_create_clock.targets.strings.size() == 0 && !sdc_create_clock.name.empty()) {
//Have a virtual target if there is a name AND no target strings
sdc_create_clock.is_virtual = true;
} else {
assert(sdc_create_clock.targets.strings.size() > 0);
//Have a real target so this is not a virtual clock
sdc_create_clock.is_virtual = false;
}
/*
* Add command
*/
callback.create_clock(sdc_create_clock);
}
/*
* Functions for set_input_delay/set_output_delay
*/
void sdc_set_io_delay_set_clock(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay, const std::string& clock_name) {
if(!sdc_set_io_delay.clock_name.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only specify a single clock\n");
}
sdc_set_io_delay.clock_name = clock_name;
}
void sdc_set_io_delay_set_value(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay, double value) {
if(!std::isnan(sdc_set_io_delay.delay)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Delay value can only specified once.\n");
}
sdc_set_io_delay.delay = value;
}
void sdc_set_io_delay_set_max(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay) {
if (sdc_set_io_delay.is_max) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-max can only be specified once.\n");
}
sdc_set_io_delay.is_max = true;
}
void sdc_set_io_delay_set_min(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay) {
if (sdc_set_io_delay.is_min) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-min can only be specified once.\n");
}
sdc_set_io_delay.is_min = true;
}
void sdc_set_io_delay_set_ports(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay, StringGroup ports) {
assert(ports.type == StringGroupType::PORT);
if(!sdc_set_io_delay.target_ports.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Currently only a single get_ports command is supported.\n");
}
sdc_set_io_delay.target_ports = ports;
}
void add_sdc_set_io_delay(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay) {
/*
* Error checks
*/
if(sdc_set_io_delay.clock_name.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify clock name.\n");
}
if(std::isnan(sdc_set_io_delay.delay)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify delay value.\n");
}
if(sdc_set_io_delay.target_ports.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify target ports using get_ports.\n");
}
/*
* Add command
*/
callback.set_io_delay(sdc_set_io_delay);
}
/*
* Functions for set_clock_groups
*/
void sdc_set_clock_groups_set_type(Callback& callback, const Lexer& lexer, SetClockGroups& sdc_set_clock_groups, ClockGroupsType type) {
if(sdc_set_clock_groups.type != ClockGroupsType::NONE) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only specify a single clock groups relation type (e.g. '-exclusive')\n");
}
sdc_set_clock_groups.type = type;
}
void sdc_set_clock_groups_add_group(Callback& /*callback*/, const Lexer& /*lexer*/, SetClockGroups& sdc_set_clock_groups, StringGroup clock_group) {
assert(clock_group.type == StringGroupType::CLOCK || clock_group.type == StringGroupType::STRING);
//Duplicate and insert the clock group
sdc_set_clock_groups.clock_groups.push_back(clock_group);
}
void add_sdc_set_clock_groups(Callback& callback, const Lexer& lexer, SetClockGroups& sdc_set_clock_groups) {
/*
* Error checks
*/
if(sdc_set_clock_groups.type == ClockGroupsType::NONE) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify clock relation type as '-exclusive'.\n");
}
if(sdc_set_clock_groups.clock_groups.size() < 2) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify at least 2 clock groups.\n");
}
/*
* Add command
*/
callback.set_clock_groups(sdc_set_clock_groups);
}
/*
* Functions for set_false_path
*/
void sdc_set_false_path_add_to_from_group(Callback& callback, const Lexer& lexer,
SetFalsePath& sdc_set_false_path,
StringGroup group,
FromToType to_from_dir) {
assert(group.type == StringGroupType::CLOCK || group.type == StringGroupType::STRING);
//Error checking
if(to_from_dir == FromToType::FROM) {
//Check that we haven't already defined the from path
if(!sdc_set_false_path.from.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n");
}
} else {
assert(to_from_dir == FromToType::TO);
//Check that we haven't already defined the from path
if(!sdc_set_false_path.to.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n");
}
}
//Add the clock group
if(to_from_dir == FromToType::FROM) {
sdc_set_false_path.from = group;
} else {
assert(to_from_dir == FromToType::TO);
sdc_set_false_path.to = group;
}
}
void add_sdc_set_false_path(Callback& callback, const Lexer& /*lexer*/, SetFalsePath& sdc_set_false_path) {
/*
* Error checks
*/
/*
* Add command
*/
callback.set_false_path(sdc_set_false_path);
}
/*
* Functions for set_max_delay/set_min_delay
*/
void sdc_set_min_max_delay_set_value(Callback& callback, const Lexer& lexer, SetMinMaxDelay& sdc_set_min_max_delay, double min_max_delay) {
if(!std::isnan(sdc_set_min_max_delay.value)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify max delay value only once.\n");
}
sdc_set_min_max_delay.value = min_max_delay;
}
void sdc_set_min_max_delay_add_to_from_group(Callback& callback, const Lexer& lexer, SetMinMaxDelay& sdc_set_min_max_delay, StringGroup group, FromToType to_from_dir) {
assert(group.type == StringGroupType::CLOCK || group.type == StringGroupType::STRING);
//Error checking
if(to_from_dir == FromToType::FROM) {
//Check that we haven't already defined the from path
if(!sdc_set_min_max_delay.from.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n");
}
} else {
assert(to_from_dir == FromToType::TO);
//Check that we haven't already defined the from path
if(!sdc_set_min_max_delay.to.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n");
}
}
//Add the clock group
if(to_from_dir == FromToType::FROM) {
sdc_set_min_max_delay.from = group;
} else {
assert(to_from_dir == FromToType::TO);
sdc_set_min_max_delay.to = group;
}
}
void add_sdc_set_min_max_delay(Callback& callback, const Lexer& lexer, SetMinMaxDelay& sdc_set_min_max_delay) {
/*
* Error checks
*/
if(std::isnan(sdc_set_min_max_delay.value)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the max delay value.\n");
}
/*
* Add command
*/
callback.set_min_max_delay(sdc_set_min_max_delay);
}
/*
* Functions for set_multicycle_path
*/
void sdc_set_multicycle_path_set_setup(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path) {
if(sdc_set_multicycle_path.is_setup) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "'-setup' should only be specified once.\n");
}
sdc_set_multicycle_path.is_setup = true;
}
void sdc_set_multicycle_path_set_hold(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path) {
if(sdc_set_multicycle_path.is_hold) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "'-hold' should only be specified once.\n");
}
sdc_set_multicycle_path.is_hold = true;
}
void sdc_set_multicycle_path_set_mcp_value(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path, int mcp_value) {
if(sdc_set_multicycle_path.mcp_value != UNINITIALIZED_INT) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify multicycle path value only once.\n");
}
sdc_set_multicycle_path.mcp_value = mcp_value;
}
void sdc_set_multicycle_path_add_to_from_group(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path,
StringGroup group,
FromToType to_from_dir) {
assert(group.type == StringGroupType::CLOCK || group.type == StringGroupType::STRING || group.type == StringGroupType::PIN);
//Error checking
if(to_from_dir == FromToType::FROM) {
//Check that we haven't already defined the from path
if(!sdc_set_multicycle_path.from.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n");
}
} else {
assert(to_from_dir == FromToType::TO);
//Check that we haven't already defined the from path
if(!sdc_set_multicycle_path.to.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n");
}
}
//Add the clock group
if(to_from_dir == FromToType::FROM) {
sdc_set_multicycle_path.from = group;
} else {
assert(to_from_dir == FromToType::TO);
sdc_set_multicycle_path.to = group;
}
}
void add_sdc_set_multicycle_path(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path) {
/*
* Error checks
*/
if(sdc_set_multicycle_path.mcp_value == UNINITIALIZED_INT) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the multicycle path value.\n");
}
/*
* Add command
*/
callback.set_multicycle_path(sdc_set_multicycle_path);
}
/*
* Functions for set_clock_uncertainty
*/
void sdc_set_clock_uncertainty_set_setup(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty) {
if(sdc_set_clock_uncertainty.is_setup) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "'-setup' should only be specified once.\n");
}
sdc_set_clock_uncertainty.is_setup = true;
}
void sdc_set_clock_uncertainty_set_hold(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty) {
if(sdc_set_clock_uncertainty.is_hold) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "'-hold' should only be specified once.\n");
}
sdc_set_clock_uncertainty.is_hold = true;
}
void sdc_set_clock_uncertainty_set_value(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty, float value) {
if(!std::isnan(sdc_set_clock_uncertainty.value)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify clock uncertainty value only once.\n");
}
sdc_set_clock_uncertainty.value = value;
}
void sdc_set_clock_uncertainty_add_to_from_group(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty,
StringGroup group,
FromToType to_from_dir) {
assert(group.type == StringGroupType::CLOCK || group.type == StringGroupType::STRING);
//Error checking
if(to_from_dir == FromToType::FROM) {
//Check that we haven't already defined the from path
if(!sdc_set_clock_uncertainty.from.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n");
}
} else {
assert(to_from_dir == FromToType::TO);
//Check that we haven't already defined the from path
if(!sdc_set_clock_uncertainty.to.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n");
}
}
//Add the clock group
if(to_from_dir == FromToType::FROM) {
sdc_set_clock_uncertainty.from = group;
} else {
assert(to_from_dir == FromToType::TO);
sdc_set_clock_uncertainty.to = group;
}
}
void add_sdc_set_clock_uncertainty(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty) {
/*
* Error checks
*/
if(std::isnan(sdc_set_clock_uncertainty.value)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the clock uncertainty value.\n");
}
/*
* Add command
*/
callback.set_clock_uncertainty(sdc_set_clock_uncertainty);
}
/*
* Functions for set_clock_latency
*/
void sdc_set_clock_latency_set_type(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency, ClockLatencyType type) {
//Error checking
if(sdc_set_clock_latency.type != ClockLatencyType::NONE) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "The latency type (e.g. '-source') can only be specified once.\n");
}
sdc_set_clock_latency.type = type;
}
void sdc_set_clock_latency_early(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency) {
//Error checking
if(sdc_set_clock_latency.is_early) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "The '-early' option can only be specified once.\n");
}
sdc_set_clock_latency.is_early = true;
}
void sdc_set_clock_latency_late(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency) {
//Error checking
if(sdc_set_clock_latency.is_late) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "The '-late' option can only be specified once.\n");
}
sdc_set_clock_latency.is_late = true;
}
void sdc_set_clock_latency_set_value(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency, float value) {
if(!std::isnan(sdc_set_clock_latency.value)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "The clock latency value can only be specified once.\n");
}
sdc_set_clock_latency.value = value;
}
void sdc_set_clock_latency_set_clocks(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency, StringGroup target_clocks) {
if(target_clocks.type != StringGroupType::CLOCK) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Target clocks must be specified with 'get_clocks'.\n");
}
sdc_set_clock_latency.target_clocks = target_clocks;
}
void add_sdc_set_clock_latency(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency) {
/*
* Error checks
*/
if(sdc_set_clock_latency.type != ClockLatencyType::SOURCE) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the clock latency type (e.g. '-source').\n");
}
if(std::isnan(sdc_set_clock_latency.value)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the clock latency value.\n");
}
if(sdc_set_clock_latency.target_clocks.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify target clocks.\n");
}
/*
* Add command
*/
callback.set_clock_latency(sdc_set_clock_latency);
}
/*
* Functions for set_disable_timing
*/
void sdc_set_disable_timing_add_to_from_group(Callback& callback, const Lexer& lexer,
SetDisableTiming& sdc_set_disable_timing,
StringGroup group,
FromToType to_from_dir) {
//Error checking
if (group.type != StringGroupType::PIN && group.type != StringGroupType::STRING) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only get_pins supported with set_disable_timing.\n");
}
if(to_from_dir == FromToType::FROM) {
//Check that we haven't already defined the from path
if(!sdc_set_disable_timing.from.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n");
}
} else {
assert(to_from_dir == FromToType::TO);
//Check that we haven't already defined the from path
if(!sdc_set_disable_timing.to.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n");
}
}
//Add the clock group
if(to_from_dir == FromToType::FROM) {
sdc_set_disable_timing.from = group;
} else {
assert(to_from_dir == FromToType::TO);
sdc_set_disable_timing.to = group;
}
}
void add_sdc_set_disable_timing(Callback& callback, const Lexer& /*lexer*/, SetDisableTiming& sdc_set_disable_timing) {
/*
* Error checks
*/
/*
* Add command
*/
callback.set_disable_timing(sdc_set_disable_timing);
}
/*
* Functions for set_timing_derate
*/
void sdc_set_timing_derate_early(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate) {
if(sdc_set_timing_derate.is_early) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-early should only be specified once.\n");
}
sdc_set_timing_derate.is_early = true;
}
void sdc_set_timing_derate_late(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate) {
if(sdc_set_timing_derate.is_late) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-late should only be specified once.\n");
}
sdc_set_timing_derate.is_late = true;
}
void sdc_set_timing_derate_target_type(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate, TimingDerateTargetType target_type) {
if(target_type == TimingDerateTargetType::NET) {
if(sdc_set_timing_derate.derate_nets) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-net_delay should only be specified once.\n");
} else {
sdc_set_timing_derate.derate_nets = true;
}
} else if (target_type == TimingDerateTargetType::CELL) {
if(sdc_set_timing_derate.derate_cells) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-cell_delay should only be specified once.\n");
} else {
sdc_set_timing_derate.derate_cells = true;
}
}
}
void sdc_set_timing_derate_value(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate, float value) {
if(!std::isnan(sdc_set_timing_derate.value)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single derate value per set_timing_derate command is allowed.\n");
}
if(sdc_set_timing_derate.value < 0) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Timing derate values must be positive.\n");
}
sdc_set_timing_derate.value = value;
}
void sdc_set_timing_derate_targets(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate, StringGroup targets) {
if(targets.type != StringGroupType::CELL) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only get_cells is supported with set_timing_derate.\n");
}
if(!sdc_set_timing_derate.cell_targets.strings.empty()) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single get_cells call is supported with set_timing_derate.\n");
}
sdc_set_timing_derate.cell_targets = targets;
}
void add_sdc_set_timing_derate(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate) {
/*
* Error checks
*/
if(std::isnan(sdc_set_timing_derate.value)) {
sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify timing derate value\n");
}
/*
* Defaults
*/
if(!sdc_set_timing_derate.derate_nets && !sdc_set_timing_derate.derate_cells) {
//If unspecified, both cells and nets are derated
sdc_set_timing_derate.derate_nets = true;
sdc_set_timing_derate.derate_cells = true;
}
/*
* Add command
*/
callback.set_timing_derate(sdc_set_timing_derate);
}
/*
* Functions for string_group
*/
StringGroup make_sdc_string_group(StringGroupType type, const std::string& string) {
//Convenience function for converting a single string into a group
StringGroup sdc_string_group(type);
sdc_string_group.strings.push_back(string);
return sdc_string_group;
}
void sdc_string_group_add_string(StringGroup& sdc_string_group, const std::string& string) {
//Insert the new string
sdc_string_group.strings.push_back(string);
}
void sdc_string_group_add_strings(StringGroup& sdc_string_group, const StringGroup& string_group_to_add) {
for(const auto& string : string_group_to_add.strings) {
sdc_string_group_add_string(sdc_string_group, string);
}
}
char* strdup(const char* src) {
if(src == NULL) {
return NULL;
}
size_t len = std::strlen(src); //Number of char's excluding null terminator
//Space for [0..len] chars
char* new_str = (char*) std::malloc((len+1)*sizeof(*src));
assert(new_str != NULL);
//Copy chars from [0..len], i.e. src[len] should be null terminator
std::memcpy(new_str, src, len+1);
return new_str;
}
char* strndup(const char* src, size_t len) {
if(src == NULL) {
return NULL;
}
//Space for [0..len] chars
char* new_str = (char*) std::malloc((len+1)*sizeof(*src));
assert(new_str != NULL);
//Copy chars from [0..len-1]
std::memcpy(new_str, src, len);
//Add the null terminator
new_str[len] = '\0';
return new_str;
}
} //namespace