Update for jart/json.cpp change

This commit is contained in:
Peter Johnson
2026-03-29 15:38:18 -07:00
parent de3e211fdb
commit 9ca93fa190
120 changed files with 1240 additions and 1087 deletions

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -15,8 +15,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -24,6 +22,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -19,18 +19,17 @@ using namespace wpi::nt;
static std::string ConnInfoToJson(bool connected, const ConnectionInfo& info) {
std::string str;
wpi::util::raw_string_ostream os{str};
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"connected\":" << (connected ? "true" : "false");
os << ",\"remote_id\":\"";
s.dump_escaped(info.remote_id, false);
os << "\",\"remote_ip\":\"";
s.dump_escaped(info.remote_ip, false);
os << "\",\"remote_port\":";
s.dump_integer(static_cast<uint64_t>(info.remote_port));
os << "{\"connected\":";
wpi::util::json::stringify_bool(os, connected);
os << ",\"remote_id\":";
wpi::util::json::stringify_string(os, info.remote_id);
os << ",\"remote_ip\":";
wpi::util::json::stringify_string(os, info.remote_ip);
os << ",\"remote_port\":";
wpi::util::json::stringify_int(os, info.remote_port);
os << ",\"protocol_version\":";
s.dump_integer(static_cast<uint64_t>(info.protocol_version));
wpi::util::json::stringify_int(os, info.protocol_version);
os << "}";
os.flush();
return str;
}

View File

