Change C APIs to a unified string implementation (#6299)

Currently in the entire C API of WPILib we have ~8 different ways of handling strings. The C API actually isn't built for pure C callers (We don't actually have any of those). Instead, they're built for interop between languages like LabVIEW and C# which can talk to C API's directly.

For output parameters, the choice was fairly obvious. An output struct containing a const string pointer and a length makes the most sense. Its easy to use these from most other languages, and doesn't require special null termination handling. Freeing these is also easy, as if you ever receive one of these string structures, theres just a single function call to free it.

Input parameters are a bit more complex. To be used from pure C, and from LabVIEW, a null terminated string is the best in most cases. However, null terminated strings in general have a lot of downsides. Additionally, from LabVIEW there are other considerations around encoding that having a wrapper struct helps make a bit easier. From a language like C#, a wrapper struct is by far the easiest, as custom marshalling can make it trivial to marshal both UTF8 and UTF16 strings down.

The final consideration is its nice to have an identical concept for both input and output. It makes the rules fairly easy to understand.

WPILib will not have any APIs that manipulate a string allocated externally. This means WPI_String can be const, as across the boundary it is always const.
If a WPILib API takes a const WPI_String*, WPILib will not manipulate or attempt to free that string, and that string is treated as an input. It is up to the caller to handle that memory, WPILib will never hold onto that memory longer than the call.
If a WPILib API takes a WPI_String*, that string is an output. WPILib will allocate that API with WPI_AllocateString(), fill in the string, and return to the caller. When the caller is done with the string, they must free it with WPI_FreeString().
If an output struct contains a WPI_String member, that member is considered read only, and should not be explicitly freed. The caller should call the free function for that struct.
If an array of WPI_Strings are returned, each individual string is considered read only, and should not be explicitly freed. The free function for that array should be called by the caller.
If an input struct containing a WPI_String, or an input array of WPI_Strings is passed to WPILib, the individual strings will not be manipulated or freed by WPILib, and the caller owns and should free that memory.
Callbacks also follow these rules. The most common is a callback either getting passed a const WPI_String* or a struct containing a WPI_String. In both of these cases, the callback target should consider these strings read only, and not attempt to free them or manipulate them.
This commit is contained in:
Thad House
2024-05-13 05:35:14 -07:00
committed by GitHub
parent 178fe99f12
commit 4ce8f3f935
60 changed files with 990 additions and 914 deletions

View File

@@ -32,7 +32,7 @@ struct StringArrayStorage {
size_t EstimateSize() const {
return sizeof(StringArrayStorage) +
strings.capacity() * sizeof(std::string) +
ntStrings.capacity() * sizeof(NT_String) +
ntStrings.capacity() * sizeof(WPI_String) +
std::accumulate(strings.begin(), strings.end(), 0,
[](const auto& sum, const auto& val) {
return sum + val.capacity();
@@ -40,7 +40,7 @@ struct StringArrayStorage {
}
std::vector<std::string> strings;
std::vector<NT_String> ntStrings;
std::vector<WPI_String> ntStrings;
};
template <typename T>
@@ -58,11 +58,11 @@ inline std::shared_ptr<T[]> AllocateArray(size_t nelem) {
} // namespace
void StringArrayStorage::InitNtStrings() {
// point NT_String's to the contents in the vector.
// point WPI_String's to the contents in the vector.
ntStrings.reserve(strings.size());
for (const auto& str : strings) {
ntStrings.emplace_back(
NT_String{const_cast<char*>(str.c_str()), str.size()});
WPI_String{const_cast<char*>(str.c_str()), str.size()});
}
}
@@ -250,8 +250,7 @@ void nt::ConvertToC(const Value& in, NT_Value* out) {
}
case NT_STRING_ARRAY: {
auto v = in.GetStringArray();
out->data.arr_string.arr = static_cast<NT_String*>(
wpi::safe_malloc(v.size() * sizeof(NT_String)));
out->data.arr_string.arr = WPI_AllocateStringArray(v.size());
for (size_t i = 0; i < v.size(); ++i) {
ConvertToC(std::string_view{v[i]}, &out->data.arr_string.arr[i]);
}
@@ -263,18 +262,14 @@ void nt::ConvertToC(const Value& in, NT_Value* out) {
}
}
size_t nt::ConvertToC(std::string_view in, char** out) {
*out = static_cast<char*>(wpi::safe_malloc(in.size() + 1));
std::memmove(*out, in.data(), in.size()); // NOLINT
(*out)[in.size()] = '\0';
return in.size();
}
void nt::ConvertToC(std::string_view in, NT_String* out) {
out->len = in.size();
out->str = static_cast<char*>(wpi::safe_malloc(in.size() + 1));
std::memcpy(out->str, in.data(), in.size());
out->str[in.size()] = '\0';
void nt::ConvertToC(std::string_view in, WPI_String* out) {
if (in.empty()) {
out->len = 0;
out->str = nullptr;
return;
}
auto write = WPI_AllocateString(out, in.size());
std::memcpy(write, in.data(), in.size());
}
Value nt::ConvertFromC(const NT_Value& value) {

View File

@@ -420,8 +420,8 @@ inline void ConvertToC(const T& in, T* out) {
void ConvertToC(const Value& in, NT_Value* out);
Value ConvertFromC(const NT_Value& value);
size_t ConvertToC(std::string_view in, char** out);
void ConvertToC(std::string_view in, NT_String* out);
inline std::string_view ConvertFromC(const NT_String& str) {
void ConvertToC(std::string_view in, WPI_String* out);
inline std::string_view ConvertFromC(const WPI_String& str) {
return {str.str, str.len};
}

View File

@@ -87,19 +87,19 @@ static void ConvertToC(const Event& in, NT_Event* out) {
}
static void DisposeConnectionInfo(NT_ConnectionInfo* info) {
std::free(info->remote_id.str);
std::free(info->remote_ip.str);
WPI_FreeString(&info->remote_id);
WPI_FreeString(&info->remote_ip);
}
static void DisposeTopicInfo(NT_TopicInfo* info) {
std::free(info->name.str);
std::free(info->type_str.str);
std::free(info->properties.str);
WPI_FreeString(&info->name);
WPI_FreeString(&info->type_str);
WPI_FreeString(&info->properties);
}
static void DisposeLogMessage(NT_LogMessage* msg) {
std::free(msg->filename);
std::free(msg->message);
WPI_FreeString(&msg->filename);
WPI_FreeString(&msg->message);
}
static void DisposeEvent(NT_Event* event) {
@@ -156,15 +156,12 @@ NT_Inst NT_GetInstanceFromHandle(NT_Handle handle) {
* Table Functions
*/
NT_Entry NT_GetEntry(NT_Inst inst, const char* name, size_t name_len) {
return nt::GetEntry(inst, {name, name_len});
NT_Entry NT_GetEntry(NT_Inst inst, const struct WPI_String* name) {
return nt::GetEntry(inst, wpi::to_string_view(name));
}
char* NT_GetEntryName(NT_Entry entry, size_t* name_len) {
struct NT_String v_name;
nt::ConvertToC(nt::GetEntryName(entry), &v_name);
*name_len = v_name.len;
return v_name.str;
void NT_GetEntryName(NT_Entry entry, struct WPI_String* name) {
nt::ConvertToC(nt::GetEntryName(entry), name);
}
enum NT_Type NT_GetEntryType(NT_Entry entry) {
@@ -218,41 +215,41 @@ struct NT_Value* NT_ReadQueueValueType(NT_Handle subentry, unsigned int types,
return ConvertToC<NT_Value>(nt::ReadQueueValue(subentry, types), count);
}
NT_Topic* NT_GetTopics(NT_Inst inst, const char* prefix, size_t prefix_len,
NT_Topic* NT_GetTopics(NT_Inst inst, const struct WPI_String* prefix,
unsigned int types, size_t* count) {
auto info_v = nt::GetTopics(inst, {prefix, prefix_len}, types);
auto info_v = nt::GetTopics(inst, wpi::to_string_view(prefix), types);
return ConvertToC<NT_Topic>(info_v, count);
}
NT_Topic* NT_GetTopicsStr(NT_Inst inst, const char* prefix, size_t prefix_len,
const char* const* types, size_t types_len,
NT_Topic* NT_GetTopicsStr(NT_Inst inst, const struct WPI_String* prefix,
const struct WPI_String* types, size_t types_len,
size_t* count) {
wpi::SmallVector<std::string_view, 4> typesCpp;
typesCpp.reserve(types_len);
for (size_t i = 0; i < types_len; ++i) {
typesCpp.emplace_back(types[i]);
typesCpp.emplace_back(wpi::to_string_view(&types[i]));
}
auto info_v = nt::GetTopics(inst, {prefix, prefix_len}, typesCpp);
auto info_v = nt::GetTopics(inst, wpi::to_string_view(prefix), typesCpp);
return ConvertToC<NT_Topic>(info_v, count);
}
struct NT_TopicInfo* NT_GetTopicInfos(NT_Inst inst, const char* prefix,
size_t prefix_len, unsigned int types,
size_t* count) {
auto info_v = nt::GetTopicInfo(inst, {prefix, prefix_len}, types);
struct NT_TopicInfo* NT_GetTopicInfos(NT_Inst inst,
const struct WPI_String* prefix,
unsigned int types, size_t* count) {
auto info_v = nt::GetTopicInfo(inst, wpi::to_string_view(prefix), types);
return ConvertToC<NT_TopicInfo>(info_v, count);
}
struct NT_TopicInfo* NT_GetTopicInfosStr(NT_Inst inst, const char* prefix,
size_t prefix_len,
const char* const* types,
struct NT_TopicInfo* NT_GetTopicInfosStr(NT_Inst inst,
const struct WPI_String* prefix,
const struct WPI_String* types,
size_t types_len, size_t* count) {
wpi::SmallVector<std::string_view, 4> typesCpp;
typesCpp.reserve(types_len);
for (size_t i = 0; i < types_len; ++i) {
typesCpp.emplace_back(types[i]);
typesCpp.emplace_back(wpi::to_string_view(&types[i]));
}
auto info_v = nt::GetTopicInfo(inst, {prefix, prefix_len}, typesCpp);
auto info_v = nt::GetTopicInfo(inst, wpi::to_string_view(prefix), typesCpp);
return ConvertToC<NT_TopicInfo>(info_v, count);
}
@@ -265,32 +262,20 @@ NT_Bool NT_GetTopicInfo(NT_Topic topic, struct NT_TopicInfo* info) {
return true;
}
NT_Topic NT_GetTopic(NT_Inst inst, const char* name, size_t name_len) {
return nt::GetTopic(inst, std::string_view{name, name_len});
NT_Topic NT_GetTopic(NT_Inst inst, const struct WPI_String* name) {
return nt::GetTopic(inst, wpi::to_string_view(name));
}
char* NT_GetTopicName(NT_Topic topic, size_t* name_len) {
auto name = nt::GetTopicName(topic);
if (name.empty()) {
*name_len = 0;
return nullptr;
}
struct NT_String v_name;
nt::ConvertToC(name, &v_name);
*name_len = v_name.len;
return v_name.str;
void NT_GetTopicName(NT_Topic topic, struct WPI_String* name) {
nt::ConvertToC(nt::GetTopicName(topic), name);
}
NT_Type NT_GetTopicType(NT_Topic topic) {
return nt::GetTopicType(topic);
}
char* NT_GetTopicTypeString(NT_Topic topic, size_t* type_len) {
auto type = nt::GetTopicTypeString(topic);
struct NT_String v_type;
nt::ConvertToC(type, &v_type);
*type_len = v_type.len;
return v_type.str;
void NT_GetTopicTypeString(NT_Topic topic, struct WPI_String* type) {
nt::ConvertToC(nt::GetTopicTypeString(topic), type);
}
void NT_SetTopicPersistent(NT_Topic topic, NT_Bool value) {
@@ -321,87 +306,91 @@ NT_Bool NT_GetTopicExists(NT_Handle handle) {
return nt::GetTopicExists(handle);
}
char* NT_GetTopicProperty(NT_Topic topic, const char* name, size_t* len) {
wpi::json j = nt::GetTopicProperty(topic, name);
struct NT_String v;
nt::ConvertToC(j.dump(), &v);
*len = v.len;
return v.str;
void NT_GetTopicProperty(NT_Topic topic, const struct WPI_String* name,
struct WPI_String* prop) {
wpi::json j = nt::GetTopicProperty(topic, wpi::to_string_view(name));
nt::ConvertToC(j.dump(), prop);
}
NT_Bool NT_SetTopicProperty(NT_Topic topic, const char* name,
const char* value) {
NT_Bool NT_SetTopicProperty(NT_Topic topic, const struct WPI_String* name,
const struct WPI_String* value) {
wpi::json j;
try {
j = wpi::json::parse(value);
j = wpi::json::parse(wpi::to_string_view(value));
} catch (wpi::json::parse_error&) {
return false;
}
nt::SetTopicProperty(topic, name, j);
nt::SetTopicProperty(topic, wpi::to_string_view(name), j);
return true;
}
void NT_DeleteTopicProperty(NT_Topic topic, const char* name) {
nt::DeleteTopicProperty(topic, name);
void NT_DeleteTopicProperty(NT_Topic topic, const struct WPI_String* name) {
nt::DeleteTopicProperty(topic, wpi::to_string_view(name));
}
char* NT_GetTopicProperties(NT_Topic topic, size_t* len) {
void NT_GetTopicProperties(NT_Topic topic, struct WPI_String* property) {
wpi::json j = nt::GetTopicProperties(topic);
struct NT_String v;
nt::ConvertToC(j.dump(), &v);
*len = v.len;
return v.str;
nt::ConvertToC(j.dump(), property);
}
NT_Bool NT_SetTopicProperties(NT_Topic topic, const char* properties) {
NT_Bool NT_SetTopicProperties(NT_Topic topic,
const struct WPI_String* properties) {
wpi::json j;
try {
j = wpi::json::parse(properties);
j = wpi::json::parse(wpi::to_string_view(properties));
} catch (wpi::json::parse_error&) {
return false;
}
return nt::SetTopicProperties(topic, j);
}
NT_Subscriber NT_Subscribe(NT_Topic topic, NT_Type type, const char* typeStr,
NT_Subscriber NT_Subscribe(NT_Topic topic, NT_Type type,
const struct WPI_String* typeStr,
const struct NT_PubSubOptions* options) {
return nt::Subscribe(topic, type, typeStr, ConvertToCpp(options));
return nt::Subscribe(topic, type, wpi::to_string_view(typeStr),
ConvertToCpp(options));
}
void NT_Unsubscribe(NT_Subscriber sub) {
return nt::Unsubscribe(sub);
}
NT_Publisher NT_Publish(NT_Topic topic, NT_Type type, const char* typeStr,
NT_Publisher NT_Publish(NT_Topic topic, NT_Type type,
const struct WPI_String* typeStr,
const struct NT_PubSubOptions* options) {
return nt::Publish(topic, type, typeStr, ConvertToCpp(options));
return nt::Publish(topic, type, wpi::to_string_view(typeStr),
ConvertToCpp(options));
}
NT_Publisher NT_PublishEx(NT_Topic topic, NT_Type type, const char* typeStr,
const char* properties,
NT_Publisher NT_PublishEx(NT_Topic topic, NT_Type type,
const struct WPI_String* typeStr,
const struct WPI_String* properties,
const struct NT_PubSubOptions* options) {
wpi::json j;
if (properties[0] == '\0') {
if (properties->len == 0) {
// gracefully handle empty string
j = wpi::json::object();
} else {
try {
j = wpi::json::parse(properties);
j = wpi::json::parse(wpi::to_string_view(properties));
} catch (wpi::json::parse_error&) {
return {};
}
}
return nt::PublishEx(topic, type, typeStr, j, ConvertToCpp(options));
return nt::PublishEx(topic, type, wpi::to_string_view(typeStr), j,
ConvertToCpp(options));
}
void NT_Unpublish(NT_Handle pubentry) {
return nt::Unpublish(pubentry);
}
NT_Entry NT_GetEntryEx(NT_Topic topic, NT_Type type, const char* typeStr,
NT_Entry NT_GetEntryEx(NT_Topic topic, NT_Type type,
const struct WPI_String* typeStr,
const struct NT_PubSubOptions* options) {
return nt::GetEntry(topic, type, typeStr, ConvertToCpp(options));
return nt::GetEntry(topic, type, wpi::to_string_view(typeStr),
ConvertToCpp(options));
}
void NT_ReleaseEntry(NT_Entry entry) {
@@ -441,10 +430,10 @@ NT_Bool NT_WaitForListenerQueue(NT_Handle handle, double timeout) {
return nt::WaitForListenerQueue(handle, timeout);
}
NT_Listener NT_AddListenerSingle(NT_Inst inst, const char* prefix,
size_t prefix_len, unsigned int mask,
void* data, NT_ListenerCallback callback) {
std::string_view p{prefix, prefix_len};
NT_Listener NT_AddListenerSingle(NT_Inst inst, const struct WPI_String* prefix,
unsigned int mask, void* data,
NT_ListenerCallback callback) {
std::string_view p = wpi::to_string_view(prefix);
return nt::AddListener(inst, {{p}}, mask, [=](auto& event) {
NT_Event event_c;
ConvertToC(event, &event_c);
@@ -453,7 +442,8 @@ NT_Listener NT_AddListenerSingle(NT_Inst inst, const char* prefix,
});
}
NT_Listener NT_AddListenerMultiple(NT_Inst inst, const NT_String* prefixes,
NT_Listener NT_AddListenerMultiple(NT_Inst inst,
const struct WPI_String* prefixes,
size_t prefixes_len, unsigned int mask,
void* data, NT_ListenerCallback callback) {
wpi::SmallVector<std::string_view, 8> p;
@@ -480,14 +470,14 @@ NT_Listener NT_AddListener(NT_Topic topic, unsigned int mask, void* data,
}
NT_Listener NT_AddPolledListenerSingle(NT_ListenerPoller poller,
const char* prefix, size_t prefix_len,
const struct WPI_String* prefix,
unsigned int mask) {
std::string_view p{prefix, prefix_len};
std::string_view p = wpi::to_string_view(prefix);
return nt::AddPolledListener(poller, {{p}}, mask);
}
NT_Listener NT_AddPolledListenerMultiple(NT_ListenerPoller poller,
const NT_String* prefixes,
const struct WPI_String* prefixes,
size_t prefixes_len,
unsigned int mask) {
wpi::SmallVector<std::string_view, 8> p;
@@ -519,38 +509,42 @@ void NT_StopLocal(NT_Inst inst) {
nt::StopLocal(inst);
}
void NT_StartServer(NT_Inst inst, const char* persist_filename,
const char* listen_address, unsigned int port3,
void NT_StartServer(NT_Inst inst, const struct WPI_String* persist_filename,
const struct WPI_String* listen_address, unsigned int port3,
unsigned int port4) {
nt::StartServer(inst, persist_filename, listen_address, port3, port4);
nt::StartServer(inst, wpi::to_string_view(persist_filename),
wpi::to_string_view(listen_address), port3, port4);
}
void NT_StopServer(NT_Inst inst) {
nt::StopServer(inst);
}
void NT_StartClient3(NT_Inst inst, const char* identity) {
nt::StartClient3(inst, identity);
void NT_StartClient3(NT_Inst inst, const struct WPI_String* identity) {
nt::StartClient3(inst, wpi::to_string_view(identity));
}
void NT_StartClient4(NT_Inst inst, const char* identity) {
nt::StartClient4(inst, identity);
void NT_StartClient4(NT_Inst inst, const struct WPI_String* identity) {
nt::StartClient4(inst, wpi::to_string_view(identity));
}
void NT_StopClient(NT_Inst inst) {
nt::StopClient(inst);
}
void NT_SetServer(NT_Inst inst, const char* server_name, unsigned int port) {
nt::SetServer(inst, server_name, port);
void NT_SetServer(NT_Inst inst, const struct WPI_String* server_name,
unsigned int port) {
nt::SetServer(inst, wpi::to_string_view(server_name), port);
}
void NT_SetServerMulti(NT_Inst inst, size_t count, const char** server_names,
void NT_SetServerMulti(NT_Inst inst, size_t count,
const struct WPI_String* server_names,
const unsigned int* ports) {
std::vector<std::pair<std::string_view, unsigned int>> servers;
servers.reserve(count);
for (size_t i = 0; i < count; ++i) {
servers.emplace_back(std::make_pair(server_names[i], ports[i]));
servers.emplace_back(
std::make_pair(wpi::to_string_view(&server_names[i]), ports[i]));
}
nt::SetServer(inst, servers);
}
@@ -611,20 +605,22 @@ void NT_SetNow(int64_t timestamp) {
}
NT_DataLogger NT_StartEntryDataLog(NT_Inst inst, struct WPI_DataLog* log,
const char* prefix, const char* logPrefix) {
const struct WPI_String* prefix,
const struct WPI_String* logPrefix) {
return nt::StartEntryDataLog(inst, *reinterpret_cast<wpi::log::DataLog*>(log),
prefix, logPrefix);
wpi::to_string_view(prefix),
wpi::to_string_view(logPrefix));
}
void NT_StopEntryDataLog(NT_DataLogger logger) {
nt::StopEntryDataLog(logger);
}
NT_ConnectionDataLogger NT_StartConnectionDataLog(NT_Inst inst,
struct WPI_DataLog* log,
const char* name) {
return nt::StartConnectionDataLog(
inst, *reinterpret_cast<wpi::log::DataLog*>(log), name);
NT_ConnectionDataLogger NT_StartConnectionDataLog(
NT_Inst inst, struct WPI_DataLog* log, const struct WPI_String* name) {
return nt::StartConnectionDataLog(inst,
*reinterpret_cast<wpi::log::DataLog*>(log),
wpi::to_string_view(name));
}
void NT_StopConnectionDataLog(NT_ConnectionDataLogger logger) {
@@ -647,13 +643,15 @@ NT_Listener NT_AddPolledLogger(NT_ListenerPoller poller, unsigned int min_level,
return nt::AddPolledLogger(poller, min_level, max_level);
}
NT_Bool NT_HasSchema(NT_Inst inst, const char* name) {
return nt::HasSchema(inst, name);
NT_Bool NT_HasSchema(NT_Inst inst, const struct WPI_String* name) {
return nt::HasSchema(inst, wpi::to_string_view(name));
}
void NT_AddSchema(NT_Inst inst, const char* name, const char* type,
const uint8_t* schema, size_t schemaSize) {
nt::AddSchema(inst, name, type, {schema, schemaSize});
void NT_AddSchema(NT_Inst inst, const struct WPI_String* name,
const struct WPI_String* type, const uint8_t* schema,
size_t schemaSize) {
nt::AddSchema(inst, wpi::to_string_view(name), wpi::to_string_view(type),
{schema, schemaSize});
}
void NT_DisposeValue(NT_Value* value) {
@@ -665,7 +663,7 @@ void NT_DisposeValue(NT_Value* value) {
case NT_DOUBLE:
break;
case NT_STRING:
std::free(value->data.v_string.str);
WPI_FreeString(&value->data.v_string);
break;
case NT_RAW:
std::free(value->data.v_raw.data);
@@ -684,7 +682,7 @@ void NT_DisposeValue(NT_Value* value) {
break;
case NT_STRING_ARRAY: {
for (size_t i = 0; i < value->data.arr_string.size; i++) {
std::free(value->data.arr_string.arr[i].str);
WPI_FreeString(&value->data.arr_string.arr[i]);
}
std::free(value->data.arr_string.arr);
break;
@@ -703,17 +701,6 @@ void NT_InitValue(NT_Value* value) {
value->server_time = 0;
}
void NT_DisposeString(NT_String* str) {
std::free(str->str);
str->str = nullptr;
str->len = 0;
}
void NT_InitString(NT_String* str) {
str->str = nullptr;
str->len = 0;
}
void NT_DisposeValueArray(struct NT_Value* arr, size_t count) {
for (size_t i = 0; i < count; ++i) {
NT_DisposeValue(&arr[i]);
@@ -781,12 +768,6 @@ double* NT_AllocateDoubleArray(size_t size) {
return retVal;
}
struct NT_String* NT_AllocateStringArray(size_t size) {
NT_String* retVal =
static_cast<NT_String*>(wpi::safe_malloc(size * sizeof(NT_String)));
return retVal;
}
void NT_FreeCharArray(char* v_char) {
std::free(v_char);
}
@@ -802,12 +783,6 @@ void NT_FreeFloatArray(float* v_float) {
void NT_FreeDoubleArray(double* v_double) {
std::free(v_double);
}
void NT_FreeStringArray(struct NT_String* v_string, size_t arr_size) {
for (size_t i = 0; i < arr_size; ++i) {
std::free(v_string[i].str);
}
std::free(v_string);
}
enum NT_Type NT_GetValueType(const struct NT_Value* value) {
if (!value) {
@@ -938,20 +913,19 @@ double* NT_GetValueDoubleArray(const struct NT_Value* value,
return arr;
}
NT_String* NT_GetValueStringArray(const struct NT_Value* value,
uint64_t* last_change, size_t* arr_size) {
struct WPI_String* NT_GetValueStringArray(const struct NT_Value* value,
uint64_t* last_change,
size_t* arr_size) {
if (!value || value->type != NT_Type::NT_STRING_ARRAY) {
return nullptr;
}
*last_change = value->last_change;
*arr_size = value->data.arr_string.size;
NT_String* arr = static_cast<NT_String*>(
wpi::safe_malloc(value->data.arr_string.size * sizeof(NT_String)));
struct WPI_String* arr = WPI_AllocateStringArray(value->data.arr_string.size);
for (size_t i = 0; i < value->data.arr_string.size; ++i) {
size_t len = value->data.arr_string.arr[i].len;
arr[i].len = len;
arr[i].str = static_cast<char*>(wpi::safe_malloc(len + 1));
std::memcpy(arr[i].str, value->data.arr_string.arr[i].str, len + 1);
auto write = WPI_AllocateString(&arr[i], len);
std::memcpy(write, value->data.arr_string.arr[i].str, len);
}
return arr;
}

View File

@@ -630,7 +630,7 @@ void StopLocal(NT_Inst inst) {
}
void StartServer(NT_Inst inst, std::string_view persist_filename,
const char* listen_address, unsigned int port3,
std::string_view listen_address, unsigned int port3,
unsigned int port4) {
if (auto ii = InstanceImpl::GetTyped(inst, Handle::kInstance)) {
ii->StartServer(persist_filename, listen_address, port3, port4);
@@ -661,7 +661,7 @@ void StopClient(NT_Inst inst) {
}
}
void SetServer(NT_Inst inst, const char* server_name, unsigned int port) {
void SetServer(NT_Inst inst, std::string_view server_name, unsigned int port) {
SetServer(inst, {{{server_name, port}}});
}

View File

@@ -40,7 +40,7 @@ static void ConvertToC(const ClientPublisher& in,
static void ConvertToC(const ClientSubscriber& in,
NT_Meta_ClientSubscriber* out) {
out->uid = in.uid;
out->topics = ConvertToC<NT_String>(in.topics, &out->topicsCount);
out->topics = ConvertToC<WPI_String>(in.topics, &out->topicsCount);
ConvertToC(in.options, &out->options);
}
@@ -98,7 +98,7 @@ struct NT_Meta_Client* NT_Meta_DecodeClients(const uint8_t* data, size_t size,
void NT_Meta_FreeTopicPublishers(struct NT_Meta_TopicPublisher* arr,
size_t count) {
for (size_t i = 0; i < count; ++i) {
std::free(arr[i].client.str);
WPI_FreeString(&arr[i].client);
}
std::free(arr);
}
@@ -106,7 +106,7 @@ void NT_Meta_FreeTopicPublishers(struct NT_Meta_TopicPublisher* arr,
void NT_Meta_FreeTopicSubscribers(struct NT_Meta_TopicSubscriber* arr,
size_t count) {
for (size_t i = 0; i < count; ++i) {
std::free(arr[i].client.str);
WPI_FreeString(&arr[i].client);
}
std::free(arr);
}
@@ -114,7 +114,7 @@ void NT_Meta_FreeTopicSubscribers(struct NT_Meta_TopicSubscriber* arr,
void NT_Meta_FreeClientPublishers(struct NT_Meta_ClientPublisher* arr,
size_t count) {
for (size_t i = 0; i < count; ++i) {
std::free(arr[i].topic.str);
WPI_FreeString(&arr[i].topic);
}
std::free(arr);
}
@@ -122,15 +122,15 @@ void NT_Meta_FreeClientPublishers(struct NT_Meta_ClientPublisher* arr,
void NT_Meta_FreeClientSubscribers(struct NT_Meta_ClientSubscriber* arr,
size_t count) {
for (size_t i = 0; i < count; ++i) {
NT_FreeStringArray(arr[i].topics, arr[i].topicsCount);
WPI_FreeStringArray(arr[i].topics, arr[i].topicsCount);
}
std::free(arr);
}
void NT_Meta_FreeClients(struct NT_Meta_Client* arr, size_t count) {
for (size_t i = 0; i < count; ++i) {
std::free(arr[i].id.str);
std::free(arr[i].conn.str);
WPI_FreeString(&arr[i].id);
WPI_FreeString(&arr[i].conn);
}
std::free(arr);
}

View File

@@ -11,11 +11,11 @@
#include "Value_internal.h"
extern "C" {
struct NT_String* NT_GetStringForTesting(const char* str, int* struct_size) {
struct NT_String* strout =
static_cast<NT_String*>(wpi::safe_calloc(1, sizeof(NT_String)));
struct WPI_String* NT_GetStringForTesting(const char* str, int* struct_size) {
struct WPI_String* strout =
static_cast<WPI_String*>(wpi::safe_calloc(1, sizeof(WPI_String)));
nt::ConvertToC(str, strout);
*struct_size = sizeof(NT_String);
*struct_size = sizeof(WPI_String);
return strout;
}
@@ -33,8 +33,9 @@ struct NT_TopicInfo* NT_GetTopicInfoForTesting(const char* name,
}
void NT_FreeTopicInfoForTesting(struct NT_TopicInfo* info) {
std::free(info->name.str);
std::free(info->type_str.str);
WPI_FreeString(&info->name);
WPI_FreeString(&info->type_str);
WPI_FreeString(&info->properties);
std::free(info);
}
@@ -53,8 +54,8 @@ struct NT_ConnectionInfo* NT_GetConnectionInfoForTesting(
}
void NT_FreeConnectionInfoForTesting(struct NT_ConnectionInfo* info) {
std::free(info->remote_id.str);
std::free(info->remote_ip.str);
WPI_FreeString(&info->remote_id);
WPI_FreeString(&info->remote_ip);
std::free(info);
}
@@ -136,21 +137,19 @@ struct NT_Value* NT_GetValueDoubleArrayForTesting(uint64_t last_change,
}
struct NT_Value* NT_GetValueStringArrayForTesting(uint64_t last_change,
const struct NT_String* arr,
const struct WPI_String* arr,
size_t array_len,
int* struct_size) {
struct NT_Value* value =
static_cast<NT_Value*>(wpi::safe_calloc(1, sizeof(NT_Value)));
value->type = NT_BOOLEAN;
value->last_change = last_change;
value->data.arr_string.arr = NT_AllocateStringArray(array_len);
value->data.arr_string.arr = WPI_AllocateStringArray(array_len);
value->data.arr_string.size = array_len;
for (size_t i = 0; i < value->data.arr_string.size; ++i) {
size_t len = arr[i].len;
value->data.arr_string.arr[i].len = len;
value->data.arr_string.arr[i].str =
static_cast<char*>(wpi::safe_malloc(len + 1));
std::memcpy(value->data.arr_string.arr[i].str, arr[i].str, len + 1);
auto write = WPI_AllocateString(&value->data.arr_string.arr[i], len);
std::memcpy(write, arr[i].str, len);
}
*struct_size = sizeof(NT_Value);
return value;