@@ -735,7 +735,7 @@ Java_org_wpilib_networktables_NetworkTablesJNI_getTopicProperty
(JNIEnv* env, jclass, jint topic, jstring name)
{
return MakeJString(
env, wpi::nt::GetTopicProperty(topic, JStringRef{env, name}).dump());
env, wpi::nt::GetTopicProperty(topic, JStringRef{env, name}).to_string());
}
/*
@@ -747,15 +747,13 @@ JNIEXPORT void JNICALL
Java_org_wpilib_networktables_NetworkTablesJNI_setTopicProperty
(JNIEnv* env, jclass, jint topic, jstring name, jstring value)
{
wpi::util::json j;
try {
j = wpi::util::json::parse(std::string_view{JStringRef{env, value}});
} catch (wpi::util::json::parse_error& err) {
auto j = wpi::util::json::parse(std::string_view{JStringRef{env, value}});
if (!j) {
illegalArgEx.Throw(
env, fmt::format("could not parse value JSON: {}", err.what()));
env, fmt::format("could not parse value JSON: {}", j.error()));
return;
}
wpi::nt::SetTopicProperty(topic, JStringRef{env, name}, j);
wpi::nt::SetTopicProperty(topic, JStringRef{env, name}, *j);
}
/*
@@ -779,7 +777,7 @@ JNIEXPORT jstring JNICALL
Java_org_wpilib_networktables_NetworkTablesJNI_getTopicProperties
(JNIEnv* env, jclass, jint topic)
{
return MakeJString(env, wpi::nt::GetTopicProperties(topic).dump());
return MakeJString(env, wpi::nt::GetTopicProperties(topic).to_string());
}
/*
@@ -791,19 +789,18 @@ JNIEXPORT void JNICALL
Java_org_wpilib_networktables_NetworkTablesJNI_setTopicProperties
(JNIEnv* env, jclass, jint topic, jstring properties)
{
wpi::util::json j;
try {
j = wpi::util::json::parse(std::string_view{JStringRef{env, properties}});
} catch (wpi::util::json::parse_error& err) {
auto j =
wpi::util::json::parse(std::string_view{JStringRef{env, properties}});
if (!j) {
illegalArgEx.Throw(
env, fmt::format("could not parse properties JSON: {}", err.what()));
env, fmt::format("could not parse properties JSON: {}", j.error()));
return;
}
if (!j.is_object()) {
if (!j->is_object()) {
illegalArgEx.Throw(env, "properties is not a JSON object");
return;
}
wpi::nt::SetTopicProperties(topic, j);
wpi::nt::SetTopicProperties(topic, *j);
}
/*
@@ -856,20 +853,19 @@ Java_org_wpilib_networktables_NetworkTablesJNI_publishEx
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr,
jstring properties, jobject options)
{
wpi::util::json j;
try {
j = wpi::util::json::parse(std::string_view{JStringRef{env, properties}});
} catch (wpi::util::json::parse_error& err) {
auto j =
wpi::util::json::parse(std::string_view{JStringRef{env, properties}});
if (!j) {
illegalArgEx.Throw(
env, fmt::format("could not parse properties JSON: {}", err.what()));
env, fmt::format("could not parse properties JSON: {}", j.error()));
return 0;
}
if (!j.is_object()) {
if (!j->is_object()) {
illegalArgEx.Throw(env, "properties is not a JSON object");
return 0;
}
return wpi::nt::PublishEx(topic, static_cast<NT_Type>(type),
JStringRef{env, typeStr}, j,
JStringRef{env, typeStr}, *j,
FromJavaPubSubOptions(env, options));
}

View File

@@ -40,7 +40,7 @@ void StorageImpl::NetworkAnnounce(LocalTopic* topic, std::string_view typeStr,
const wpi::util::json& properties,
std::optional<int> pubuid) {
DEBUG4("LS NetworkAnnounce({}, {}, {}, {})", topic->name, typeStr,
properties.dump(), pubuid.value_or(-1));
properties.to_string(), pubuid.value_or(-1));
if (pubuid.has_value()) {
return; // ack of our publish; ignore
}
@@ -751,9 +751,10 @@ void StorageImpl::AddSchema(std::string_view name, std::string_view type,
return;
}
pubHandle = AddLocalPublisher(topic, {{"retained", true}},
PubSubConfig{NT_RAW, type, {}})
->handle;
pubHandle =
AddLocalPublisher(topic, wpi::util::json::object("retained", true),
PubSubConfig{NT_RAW, type, {}})
->handle;
SetDefaultEntryValue(pubHandle, Value::MakeRaw(schema));
}
@@ -870,8 +871,8 @@ void StorageImpl::PropertiesUpdated(LocalTopic* topic,
const wpi::util::json& update,
unsigned int eventFlags, bool sendNetwork,
bool updateFlags) {
DEBUG4("PropertiesUpdated({}, {}, {}, {}, {})", topic->name, update.dump(),
eventFlags, sendNetwork, updateFlags);
DEBUG4("PropertiesUpdated({}, {}, {}, {}, {})", topic->name,
update.to_string(), eventFlags, sendNetwork, updateFlags);
topic->RefreshProperties(updateFlags);
unsigned int flags = topic->GetFlags();
if (updateFlags && (flags & NT_UNCACHED) != 0 &&

View File

@@ -137,11 +137,11 @@ bool LocalTopic::SetProperties(const wpi::util::json& update) {
if (!update.is_object()) {
return false;
}
for (auto&& change : update.items()) {
if (change.value().is_null()) {
properties.erase(change.key());
for (auto&& [key, value] : update.get_object()) {
if (value.is_null()) {
properties.erase(key);
} else {
properties[change.key()] = change.value();
properties[key] = value;
}
}
return true;
@@ -151,22 +151,25 @@ void LocalTopic::RefreshProperties(bool updateFlags) {
if (updateFlags) {
RefreshFlags();
}
m_propertiesStr = properties.dump();
m_propertiesStr = properties.to_string();
}
wpi::util::json LocalTopic::CompareProperties(const wpi::util::json props) {
wpi::util::json LocalTopic::CompareProperties(const wpi::util::json& props) {
wpi::util::json update = wpi::util::json::object();
// added/changed
for (auto&& prop : props.items()) {
auto it = properties.find(prop.key());
if (it == properties.end() || *it != prop.value()) {
update[prop.key()] = prop.value();
if (!props.is_object()) {
return update;
}
for (auto&& [key, value] : props.get_object()) {
auto it = properties.lookup(key);
if (!it || *it != value) {
update[key] = value;
}
}
// removed
for (auto&& prop : properties.items()) {
if (props.find(prop.key()) == props.end()) {
update[prop.key()] = wpi::util::json();
for (auto&& [key, value] : properties.get_object()) {
if (!props.contains(key)) {
update[key] = wpi::util::json();
}
}
return update;
@@ -187,30 +190,27 @@ void LocalTopic::ResetIfDoesNotExist() {
}
void LocalTopic::RefreshFlags() {
auto it = properties.find("persistent");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
if (auto persistent = properties.lookup("persistent")) {
if (persistent->is_bool()) {
if (persistent->get_bool()) {
m_flags |= NT_PERSISTENT;
} else {
m_flags &= ~NT_PERSISTENT;
}
}
}
it = properties.find("retained");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
if (auto retained = properties.lookup("retained")) {
if (retained->is_bool()) {
if (retained->get_bool()) {
m_flags |= NT_RETAINED;
} else {
m_flags &= ~NT_RETAINED;
}
}
}
it = properties.find("cached");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
if (auto cached = properties.lookup("cached")) {
if (cached->is_bool()) {
if (cached->get_bool()) {
m_flags &= ~NT_UNCACHED;
} else {
m_flags |= NT_UNCACHED;

View File

@@ -62,7 +62,7 @@ struct LocalTopic {
void RefreshProperties(bool updateFlags);
// returns update json
wpi::util::json CompareProperties(const wpi::util::json props);
wpi::util::json CompareProperties(const wpi::util::json& props);
TopicInfo GetTopicInfo() const {
TopicInfo info;

View File

@@ -240,7 +240,7 @@ void ClientImpl::ServerUnannounce(std::string_view name, int id) {
void ClientImpl::ServerPropertiesUpdate(std::string_view name,
const wpi::util::json& update,
bool ack) {
DEBUG4("ServerProperties({}, {}, {})", name, update.dump(), ack);
DEBUG4("ServerProperties({}, {}, {})", name, update.to_string(), ack);
assert(m_local);
m_local->ServerPropertiesUpdate(name, update, ack);
}

View File

@@ -9,7 +9,9 @@
#include <string>
#include <string_view>
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {
class PubSubOptionsImpl;

View File

@@ -20,6 +20,8 @@
#include "wpi/nt/NetworkTableValue.hpp"
#include "wpi/nt/ntcore_c.h"
#include "wpi/util/DenseMap.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/raw_ostream.hpp"
namespace wpi::nt::net {

View File

@@ -24,12 +24,8 @@ using namespace wpi::nt::net;
using namespace mpack;
static bool GetNumber(wpi::util::json& val, double* num) {
if (auto v = val.get_ptr<const int64_t*>()) {
*num = *v;
} else if (auto v = val.get_ptr<const uint64_t*>()) {
*num = *v;
} else if (auto v = val.get_ptr<const double*>()) {
*num = *v;
if (val.is_number()) {
*num = val.get_number();
} else {
return false;
}
@@ -37,67 +33,64 @@ static bool GetNumber(wpi::util::json& val, double* num) {
}
static bool GetNumber(wpi::util::json& val, int64_t* num) {
if (auto v = val.get_ptr<const int64_t*>()) {
*num = *v;
} else if (auto v = val.get_ptr<const uint64_t*>()) {
*num = *v;
if (val.is_int()) {
*num = val.get_int();
} else {
return false;
}
return true;
}
static std::string* ObjGetString(wpi::util::json::object_t& obj,
std::string_view key, std::string* error) {
auto it = obj.find(key);
if (it == obj.end()) {
static std::string* ObjGetString(wpi::util::json& obj, std::string_view key,
std::string* error) {
auto val = obj.lookup(key);
if (!val) {
*error = fmt::format("no {} key", key);
return nullptr;
}
auto val = it->second.get_ptr<std::string*>();
if (!val) {
if (!val->is_string()) {
*error = fmt::format("{} must be a string", key);
return nullptr;
}
return val;
return &val->get_string();
}
static bool ObjGetNumber(wpi::util::json::object_t& obj, std::string_view key,
static bool ObjGetNumber(wpi::util::json& obj, std::string_view key,
std::string* error, int64_t* num) {
auto it = obj.find(key);
if (it == obj.end()) {
auto val = obj.lookup(key);
if (!val) {
*error = fmt::format("no {} key", key);
return false;
}
if (!GetNumber(it->second, num)) {
if (!GetNumber(*val, num)) {
*error = fmt::format("{} must be a number", key);
return false;
}
return true;
}
static bool ObjGetStringArray(wpi::util::json::object_t& obj,
std::string_view key, std::string* error,
static bool ObjGetStringArray(wpi::util::json& obj, std::string_view key,
std::string* error,
std::vector<std::string>* out) {
// prefixes
auto it = obj.find(key);
if (it == obj.end()) {
auto val = obj.lookup(key);
if (!val) {
*error = fmt::format("no {} key", key);
return false;
}
auto jarr = it->second.get_ptr<wpi::util::json::array_t*>();
if (!jarr) {
if (!val->is_array()) {
*error = fmt::format("{} must be an array", key);
return false;
}
auto& arr = val->get_array();
out->resize(0);
out->reserve(jarr->size());
for (auto&& jval : *jarr) {
auto str = jval.get_ptr<std::string*>();
if (!str) {
out->reserve(arr.size());
for (auto&& jval : arr) {
if (!jval.is_string()) {
*error = fmt::format("{}/{} must be a string", key, out->size());
return false;
}
out->emplace_back(std::move(*str));
out->emplace_back(jval.get_string());
}
return true;
}
@@ -113,43 +106,39 @@ template <typename T>
std::same_as<T, ServerMessageHandler>)
static bool WireDecodeTextImpl(std::string_view in, T& out,
wpi::util::Logger& logger) {
wpi::util::json j;
try {
j = wpi::util::json::parse(in);
} catch (wpi::util::json::parse_error& err) {
WPI_WARNING(logger, "could not decode JSON message: {}", err.what());
auto j = wpi::util::json::parse(in);
if (!j) {
WPI_WARNING(logger, "could not decode JSON message: {}", j.error());
return false;
}
if (!j.is_array()) {
if (!j->is_array()) {
WPI_WARNING(logger, "expected JSON array at top level");
return false;
}
bool rv = false;
int i = -1;
for (auto&& jmsg : j) {
for (auto&& jmsg : j->get_array()) {
++i;
std::string error;
{
auto obj = jmsg.get_ptr<wpi::util::json::object_t*>();
if (!obj) {
if (!jmsg.is_object()) {
error = "expected message to be an object";
goto err;
}
auto method = ObjGetString(*obj, "method", &error);
auto method = ObjGetString(jmsg, "method", &error);
if (!method) {
goto err;
}
auto paramsIt = obj->find("params");
if (paramsIt == obj->end()) {
auto params = jmsg.lookup("params");
if (!params) {
error = "no params key";
goto err;
}
auto params = paramsIt->second.get_ptr<wpi::util::json::object_t*>();
if (!params) {
if (!params->is_object()) {
error = "params must be an object";
goto err;
}
@@ -182,14 +171,10 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// properties; allow missing (treated as empty)
wpi::util::json* properties = nullptr;
auto propertiesIt = params->find("properties");
if (propertiesIt != params->end()) {
properties = &propertiesIt->second;
if (!properties->is_object()) {
error = "properties must be an object";
goto err;
}
auto properties = params->lookup("properties");
if (properties && !properties->is_object()) {
error = "properties must be an object";
goto err;
}
wpi::util::json propertiesEmpty;
if (!properties) {
@@ -225,12 +210,11 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// update
auto updateIt = params->find("update");
if (updateIt == params->end()) {
auto update = params->lookup("update");
if (!update) {
error = "no update key";
goto err;
}
auto update = &updateIt->second;
if (!update->is_object()) {
error = "update must be an object";
goto err;
@@ -254,20 +238,16 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
// options
PubSubOptionsImpl options;
auto optionsIt = params->find("options");
if (optionsIt != params->end()) {
auto joptions =
optionsIt->second.get_ptr<wpi::util::json::object_t*>();
if (!joptions) {
if (auto joptions = params->lookup("options")) {
if (!joptions->is_object()) {
error = "options must be an object";
goto err;
}
// periodic
auto periodicIt = joptions->find("periodic");
if (periodicIt != joptions->end()) {
if (auto periodic = joptions->lookup("periodic")) {
double val;
if (!GetNumber(periodicIt->second, &val)) {
if (!GetNumber(*periodic, &val)) {
error = "periodic value must be a number";
goto err;
}
@@ -276,36 +256,30 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// send all changes
auto sendAllIt = joptions->find("all");
if (sendAllIt != joptions->end()) {
auto sendAll = sendAllIt->second.get_ptr<bool*>();
if (!sendAll) {
if (auto sendAll = joptions->lookup("all")) {
if (!sendAll->is_bool()) {
error = "all value must be a boolean";
goto err;
}
options.sendAll = *sendAll;
options.sendAll = sendAll->get_bool();
}
// topics only
auto topicsOnlyIt = joptions->find("topicsonly");
if (topicsOnlyIt != joptions->end()) {
auto topicsOnly = topicsOnlyIt->second.get_ptr<bool*>();
if (!topicsOnly) {
if (auto topicsOnly = joptions->lookup("topicsonly")) {
if (!topicsOnly->is_bool()) {
error = "topicsonly value must be a boolean";
goto err;
}
options.topicsOnly = *topicsOnly;
options.topicsOnly = topicsOnly->get_bool();
}
// prefix match
auto prefixMatchIt = joptions->find("prefix");
if (prefixMatchIt != joptions->end()) {
auto prefixMatch = prefixMatchIt->second.get_ptr<bool*>();
if (!prefixMatch) {
if (auto prefixMatch = joptions->lookup("prefix")) {
if (!prefixMatch->is_bool()) {
error = "prefix value must be a boolean";
goto err;
}
options.prefixMatch = *prefixMatch;
options.prefixMatch = prefixMatch->get_bool();
}
}
@@ -368,10 +342,9 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
// pubuid
std::optional<int64_t> pubuid;
auto pubuidIt = params->find("pubuid");
if (pubuidIt != params->end()) {
if (auto jpubuid = params->lookup("pubuid")) {
int64_t val;
if (!GetNumber(pubuidIt->second, &val)) {
if (!GetNumber(*jpubuid, &val)) {
error = "pubuid value must be a number";
goto err;
}
@@ -387,12 +360,11 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// properties
auto propertiesIt = params->find("properties");
if (propertiesIt == params->end()) {
auto properties = params->lookup("properties");
if (!properties) {
error = "no properties key";
goto err;
}
auto properties = &propertiesIt->second;
if (!properties->is_object()) {
WPI_WARNING(logger, "{}: properties is not an object", *name);
*properties = wpi::util::json::object();
@@ -430,26 +402,23 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// update
auto updateIt = params->find("update");
if (updateIt == params->end()) {
auto update = params->lookup("update");
if (!update) {
error = "no update key";
goto err;
}
auto update = &updateIt->second;
if (!update->is_object()) {
error = "update must be an object";
goto err;
}
bool ack = false;
auto ackIt = params->find("ack");
if (ackIt != params->end()) {
auto val = ackIt->second.get_ptr<bool*>();
if (!val) {
if (auto jack = params->lookup("ack")) {
if (!jack->is_bool()) {
error = "ack must be a boolean";
goto err;
}
ack = *val;
ack = jack->get_bool();
}
// complete

View File

@@ -22,43 +22,39 @@ void wpi::nt::net::WireEncodePublish(wpi::util::raw_ostream& os, int pubuid,
std::string_view name,
std::string_view typeStr,
const wpi::util::json& properties) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << PublishMsg::kMethodStr << "\",\"params\":{";
os << "\"name\":\"";
s.dump_escaped(name, false);
os << "\",\"properties\":";
s.dump(properties, false, false, 0, 0);
os << "\"name\":";
wpi::util::json::stringify_string(os, name);
os << ",\"properties\":";
properties.marshal(os);
os << ",\"pubuid\":";
s.dump_integer(pubuid);
os << ",\"type\":\"";
s.dump_escaped(typeStr, false);
os << "\"}}";
wpi::util::json::stringify_int(os, pubuid);
os << ",\"type\":";
wpi::util::json::stringify_string(os, typeStr);
os << "}}";
}
void wpi::nt::net::WireEncodeUnpublish(wpi::util::raw_ostream& os, int pubuid) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << UnpublishMsg::kMethodStr << "\",\"params\":{";
os << "\"pubuid\":";
s.dump_integer(pubuid);
wpi::util::json::stringify_int(os, pubuid);
os << "}}";
}
void wpi::nt::net::WireEncodeSetProperties(wpi::util::raw_ostream& os,
std::string_view name,
const wpi::util::json& update) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << SetPropertiesMsg::kMethodStr << "\",\"params\":{";
os << "\"name\":\"";
s.dump_escaped(name, false);
os << "\",\"update\":";
s.dump(update, false, false, 0, 0);
os << "\"name\":";
wpi::util::json::stringify_string(os, name);
os << ",\"update\":";
update.marshal(os);
os << "}}";
}
template <typename T>
static void EncodePrefixes(wpi::util::raw_ostream& os,
std::span<const T> topicNames,
wpi::util::json::serializer& s) {
std::span<const T> topicNames) {
os << '[';
bool first = true;
for (auto&& name : topicNames) {
@@ -67,9 +63,7 @@ static void EncodePrefixes(wpi::util::raw_ostream& os,
} else {
os << ',';
}
os << '"';
s.dump_escaped(name, false);
os << '"';
wpi::util::json::stringify_string(os, name);
}
os << ']';
}
@@ -78,7 +72,6 @@ template <typename T>
static void WireEncodeSubscribeImpl(wpi::util::raw_ostream& os, int subuid,
std::span<const T> topicNames,
const PubSubOptionsImpl& options) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << SubscribeMsg::kMethodStr << "\",\"params\":{";
os << "\"options\":{";
bool first = true;
@@ -105,12 +98,12 @@ static void WireEncodeSubscribeImpl(wpi::util::raw_ostream& os, int subuid,
os << ',';
}
os << "\"periodic\":";
s.dump_float(options.periodicMs / 1000.0);
wpi::util::json::stringify_float(os, options.periodicMs / 1000.0);
}
os << "},\"topics\":";
EncodePrefixes(os, topicNames, s);
EncodePrefixes(os, topicNames);
os << ",\"subuid\":";
s.dump_integer(subuid);
wpi::util::json::stringify_int(os, subuid);
os << "}}";
}
@@ -129,10 +122,9 @@ void wpi::nt::net::WireEncodeSubscribe(wpi::util::raw_ostream& os, int subuid,
void wpi::nt::net::WireEncodeUnsubscribe(wpi::util::raw_ostream& os,
int subuid) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << UnsubscribeMsg::kMethodStr << "\",\"params\":{";
os << "\"subuid\":";
s.dump_integer(subuid);
wpi::util::json::stringify_int(os, subuid);
os << "}}";
}
@@ -159,45 +151,42 @@ void wpi::nt::net::WireEncodeAnnounce(wpi::util::raw_ostream& os,
std::string_view typeStr,
const wpi::util::json& properties,
std::optional<int> pubuid) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << AnnounceMsg::kMethodStr << "\",\"params\":{";
os << "\"id\":";
s.dump_integer(id);
os << ",\"name\":\"";
s.dump_escaped(name, false);
os << "\",\"properties\":";
s.dump(properties, false, false, 0, 0);
wpi::util::json::stringify_int(os, id);
os << ",\"name\":";
wpi::util::json::stringify_string(os, name);
os << ",\"properties\":";
properties.marshal(os);
if (pubuid) {
os << ",\"pubuid\":";
s.dump_integer(*pubuid);
wpi::util::json::stringify_int(os, *pubuid);
}
os << ",\"type\":\"";
s.dump_escaped(typeStr, false);
os << "\"}}";
os << ",\"type\":";
wpi::util::json::stringify_string(os, typeStr);
os << "}}";
}
void wpi::nt::net::WireEncodeUnannounce(wpi::util::raw_ostream& os,
std::string_view name, int64_t id) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << UnannounceMsg::kMethodStr << "\",\"params\":{";
os << "\"id\":";
s.dump_integer(id);
os << ",\"name\":\"";
s.dump_escaped(name, false);
os << "\"}}";
wpi::util::json::stringify_int(os, id);
os << ",\"name\":";
wpi::util::json::stringify_string(os, name);
os << "}}";
}
void wpi::nt::net::WireEncodePropertiesUpdate(wpi::util::raw_ostream& os,
std::string_view name,
const wpi::util::json& update,
bool ack) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << PropertiesUpdateMsg::kMethodStr
<< "\",\"params\":{";
os << "\"name\":\"";
s.dump_escaped(name, false);
os << "\",\"update\":";
s.dump(update, false, false, 0, 0);
os << "\"name\":";
wpi::util::json::stringify_string(os, name);
os << ",\"update\":";
update.marshal(os);
if (ack) {
os << ",\"ack\":true";
}

View File

@@ -9,9 +9,8 @@
#include <string>
#include <string_view>
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
class raw_ostream;
} // namespace wpi::util

View File

@@ -10,6 +10,7 @@
#include <cstdlib>
#include <cstring>
#include <string_view>
#include <utility>
#include <vector>
#include "Value_internal.hpp"
@@ -313,18 +314,16 @@ void NT_GetTopicProperty(NT_Topic topic, const struct WPI_String* name,
struct WPI_String* prop) {
wpi::util::json j =
wpi::nt::GetTopicProperty(topic, wpi::util::to_string_view(name));
wpi::nt::ConvertToC(j.dump(), prop);
wpi::nt::ConvertToC(j.to_string(), prop);
}
NT_Bool NT_SetTopicProperty(NT_Topic topic, const struct WPI_String* name,
const struct WPI_String* value) {
wpi::util::json j;
try {
j = wpi::util::json::parse(wpi::util::to_string_view(value));
} catch (wpi::util::json::parse_error&) {
auto j = wpi::util::json::parse(wpi::util::to_string_view(value));
if (!j) {
return false;
}
wpi::nt::SetTopicProperty(topic, wpi::util::to_string_view(name), j);
wpi::nt::SetTopicProperty(topic, wpi::util::to_string_view(name), *j);
return true;
}
@@ -334,18 +333,16 @@ void NT_DeleteTopicProperty(NT_Topic topic, const struct WPI_String* name) {
void NT_GetTopicProperties(NT_Topic topic, struct WPI_String* property) {
wpi::util::json j = wpi::nt::GetTopicProperties(topic);
wpi::nt::ConvertToC(j.dump(), property);
wpi::nt::ConvertToC(j.to_string(), property);
}
NT_Bool NT_SetTopicProperties(NT_Topic topic,
const struct WPI_String* properties) {
wpi::util::json j;
try {
j = wpi::util::json::parse(wpi::util::to_string_view(properties));
} catch (wpi::util::json::parse_error&) {
auto j = wpi::util::json::parse(wpi::util::to_string_view(properties));
if (!j) {
return false;
}
return wpi::nt::SetTopicProperties(topic, j);
return wpi::nt::SetTopicProperties(topic, *j);
}
NT_Subscriber NT_Subscribe(NT_Topic topic, NT_Type type,
@@ -375,11 +372,11 @@ NT_Publisher NT_PublishEx(NT_Topic topic, NT_Type type,
// gracefully handle empty string
j = wpi::util::json::object();
} else {
try {
j = wpi::util::json::parse(wpi::util::to_string_view(properties));
} catch (wpi::util::json::parse_error&) {
auto ex = wpi::util::json::parse(wpi::util::to_string_view(properties));
if (!ex) {
return {};
}
j = std::move(*ex);
}
return wpi::nt::PublishEx(topic, type, wpi::util::to_string_view(typeStr), j,

View File

@@ -29,11 +29,7 @@ static std::atomic<int64_t> gNowTime;
namespace wpi::nt {
wpi::util::json TopicInfo::GetProperties() const {
try {
return wpi::util::json::parse(properties);
} catch (wpi::util::json::parse_error&) {
return wpi::util::json::object();
}
return wpi::util::json::parse(properties).value_or(wpi::util::json::object());
}
/*

View File

@@ -17,9 +17,9 @@
#include "server/Functions.hpp"
#include "server/ServerPublisher.hpp"
#include "server/ServerSubscriber.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
class Logger;
template <typename T>
class SmallVectorImpl;

View File

@@ -4,6 +4,7 @@
#include "ServerClient4Base.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
@@ -38,7 +39,8 @@ void ServerClient4Base::ClientPublish(int pubuid, std::string_view name,
}
// respond with announce with pubuid to client
DEBUG4("client {}: announce {} pubuid {}", m_id, topic->name, pubuid);
DEBUG4("client {}: announce {} pubuid {} properties {}", m_id, topic->name,
pubuid, properties.to_string());
SendAnnounce(topic, pubuid);
}
@@ -68,18 +70,18 @@ void ServerClient4Base::ClientUnpublish(int pubuid) {
void ServerClient4Base::ClientSetProperties(std::string_view name,
const wpi::util::json& update) {
DEBUG4("ClientSetProperties({}, {}, {})", m_id, name, update.dump());
DEBUG4("ClientSetProperties({}, {}, {})", m_id, name, update.to_string());
ServerTopic* topic = m_storage.GetTopic(name);
if (!topic || !topic->IsPublished()) {
WARN(
"server ignoring SetProperties({}) from client {} on unpublished topic "
"'{}'; publish or set a value first",
update.dump(), m_id, name);
update.to_string(), m_id, name);
return; // nothing to do
}
if (topic->special) {
WARN("server ignoring SetProperties({}) from client {} on meta topic '{}'",
update.dump(), m_id, name);
update.to_string(), m_id, name);
return; // nothing to do
}
m_storage.SetProperties(nullptr, topic, update);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <string>
#include <string_view>

View File

@@ -111,7 +111,8 @@ void ServerImpl::SendAnnounce(ServerTopic* topic, ServerClient* client) {
continue; // don't announce to requesting client again
}
DEBUG4("client {}: announce {}", aClient->GetId(), topic->name);
DEBUG4("client {}: announce {} properties {}", aClient->GetId(),
topic->name, topic->properties.to_string());
aClient->SendAnnounce(topic, std::nullopt);
}
}

View File

@@ -36,6 +36,8 @@ ServerTopic* ServerStorage::CreateTopic(ServerClient* client,
}
}
} else {
DEBUG4("creating topic '{}' with type '{}' and properties {}", name,
typeStr, properties.to_string());
// new topic
unsigned int id = m_topics.emplace_back(
std::make_unique<ServerTopic>(m_logger, name, typeStr, properties));
@@ -58,7 +60,8 @@ ServerTopic* ServerStorage::CreateTopic(ServerClient* client,
}
ServerTopic* ServerStorage::CreateMetaTopic(std::string_view name) {
return CreateTopic(nullptr, name, "msgpack", {{"retained", true}}, true);
return CreateTopic(nullptr, name, "msgpack",
wpi::util::json::object("retained", true), true);
}
void ServerStorage::DeleteTopic(ServerTopic* topic) {
@@ -90,7 +93,7 @@ void ServerStorage::DeleteTopic(ServerTopic* topic) {
void ServerStorage::SetProperties(ServerClient* client, ServerTopic* topic,
const wpi::util::json& update) {
DEBUG4("SetProperties({}, {}, {})", client ? client->GetId() : -1,
topic->name, update.dump());
topic->name, update.to_string());
bool wasPersistent = topic->persistent;
if (topic->SetProperties(update)) {
// update persistentChanged flag
@@ -110,9 +113,10 @@ void ServerStorage::SetFlags(ServerClient* client, ServerTopic* topic,
m_persistentChanged = true;
wpi::util::json update;
if (topic->persistent) {
update = {{"persistent", true}};
update = wpi::util::json::object("persistent", true);
} else {
update = {{"persistent", wpi::util::json::object()}};
update =
wpi::util::json::object("persistent", wpi::util::json::object());
}
PropertiesChanged(client, topic, update);
}
@@ -234,29 +238,22 @@ void ServerStorage::PropertiesChanged(ServerClient* client, ServerTopic* topic,
}
}
static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
wpi::util::json::serializer& s) {
static void DumpValue(wpi::util::raw_ostream& os, const Value& value) {
switch (value.type()) {
case NT_BOOLEAN:
if (value.GetBoolean()) {
os << "true";
} else {
os << "false";
}
wpi::util::json::stringify_bool(os, value.GetBoolean());
break;
case NT_DOUBLE:
s.dump_float(value.GetDouble());
wpi::util::json::stringify_double(os, value.GetDouble());
break;
case NT_FLOAT:
s.dump_float(value.GetFloat());
wpi::util::json::stringify_float(os, value.GetFloat());
break;
case NT_INTEGER:
s.dump_integer(value.GetInteger());
wpi::util::json::stringify_int(os, value.GetInteger());
break;
case NT_STRING:
os << '"';
s.dump_escaped(value.GetString(), false);
os << '"';
wpi::util::json::stringify_string(os, value.GetString());
break;
case NT_RAW:
case NT_RPC:
@@ -273,11 +270,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
if (v) {
os << "true";
} else {
os << "false";
}
wpi::util::json::stringify_bool(os, v);
}
os << ']';
break;
@@ -291,7 +284,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
s.dump_float(v);
wpi::util::json::stringify_double(os, v);
}
os << ']';
break;
@@ -305,7 +298,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
s.dump_float(v);
wpi::util::json::stringify_float(os, v);
}
os << ']';
break;
@@ -319,7 +312,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
s.dump_integer(v);
wpi::util::json::stringify_int(os, v);
}
os << ']';
break;
@@ -333,9 +326,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
os << '"';
s.dump_escaped(v, false);
os << '"';
wpi::util::json::stringify_string(os, v);
}
os << ']';
break;
@@ -347,7 +338,6 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
}
void ServerStorage::DumpPersistent(wpi::util::raw_ostream& os) {
wpi::util::json::serializer s{os, ' ', 16};
os << "[\n";
bool first = true;
for (const auto& topic : m_topics) {
@@ -359,31 +349,31 @@ void ServerStorage::DumpPersistent(wpi::util::raw_ostream& os) {
} else {
os << ",\n";
}
os << " {\n \"name\": \"";
s.dump_escaped(topic->name, false);
os << "\",\n \"type\": \"";
s.dump_escaped(topic->typeStr, false);
os << "\",\n \"value\": ";
DumpValue(os, topic->lastValue, s);
os << " {\n \"name\": ";
wpi::util::json::stringify_string(os, topic->name);
os << ",\n \"type\": ";
wpi::util::json::stringify_string(os, topic->typeStr);
os << ",\n \"value\": ";
DumpValue(os, topic->lastValue);
os << ",\n \"properties\": ";
s.dump(topic->properties, true, false, 2, 4);
topic->properties.marshal(os, true, 2);
os << "\n }";
}
os << "\n]\n";
}
static std::string* ObjGetString(wpi::util::json::object_t& obj,
std::string_view key, std::string* error) {
auto it = obj.find(key);
if (it == obj.end()) {
static std::string* ObjGetString(wpi::util::json& obj, std::string_view key,
std::string* error) {
auto value = obj.lookup(key);
if (!value) {
*error = fmt::format("no {} key", key);
return nullptr;
}
auto val = it->second.get_ptr<std::string*>();
if (!val) {
if (!value->is_string()) {
*error = fmt::format("{} must be a string", key);
return nullptr;
}
return val;
return &value->get_string();
}
std::string ServerStorage::LoadPersistent(std::string_view in) {
@@ -391,14 +381,12 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
return {};
}
wpi::util::json j;
try {
j = wpi::util::json::parse(in);
} catch (wpi::util::json::parse_error& err) {
return fmt::format("could not decode JSON: {}", err.what());
auto j = wpi::util::json::parse(in);
if (!j) {
return fmt::format("could not decode JSON: {}", j.error());
}
if (!j.is_array()) {
if (!j->is_array()) {
return "expected JSON array at top level";
}
@@ -407,48 +395,46 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
std::string allerrors;
int i = -1;
auto time = wpi::nt::Now();
for (auto&& jitem : j) {
for (auto&& jitem : j->get_array()) {
++i;
std::string error;
{
auto obj = jitem.get_ptr<wpi::util::json::object_t*>();
if (!obj) {
if (!jitem.is_object()) {
error = "expected item to be an object";
goto err;
}
// name
auto name = ObjGetString(*obj, "name", &error);
auto name = ObjGetString(jitem, "name", &error);
if (!name) {
goto err;
}
// type
auto typeStr = ObjGetString(*obj, "type", &error);
auto typeStr = ObjGetString(jitem, "type", &error);
if (!typeStr) {
goto err;
}
// properties
auto propsIt = obj->find("properties");
if (propsIt == obj->end()) {
auto props = jitem.lookup("properties");
if (!props) {
error = "no properties key";
goto err;
}
auto& props = propsIt->second;
if (!props.is_object()) {
if (!props->is_object()) {
error = "properties must be an object";
goto err;
}
// check to make sure persistent property is set
auto persistentIt = props.find("persistent");
if (persistentIt == props.end()) {
auto persistent = props->lookup("persistent");
if (!persistent) {
error = "no persistent property";
goto err;
}
if (auto v = persistentIt->get_ptr<bool*>()) {
if (!*v) {
if (persistent->is_bool()) {
if (!persistent->get_bool()) {
error = "persistent property is false";
goto err;
}
@@ -458,121 +444,112 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
}
// value
auto valueIt = obj->find("value");
if (valueIt == obj->end()) {
auto jvalue = jitem.lookup("value");
if (!jvalue) {
error = "no value key";
goto err;
}
Value value;
if (*typeStr == "boolean") {
if (auto v = valueIt->second.get_ptr<bool*>()) {
value = Value::MakeBoolean(*v, time);
if (jvalue->is_bool()) {
value = Value::MakeBoolean(jvalue->get_bool(), time);
} else {
error = "value type mismatch, expected boolean";
goto err;
}
} else if (*typeStr == "int") {
if (auto v = valueIt->second.get_ptr<int64_t*>()) {
value = Value::MakeInteger(*v, time);
} else if (auto v = valueIt->second.get_ptr<uint64_t*>()) {
value = Value::MakeInteger(*v, time);
if (jvalue->is_int()) {
value = Value::MakeInteger(jvalue->get_int(), time);
} else {
error = "value type mismatch, expected int";
goto err;
}
} else if (*typeStr == "float") {
if (auto v = valueIt->second.get_ptr<double*>()) {
value = Value::MakeFloat(*v, time);
if (jvalue->is_number()) {
value = Value::MakeFloat(jvalue->get_number(), time);
} else {
error = "value type mismatch, expected float";
goto err;
}
} else if (*typeStr == "double") {
if (auto v = valueIt->second.get_ptr<double*>()) {
value = Value::MakeDouble(*v, time);
if (jvalue->is_number()) {
value = Value::MakeDouble(jvalue->get_number(), time);
} else {
error = "value type mismatch, expected double";
goto err;
}
} else if (*typeStr == "string" || *typeStr == "json") {
if (auto v = valueIt->second.get_ptr<std::string*>()) {
value = Value::MakeString(*v, time);
if (jvalue->is_string()) {
value = Value::MakeString(jvalue->get_string(), time);
} else {
error = "value type mismatch, expected string";
goto err;
}
} else if (*typeStr == "boolean[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<int> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<bool*>()) {
elems.push_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_bool()) {
elems.push_back(jelem.get_bool());
} else {
error = "value type mismatch, expected boolean";
}
}
value = Value::MakeBooleanArray(elems, time);
} else if (*typeStr == "int[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<int64_t> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<int64_t*>()) {
elems.push_back(*v);
} else if (auto v = jelem.get_ptr<uint64_t*>()) {
elems.push_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_int()) {
elems.push_back(jelem.get_int());
} else {
error = "value type mismatch, expected int";
}
}
value = Value::MakeIntegerArray(elems, time);
} else if (*typeStr == "double[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<double> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<double*>()) {
elems.push_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_number()) {
elems.push_back(jelem.get_number());
} else {
error = "value type mismatch, expected double";
}
}
value = Value::MakeDoubleArray(elems, time);
} else if (*typeStr == "float[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<float> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<double*>()) {
elems.push_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_number()) {
elems.push_back(jelem.get_number());
} else {
error = "value type mismatch, expected float";
}
}
value = Value::MakeFloatArray(elems, time);
} else if (*typeStr == "string[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<std::string> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<std::string*>()) {
elems.emplace_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_string()) {
elems.push_back(jelem.get_string());
} else {
error = "value type mismatch, expected string";
}
@@ -580,9 +557,9 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
value = Value::MakeStringArray(std::move(elems), time);
} else {
// raw
if (auto v = valueIt->second.get_ptr<std::string*>()) {
if (jvalue->is_string()) {
std::vector<uint8_t> data;
wpi::util::Base64Decode(*v, &data);
wpi::util::Base64Decode(jvalue->get_string(), &data);
value = Value::MakeRaw(std::move(data), time);
} else {
error = "value type mismatch, expected string";
@@ -591,7 +568,7 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
}
// create persistent topic
auto topic = CreateTopic(nullptr, *name, *typeStr, props);
auto topic = CreateTopic(nullptr, *name, *typeStr, *props);
// set value
SetValue(nullptr, topic, value);

View File

@@ -12,10 +12,10 @@
#include "server/ServerTopic.hpp"
#include "wpi/util/StringMap.hpp"
#include "wpi/util/UidVector.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class Logger;
class json;
class raw_ostream;
} // namespace wpi::util

View File

@@ -13,11 +13,11 @@ bool ServerTopic::SetProperties(const wpi::util::json& update) {
return false;
}
bool updated = false;
for (auto&& elem : update.items()) {
if (elem.value().is_null()) {
properties.erase(elem.key());
for (auto&& [key, value] : update.get_object()) {
if (value.is_null()) {
properties.erase(key);
} else {
properties[elem.key()] = elem.value();
properties[key] = value;
}
updated = true;
}
@@ -32,24 +32,21 @@ void ServerTopic::RefreshProperties() {
retained = false;
cached = true;
auto persistentIt = properties.find("persistent");
if (persistentIt != properties.end()) {
if (auto val = persistentIt->get_ptr<bool*>()) {
persistent = *val;
if (auto prop = properties.lookup("persistent")) {
if (prop->is_bool()) {
persistent = prop->get_bool();
}
}
auto retainedIt = properties.find("retained");
if (retainedIt != properties.end()) {
if (auto val = retainedIt->get_ptr<bool*>()) {
retained = *val;
if (auto prop = properties.lookup("retained")) {
if (prop->is_bool()) {
retained = prop->get_bool();
}
}
auto cachedIt = properties.find("cached");
if (cachedIt != properties.end()) {
if (auto val = cachedIt->get_ptr<bool*>()) {
cached = *val;
if (auto prop = properties.lookup("cached")) {
if (prop->is_bool()) {
cached = prop->get_bool();
}
}

View File

@@ -15,10 +15,13 @@
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
#include "wpi/util/protobuf/Protobuf.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {
template <wpi::util::ProtobufSerializable T>

View File

@@ -19,10 +19,13 @@
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
#include "wpi/util/struct/Struct.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {
template <typename T, typename... I>

View File

@@ -19,9 +19,12 @@
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/struct/Struct.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {
template <typename T, typename... I>

View File

@@ -14,7 +14,10 @@
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/ntcore_c.h"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {

View File

@@ -369,7 +369,8 @@ class UnitTopic final : public Topic {
PublisherType Publish(
const PubSubOptions& options = DEFAULT_PUB_SUB_OPTIONS) {
return UnitPublisher<T>{::wpi::nt::PublishEx(
m_handle, NT_DOUBLE, "double", {{"unit", T{}.name()}}, options)};
m_handle, NT_DOUBLE, "double",
wpi::util::json::object("unit", T{}.name()), options)};
}
/**

View File

@@ -19,9 +19,9 @@
#include "wpi/nt/NetworkTableValue.hpp"
#include "wpi/nt/ntcore_c.h"
#include "wpi/nt/ntcore_cpp_types.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
template <typename T>
class SmallVectorImpl;
} // namespace wpi::util

View File

@@ -144,11 +144,12 @@ TEST_F(LocalStorageTest, PublishNewNoPropsNull) {
}
TEST_F(LocalStorageTest, PublishNew) {
wpi::util::json properties = {{"persistent", true}};
auto properties = wpi::util::json::object("persistent", true);
EXPECT_CALL(network, ClientPublish(_, std::string_view{"foo"},
std::string_view{"boolean"}, properties,
IsDefaultPubSubOptions()));
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {{"persistent", true}}, {});
storage.Publish(fooTopic, NT_BOOLEAN, "boolean",
wpi::util::json::object("persistent", true), {});
auto info = storage.GetTopicInfo(fooTopic);
EXPECT_EQ(info.topic, fooTopic);
@@ -645,7 +646,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkDefault) {
SetupPubSub(false, false);
// incoming from the network are treated like a normal local publish
auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt);
auto topic = storage.ServerAnnounce("foo", 0, "double",
wpi::util::json::object(), std::nullopt);
storage.ServerSetValue(topic, val1);
storage.ServerSetValue(topic, val2);
// verify the timestamp was updated
@@ -665,7 +667,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPub) {
SetupPubSub(true, false);
// incoming from the network are treated like a normal local publish
auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt);
auto topic = storage.ServerAnnounce("foo", 0, "double",
wpi::util::json::object(), std::nullopt);
storage.ServerSetValue(topic, val1);
storage.ServerSetValue(topic, val2);
// verify the timestamp was updated
@@ -684,7 +687,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepSub) {
SetupPubSub(false, true);
// incoming from the network are treated like a normal local publish
auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt);
auto topic = storage.ServerAnnounce("foo", 0, "double",
wpi::util::json::object(), std::nullopt);
storage.ServerSetValue(topic, val1);
storage.ServerSetValue(topic, val2);
// verify the timestamp was updated
@@ -706,7 +710,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPubSub) {
SetupPubSub(true, true);
// incoming from the network are treated like a normal local publish
auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt);
auto topic = storage.ServerAnnounce("foo", 0, "double",
wpi::util::json::object(), std::nullopt);
storage.ServerSetValue(topic, val1);
storage.ServerSetValue(topic, val2);
// verify the timestamp was updated

View File

@@ -342,7 +342,8 @@ TEST_F(StructTest, InnerArrayNonconstexpr) {
TEST_F(StructTest, StructA) {
wpi::nt::StructTopic<ThingA> topic = inst.GetStructTopic<ThingA>("a");
wpi::nt::StructPublisher<ThingA> pub = topic.Publish();
wpi::nt::StructPublisher<ThingA> pub2 = topic.PublishEx({{}});
wpi::nt::StructPublisher<ThingA> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructSubscriber<ThingA> sub = topic.Subscribe({});
wpi::nt::StructEntry<ThingA> entry = topic.GetEntry({});
pub.SetDefault({});
@@ -360,7 +361,8 @@ TEST_F(StructTest, StructArrayA) {
wpi::nt::StructArrayTopic<ThingA> topic =
inst.GetStructArrayTopic<ThingA>("a");
wpi::nt::StructArrayPublisher<ThingA> pub = topic.Publish();
wpi::nt::StructArrayPublisher<ThingA> pub2 = topic.PublishEx({{}});
wpi::nt::StructArrayPublisher<ThingA> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructArraySubscriber<ThingA> sub = topic.Subscribe({});
wpi::nt::StructArrayEntry<ThingA> entry = topic.GetEntry({});
pub.SetDefault({{ThingA{}, ThingA{}}});
@@ -378,7 +380,8 @@ TEST_F(StructTest, StructFixedArrayA) {
wpi::nt::StructTopic<std::array<ThingA, 2>> topic =
inst.GetStructTopic<std::array<ThingA, 2>>("a");
wpi::nt::StructPublisher<std::array<ThingA, 2>> pub = topic.Publish();
wpi::nt::StructPublisher<std::array<ThingA, 2>> pub2 = topic.PublishEx({{}});
wpi::nt::StructPublisher<std::array<ThingA, 2>> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructSubscriber<std::array<ThingA, 2>> sub = topic.Subscribe({});
wpi::nt::StructEntry<std::array<ThingA, 2>> entry = topic.GetEntry({});
std::array<ThingA, 2> arr;
@@ -398,7 +401,8 @@ TEST_F(StructTest, StructB) {
wpi::nt::StructTopic<ThingB, Info1> topic =
inst.GetStructTopic<ThingB, Info1>("b", info);
wpi::nt::StructPublisher<ThingB, Info1> pub = topic.Publish();
wpi::nt::StructPublisher<ThingB, Info1> pub2 = topic.PublishEx({{}});
wpi::nt::StructPublisher<ThingB, Info1> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructSubscriber<ThingB, Info1> sub = topic.Subscribe({});
wpi::nt::StructEntry<ThingB, Info1> entry = topic.GetEntry({});
pub.SetDefault({});
@@ -417,7 +421,8 @@ TEST_F(StructTest, StructArrayB) {
wpi::nt::StructArrayTopic<ThingB, Info1> topic =
inst.GetStructArrayTopic<ThingB, Info1>("b", info);
wpi::nt::StructArrayPublisher<ThingB, Info1> pub = topic.Publish();
wpi::nt::StructArrayPublisher<ThingB, Info1> pub2 = topic.PublishEx({{}});
wpi::nt::StructArrayPublisher<ThingB, Info1> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructArraySubscriber<ThingB, Info1> sub = topic.Subscribe({});
wpi::nt::StructArrayEntry<ThingB, Info1> entry = topic.GetEntry({});
pub.SetDefault({{ThingB{}, ThingB{}}});
@@ -437,7 +442,7 @@ TEST_F(StructTest, StructFixedArrayB) {
inst.GetStructTopic<std::array<ThingB, 2>, Info1>("b", info);
wpi::nt::StructPublisher<std::array<ThingB, 2>, Info1> pub = topic.Publish();
wpi::nt::StructPublisher<std::array<ThingB, 2>, Info1> pub2 =
topic.PublishEx({{}});
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructSubscriber<std::array<ThingB, 2>, Info1> sub =
topic.Subscribe({});
wpi::nt::StructEntry<std::array<ThingB, 2>, Info1> entry = topic.GetEntry({});

View File

@@ -43,33 +43,20 @@ TEST_F(WireDecodeTextClientTest, EmptyArray) {
}
TEST_F(WireDecodeTextClientTest, ErrorEmpty) {
EXPECT_CALL(
logger,
Call(_, _, _,
"could not decode JSON message: [json.exception.parse_error.101] "
"parse error at line 1, column 1: attempting to parse an empty "
"input; check that your input string or stream contains the "
"expected JSON"sv));
EXPECT_CALL(logger,
Call(_, _, _, "could not decode JSON message: absent_value"sv));
net::WireDecodeText("", handler, logger);
}
TEST_F(WireDecodeTextClientTest, ErrorBadJson1) {
EXPECT_CALL(
logger,
Call(_, _, _,
"could not decode JSON message: [json.exception.parse_error.101] "
"parse error at line 1, column 2: syntax error while parsing value "
"- unexpected end of input; expected '[', '{', or a literal"sv));
EXPECT_CALL(logger,
Call(_, _, _, "could not decode JSON message: unexpected_eof"sv));
net::WireDecodeText("[", handler, logger);
}
TEST_F(WireDecodeTextClientTest, ErrorBadJson2) {
EXPECT_CALL(
logger,
Call(_, _, _,
"could not decode JSON message: [json.exception.parse_error.101] "
"parse error at line 1, column 3: syntax error while parsing object "
"key - unexpected end of input; expected string literal"sv));
EXPECT_CALL(logger,
Call(_, _, _, "could not decode JSON message: unexpected_eof"sv));
net::WireDecodeText("[{", handler, logger);
}
@@ -129,7 +116,7 @@ TEST_F(WireDecodeTextClientTest, PublishPropsEmpty) {
}
TEST_F(WireDecodeTextClientTest, PublishProps) {
wpi::util::json props = {{"k", 6}};
auto props = wpi::util::json::object("k", 6);
EXPECT_CALL(handler, ClientPublish(5, std::string_view{"test"},
std::string_view{"double"}, props,
PubSubOptionsEq({})));

View File

@@ -29,7 +29,9 @@ class WireEncoderTextTest : public ::testing::Test {
protected:
std::string out;
wpi::util::raw_string_ostream os{out};
wpi::util::json GetJson() { return wpi::util::json::parse(os.str()); }
wpi::util::json GetJson() {
return wpi::util::json::parse(os.str()).value_or(wpi::util::json::object());
}
};
class WireEncoderBinaryTest : public ::testing::Test {
@@ -47,7 +49,8 @@ TEST_F(WireEncoderTextTest, PublishPropsEmpty) {
}
TEST_F(WireEncoderTextTest, PublishProps) {
net::WireEncodePublish(os, 5, "test", "double", {{"k", 6}});
net::WireEncodePublish(os, 5, "test", "double",
wpi::util::json::object("k", 6));
ASSERT_EQ(os.str(),
"{\"method\":\"publish\",\"params\":{"
"\"name\":\"test\",\"properties\":{\"k\":6},"
@@ -60,7 +63,7 @@ TEST_F(WireEncoderTextTest, Unpublish) {
}
TEST_F(WireEncoderTextTest, SetProperties) {
net::WireEncodeSetProperties(os, "test", {{"k", 6}});
net::WireEncodeSetProperties(os, "test", wpi::util::json::object("k", 6));
ASSERT_EQ(os.str(),
"{\"method\":\"setproperties\",\"params\":{"
"\"name\":\"test\",\"update\":{\"k\":6}}}");
@@ -122,7 +125,8 @@ TEST_F(WireEncoderTextTest, Announce) {
}
TEST_F(WireEncoderTextTest, AnnounceProperties) {
net::WireEncodeAnnounce(os, "test", 5, "double", {{"k", 6}}, std::nullopt);
net::WireEncodeAnnounce(os, "test", 5, "double",
wpi::util::json::object("k", 6), std::nullopt);
ASSERT_EQ(os.str(),
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","
"\"properties\":{\"k\":6},\"type\":\"double\"}}");
@@ -144,7 +148,8 @@ TEST_F(WireEncoderTextTest, Unannounce) {
}
TEST_F(WireEncoderTextTest, MessagePublish) {
net::ClientMessage msg{net::PublishMsg{5, "test", "double", {{"k", 6}}, {}}};
net::ClientMessage msg{net::PublishMsg{
5, "test", "double", wpi::util::json::object("k", 6), {}}};
ASSERT_TRUE(net::WireEncodeText(os, msg));
ASSERT_EQ(os.str(),
"{\"method\":\"publish\",\"params\":{"
@@ -159,7 +164,8 @@ TEST_F(WireEncoderTextTest, MessageUnpublish) {
}
TEST_F(WireEncoderTextTest, MessageSetProperties) {
net::ClientMessage msg{net::SetPropertiesMsg{"test", {{"k", 6}}}};
net::ClientMessage msg{
net::SetPropertiesMsg{"test", wpi::util::json::object("k", 6)}};
ASSERT_TRUE(net::WireEncodeText(os, msg));
ASSERT_EQ(os.str(),
"{\"method\":\"setproperties\",\"params\":{"
@@ -190,8 +196,8 @@ TEST_F(WireEncoderTextTest, MessageAnnounce) {
}
TEST_F(WireEncoderTextTest, MessageAnnounceProperties) {
net::ServerMessage msg{
net::AnnounceMsg{"test", 5, "double", std::nullopt, {{"k", 6}}}};
net::ServerMessage msg{net::AnnounceMsg{"test", 5, "double", std::nullopt,
wpi::util::json::object("k", 6)}};
ASSERT_TRUE(net::WireEncodeText(os, msg));
ASSERT_EQ(os.str(),
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","